github.com/dennwc/btrfs@v0.0.0-20221026161108-3097362dc072/btrfs.go (about) 1 package btrfs 2 3 import ( 4 "fmt" 5 "io" 6 "os" 7 "path/filepath" 8 "strconv" 9 "syscall" 10 11 "github.com/dennwc/ioctl" 12 ) 13 14 const SuperMagic = 0x9123683E 15 16 func CloneFile(dst, src *os.File) error { 17 return iocClone(dst, src) 18 } 19 20 func Open(path string, ro bool) (*FS, error) { 21 if ok, err := IsSubVolume(path); err != nil { 22 return nil, err 23 } else if !ok { 24 return nil, ErrNotBtrfs{Path: path} 25 } 26 var ( 27 dir *os.File 28 err error 29 ) 30 if ro { 31 dir, err = os.OpenFile(path, os.O_RDONLY|syscall.O_NOATIME, 0644) 32 } else { 33 dir, err = os.Open(path) 34 } 35 if err != nil { 36 return nil, err 37 } else if st, err := dir.Stat(); err != nil { 38 dir.Close() 39 return nil, err 40 } else if !st.IsDir() { 41 dir.Close() 42 return nil, fmt.Errorf("not a directory: %s", path) 43 } 44 return &FS{f: dir}, nil 45 } 46 47 type FS struct { 48 f *os.File 49 } 50 51 func (f *FS) Close() error { 52 return f.f.Close() 53 } 54 55 type Info struct { 56 MaxID uint64 57 NumDevices uint64 58 FSID FSID 59 NodeSize uint32 60 SectorSize uint32 61 CloneAlignment uint32 62 } 63 64 func (f *FS) SubVolumeID() (uint64, error) { 65 id, err := getFileRootID(f.f) 66 if err != nil { 67 return 0, err 68 } 69 return uint64(id), nil 70 } 71 72 func (f *FS) Info() (out Info, err error) { 73 var arg btrfs_ioctl_fs_info_args 74 arg, err = iocFsInfo(f.f) 75 if err == nil { 76 out = Info{ 77 MaxID: arg.max_id, 78 NumDevices: arg.num_devices, 79 FSID: arg.fsid, 80 NodeSize: arg.nodesize, 81 SectorSize: arg.sectorsize, 82 CloneAlignment: arg.clone_alignment, 83 } 84 } 85 return 86 } 87 88 type DevInfo struct { 89 UUID UUID 90 BytesUsed uint64 91 TotalBytes uint64 92 Path string 93 } 94 95 func (f *FS) GetDevInfo(id uint64) (out DevInfo, err error) { 96 var arg btrfs_ioctl_dev_info_args 97 arg.devid = id 98 99 if err = ioctl.Do(f.f, _BTRFS_IOC_DEV_INFO, &arg); err != nil { 100 return 101 } 102 out.UUID = arg.uuid 103 out.BytesUsed = arg.bytes_used 104 out.TotalBytes = arg.total_bytes 105 out.Path = stringFromBytes(arg.path[:]) 106 107 return 108 } 109 110 type DevStats struct { 111 WriteErrs uint64 112 ReadErrs uint64 113 FlushErrs uint64 114 // Checksum error, bytenr error or contents is illegal: this is an 115 // indication that the block was damaged during read or write, or written to 116 // wrong location or read from wrong location. 117 CorruptionErrs uint64 118 // An indication that blocks have not been written. 119 GenerationErrs uint64 120 Unknown []uint64 121 } 122 123 func (f *FS) GetDevStats(id uint64) (out DevStats, err error) { 124 var arg btrfs_ioctl_get_dev_stats 125 arg.devid = id 126 arg.nr_items = _BTRFS_DEV_STAT_VALUES_MAX 127 arg.flags = 0 128 if err = ioctl.Do(f.f, _BTRFS_IOC_GET_DEV_STATS, &arg); err != nil { 129 return 130 } 131 i := 0 132 out.WriteErrs = arg.values[i] 133 i++ 134 out.ReadErrs = arg.values[i] 135 i++ 136 out.FlushErrs = arg.values[i] 137 i++ 138 out.CorruptionErrs = arg.values[i] 139 i++ 140 out.GenerationErrs = arg.values[i] 141 i++ 142 if int(arg.nr_items) > i { 143 out.Unknown = arg.values[i:arg.nr_items] 144 } 145 return 146 } 147 148 type FSFeatureFlags struct { 149 Compatible FeatureFlags 150 CompatibleRO FeatureFlags 151 Incompatible IncompatFeatures 152 } 153 154 func (f *FS) GetFeatures() (out FSFeatureFlags, err error) { 155 var arg btrfs_ioctl_feature_flags 156 if err = ioctl.Do(f.f, _BTRFS_IOC_GET_FEATURES, &arg); err != nil { 157 return 158 } 159 out = FSFeatureFlags{ 160 Compatible: arg.compat_flags, 161 CompatibleRO: arg.compat_ro_flags, 162 Incompatible: arg.incompat_flags, 163 } 164 return 165 } 166 167 func (f *FS) GetSupportedFeatures() (out FSFeatureFlags, err error) { 168 var arg [3]btrfs_ioctl_feature_flags 169 if err = ioctl.Do(f.f, _BTRFS_IOC_GET_SUPPORTED_FEATURES, &arg); err != nil { 170 return 171 } 172 out = FSFeatureFlags{ 173 Compatible: arg[0].compat_flags, 174 CompatibleRO: arg[0].compat_ro_flags, 175 Incompatible: arg[0].incompat_flags, 176 } 177 //for i, a := range arg { 178 // out[i] = FSFeatureFlags{ 179 // Compatible: a.compat_flags, 180 // CompatibleRO: a.compat_ro_flags, 181 // Incompatible: a.incompat_flags, 182 // } 183 //} 184 return 185 } 186 187 func (f *FS) GetFlags() (SubvolFlags, error) { 188 return iocSubvolGetflags(f.f) 189 } 190 191 func (f *FS) SetFlags(flags SubvolFlags) error { 192 return iocSubvolSetflags(f.f, flags) 193 } 194 195 func (f *FS) Sync() (err error) { 196 if err = ioctl.Ioctl(f.f, _BTRFS_IOC_START_SYNC, 0); err != nil { 197 return 198 } 199 return ioctl.Ioctl(f.f, _BTRFS_IOC_WAIT_SYNC, 0) 200 } 201 202 func (f *FS) CreateSubVolume(name string) error { 203 return CreateSubVolume(filepath.Join(f.f.Name(), name)) 204 } 205 206 func (f *FS) DeleteSubVolume(name string) error { 207 return DeleteSubVolume(filepath.Join(f.f.Name(), name)) 208 } 209 210 func (f *FS) Snapshot(dst string, ro bool) error { 211 return SnapshotSubVolume(f.f.Name(), filepath.Join(f.f.Name(), dst), ro) 212 } 213 214 func (f *FS) SnapshotSubVolume(name string, dst string, ro bool) error { 215 return SnapshotSubVolume(filepath.Join(f.f.Name(), name), 216 filepath.Join(f.f.Name(), dst), ro) 217 } 218 219 func (f *FS) Send(w io.Writer, parent string, subvols ...string) error { 220 if parent != "" { 221 parent = filepath.Join(f.f.Name(), parent) 222 } 223 sub := make([]string, 0, len(subvols)) 224 for _, s := range subvols { 225 sub = append(sub, filepath.Join(f.f.Name(), s)) 226 } 227 return Send(w, parent, sub...) 228 } 229 230 func (f *FS) Receive(r io.Reader) error { 231 return Receive(r, f.f.Name()) 232 } 233 234 func (f *FS) ReceiveTo(r io.Reader, mount string) error { 235 return Receive(r, filepath.Join(f.f.Name(), mount)) 236 } 237 238 func (f *FS) ListSubvolumes(filter func(SubvolInfo) bool) ([]SubvolInfo, error) { 239 m, err := listSubVolumes(f.f, filter) 240 if err != nil { 241 return nil, err 242 } 243 out := make([]SubvolInfo, 0, len(m)) 244 for _, v := range m { 245 out = append(out, v) 246 } 247 return out, nil 248 } 249 250 func (f *FS) SubvolumeByUUID(uuid UUID) (*SubvolInfo, error) { 251 id, err := lookupUUIDSubvolItem(f.f, uuid) 252 if err != nil { 253 return nil, err 254 } 255 return subvolSearchByRootID(f.f, id, "") 256 } 257 258 func (f *FS) SubvolumeByReceivedUUID(uuid UUID) (*SubvolInfo, error) { 259 id, err := lookupUUIDReceivedSubvolItem(f.f, uuid) 260 if err != nil { 261 return nil, err 262 } 263 return subvolSearchByRootID(f.f, id, "") 264 } 265 266 func (f *FS) SubvolumeByPath(path string) (*SubvolInfo, error) { 267 return subvolSearchByPath(f.f, path) 268 } 269 270 func (f *FS) Usage() (UsageInfo, error) { return spaceUsage(f.f) } 271 272 func (f *FS) Balance(flags BalanceFlags) (BalanceProgress, error) { 273 args := btrfs_ioctl_balance_args{flags: flags} 274 err := iocBalanceV2(f.f, &args) 275 return args.stat, err 276 } 277 278 func (f *FS) Resize(size int64) error { 279 amount := strconv.FormatInt(size, 10) 280 args := &btrfs_ioctl_vol_args{} 281 args.SetName(amount) 282 if err := iocResize(f.f, args); err != nil { 283 return fmt.Errorf("resize failed: %v", err) 284 } 285 return nil 286 } 287 288 func (f *FS) ResizeToMax() error { 289 args := &btrfs_ioctl_vol_args{} 290 args.SetName("max") 291 if err := iocResize(f.f, args); err != nil { 292 return fmt.Errorf("resize failed: %v", err) 293 } 294 return nil 295 }