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  }