tractor.dev/toolkit-go@v0.0.0-20241010005851-214d91207d07/engine/fs/unionfs/file.go (about)

     1  package unionfs
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  	"io/fs"
     7  	"os"
     8  )
     9  
    10  // The UnionFile implements the afero.File interface and will be returned
    11  // when reading a directory present at least in the overlay or opening a file
    12  // for writing.
    13  //
    14  // The calls to
    15  // Readdir() and Readdirnames() merge the file os.FileInfo / names from the
    16  // base and the overlay - for files present in both layers, only those
    17  // from the overlay will be used.
    18  //
    19  // When opening files for writing (Create() / OpenFile() with the right flags)
    20  // the operations will be done in both layers, starting with the overlay. A
    21  // successful read in the overlay will move the cursor position in the base layer
    22  // by the number of bytes read.
    23  type File struct {
    24  	Base   fs.File
    25  	Layer  fs.File
    26  	Merger DirsMerger
    27  	off    int
    28  	files  []fs.DirEntry
    29  }
    30  
    31  func (f *File) Close() error {
    32  	// first close base, so we have a newer timestamp in the overlay. If we'd close
    33  	// the overlay first, we'd get a cacheStale the next time we access this file
    34  	// -> cache would be useless ;-)
    35  	if f.Base != nil {
    36  		f.Base.Close()
    37  	}
    38  	if f.Layer != nil {
    39  		return f.Layer.Close()
    40  	}
    41  	return fs.ErrInvalid
    42  }
    43  
    44  func (f *File) Read(s []byte) (int, error) {
    45  	if f.Layer != nil {
    46  		n, err := f.Layer.Read(s)
    47  		if (err == nil || err == io.EOF) && f.Base != nil {
    48  			// advance the file position also in the base file, the next
    49  			// call may be a write at this position (or a seek with SEEK_CUR)
    50  			fseek, ok := f.Base.(io.Seeker)
    51  			if !ok {
    52  				return n, fmt.Errorf("unable to seek file")
    53  			}
    54  			if _, seekErr := fseek.Seek(int64(n), os.SEEK_CUR); seekErr != nil {
    55  				// only overwrite err in case the seek fails: we need to
    56  				// report an eventual io.EOF to the caller
    57  				err = seekErr
    58  			}
    59  		}
    60  		return n, err
    61  	}
    62  	if f.Base != nil {
    63  		return f.Base.Read(s)
    64  	}
    65  	return 0, fs.ErrInvalid
    66  }
    67  
    68  func (f *File) ReadAt(s []byte, o int64) (int, error) {
    69  	if f.Layer != nil {
    70  		freadat, ok := f.Layer.(io.ReaderAt)
    71  		if !ok {
    72  			return 0, fmt.Errorf("unable to readat file")
    73  		}
    74  		n, err := freadat.ReadAt(s, o)
    75  		if (err == nil || err == io.EOF) && f.Base != nil {
    76  			fseek, ok := f.Base.(io.Seeker)
    77  			if !ok {
    78  				return n, fmt.Errorf("unable to seek file")
    79  			}
    80  			_, err = fseek.Seek(o+int64(n), os.SEEK_SET)
    81  		}
    82  		return n, err
    83  	}
    84  	if f.Base != nil {
    85  		freadat, ok := f.Layer.(io.ReaderAt)
    86  		if !ok {
    87  			return 0, fmt.Errorf("unable to readat file")
    88  		}
    89  		return freadat.ReadAt(s, o)
    90  	}
    91  	return 0, fs.ErrInvalid
    92  }
    93  
    94  func (f *File) Seek(o int64, w int) (pos int64, err error) {
    95  	if f.Layer != nil {
    96  		fseek, ok := f.Layer.(io.Seeker)
    97  		if !ok {
    98  			return 0, fmt.Errorf("unable to seek file")
    99  		}
   100  		pos, err = fseek.Seek(o, w)
   101  		if (err == nil || err == io.EOF) && f.Base != nil {
   102  			fseek, ok := f.Base.(io.Seeker)
   103  			if !ok {
   104  				return 0, fmt.Errorf("unable to seek file")
   105  			}
   106  			_, err = fseek.Seek(o, w)
   107  		}
   108  		return pos, err
   109  	}
   110  	if f.Base != nil {
   111  		fseek, ok := f.Base.(io.Seeker)
   112  		if !ok {
   113  			return 0, fmt.Errorf("unable to seek file")
   114  		}
   115  		return fseek.Seek(o, w)
   116  	}
   117  	return 0, fs.ErrInvalid
   118  }
   119  
   120  func (f *File) Write(s []byte) (n int, err error) {
   121  	if f.Layer != nil {
   122  		fw, ok := f.Layer.(io.Writer)
   123  		if !ok {
   124  			return 0, fs.ErrPermission
   125  		}
   126  		n, err = fw.Write(s)
   127  		if err == nil && f.Base != nil { // hmm, do we have fixed size files where a write may hit the EOF mark?
   128  			fw, ok := f.Base.(io.Writer)
   129  			if !ok {
   130  				return 0, fs.ErrPermission
   131  			}
   132  			_, err = fw.Write(s)
   133  		}
   134  		return n, err
   135  	}
   136  	if f.Base != nil {
   137  		fw, ok := f.Base.(io.Writer)
   138  		if !ok {
   139  			return 0, fs.ErrPermission
   140  		}
   141  		return fw.Write(s)
   142  	}
   143  	return 0, fs.ErrInvalid
   144  }
   145  
   146  func (f *File) WriteAt(s []byte, o int64) (n int, err error) {
   147  	if f.Layer != nil {
   148  		fwriteat, ok := f.Layer.(io.WriterAt)
   149  		if !ok {
   150  			return 0, fmt.Errorf("unable to writeat file")
   151  		}
   152  		n, err = fwriteat.WriteAt(s, o)
   153  		if err == nil && f.Base != nil {
   154  			fwriteat, ok := f.Base.(io.WriterAt)
   155  			if !ok {
   156  				return 0, fmt.Errorf("unable to writeat file")
   157  			}
   158  			_, err = fwriteat.WriteAt(s, o)
   159  		}
   160  		return n, err
   161  	}
   162  	if f.Base != nil {
   163  		fwriteat, ok := f.Base.(io.WriterAt)
   164  		if !ok {
   165  			return 0, fmt.Errorf("unable to writeat file")
   166  		}
   167  		return fwriteat.WriteAt(s, o)
   168  	}
   169  	return 0, fs.ErrInvalid
   170  }
   171  
   172  // DirsMerger is how UnionFile weaves two directories together.
   173  // It takes the FileInfo slices from the layer and the base and returns a
   174  // single view.
   175  type DirsMerger func(lofi, bofi []fs.DirEntry) ([]fs.DirEntry, error)
   176  
   177  var defaultUnionMergeDirsFn = func(lofi, bofi []fs.DirEntry) ([]fs.DirEntry, error) {
   178  	var files = make(map[string]fs.DirEntry)
   179  
   180  	for _, fi := range lofi {
   181  		files[fi.Name()] = fi
   182  	}
   183  
   184  	for _, fi := range bofi {
   185  		if _, exists := files[fi.Name()]; !exists {
   186  			files[fi.Name()] = fi
   187  		}
   188  	}
   189  
   190  	rfi := make([]fs.DirEntry, len(files))
   191  
   192  	i := 0
   193  	for _, fi := range files {
   194  		rfi[i] = fi
   195  		i++
   196  	}
   197  
   198  	return rfi, nil
   199  
   200  }
   201  
   202  // Readdir will weave the two directories together and
   203  // return a single view of the overlayed directories.
   204  // At the end of the directory view, the error is io.EOF if c > 0.
   205  func (f *File) ReadDir(c int) (ofi []fs.DirEntry, err error) {
   206  	var merge DirsMerger = f.Merger
   207  	if merge == nil {
   208  		merge = defaultUnionMergeDirsFn
   209  	}
   210  
   211  	if f.off == 0 {
   212  		var lfi []fs.DirEntry
   213  		if f.Layer != nil {
   214  			df, ok := f.Layer.(fs.ReadDirFile)
   215  			if !ok {
   216  				return nil, fmt.Errorf("unable to readdir file")
   217  			}
   218  			lfi, err = df.ReadDir(-1)
   219  			if err != nil {
   220  				return nil, err
   221  			}
   222  		}
   223  
   224  		var bfi []fs.DirEntry
   225  		if f.Base != nil {
   226  			df, ok := f.Base.(fs.ReadDirFile)
   227  			if !ok {
   228  				return nil, fmt.Errorf("unable to readdir file")
   229  			}
   230  			bfi, err = df.ReadDir(-1)
   231  			if err != nil {
   232  				return nil, err
   233  			}
   234  
   235  		}
   236  		merged, err := merge(lfi, bfi)
   237  		if err != nil {
   238  			return nil, err
   239  		}
   240  		f.files = append(f.files, merged...)
   241  	}
   242  	files := f.files[f.off:]
   243  
   244  	if c <= 0 {
   245  		return files, nil
   246  	}
   247  
   248  	if len(files) == 0 {
   249  		return nil, io.EOF
   250  	}
   251  
   252  	if c > len(files) {
   253  		c = len(files)
   254  	}
   255  
   256  	defer func() { f.off += c }()
   257  	return files[:c], nil
   258  }
   259  
   260  func (f *File) Stat() (fs.FileInfo, error) {
   261  	if f.Layer != nil {
   262  		return f.Layer.Stat()
   263  	}
   264  	if f.Base != nil {
   265  		return f.Base.Stat()
   266  	}
   267  	return nil, fs.ErrInvalid
   268  }
   269  
   270  func (f *File) Sync() (err error) {
   271  	if f.Layer != nil {
   272  		fsync, ok := f.Layer.(interface{ Sync() error })
   273  		if !ok {
   274  			return fmt.Errorf("unable to sync file")
   275  		}
   276  		err = fsync.Sync()
   277  		if err == nil && f.Base != nil {
   278  			fsync, ok := f.Base.(interface{ Sync() error })
   279  			if !ok {
   280  				return fmt.Errorf("unable to sync file")
   281  			}
   282  			err = fsync.Sync()
   283  		}
   284  		return err
   285  	}
   286  	if f.Base != nil {
   287  		fsync, ok := f.Base.(interface{ Sync() error })
   288  		if !ok {
   289  			return fmt.Errorf("unable to sync file")
   290  		}
   291  		return fsync.Sync()
   292  	}
   293  	return fs.ErrInvalid
   294  }
   295  
   296  func (f *File) Truncate(s int64) (err error) {
   297  	if f.Layer != nil {
   298  		ft, ok := f.Layer.(interface{ Truncate(int64) error })
   299  		if !ok {
   300  			return fmt.Errorf("unable to truncate file")
   301  		}
   302  		err = ft.Truncate(s)
   303  		if err == nil && f.Base != nil {
   304  			ft, ok := f.Base.(interface{ Truncate(int64) error })
   305  			if !ok {
   306  				return fmt.Errorf("unable to truncate file")
   307  			}
   308  			err = ft.Truncate(s)
   309  		}
   310  		return err
   311  	}
   312  	if f.Base != nil {
   313  		ft, ok := f.Base.(interface{ Truncate(int64) error })
   314  		if !ok {
   315  			return fmt.Errorf("unable to truncate file")
   316  		}
   317  		return ft.Truncate(s)
   318  	}
   319  	return fs.ErrInvalid
   320  }
   321  
   322  // func copyToLayer(base fs.FS, layer fs.FS, name string) error {
   323  // 	bfh, err := base.Open(name)
   324  // 	if err != nil {
   325  // 		return err
   326  // 	}
   327  // 	defer bfh.Close()
   328  
   329  // 	// First make sure the directory exists
   330  // 	exists, err := vfs.Exists(layer, filepath.Dir(name))
   331  // 	if err != nil {
   332  // 		return err
   333  // 	}
   334  // 	if !exists {
   335  // 		err = layer.MkdirAll(filepath.Dir(name), 0777) // FIXME?
   336  // 		if err != nil {
   337  // 			return err
   338  // 		}
   339  // 	}
   340  
   341  // 	// Create the file on the overlay
   342  // 	lfh, err := layer.Create(name)
   343  // 	if err != nil {
   344  // 		return err
   345  // 	}
   346  // 	n, err := io.Copy(lfh, bfh)
   347  // 	if err != nil {
   348  // 		// If anything fails, clean up the file
   349  // 		layer.Remove(name)
   350  // 		lfh.Close()
   351  // 		return err
   352  // 	}
   353  
   354  // 	bfi, err := bfh.Stat()
   355  // 	if err != nil || bfi.Size() != n {
   356  // 		layer.Remove(name)
   357  // 		lfh.Close()
   358  // 		return syscall.EIO
   359  // 	}
   360  
   361  // 	err = lfh.Close()
   362  // 	if err != nil {
   363  // 		layer.Remove(name)
   364  // 		lfh.Close()
   365  // 		return err
   366  // 	}
   367  // 	return layer.Chtimes(name, bfi.ModTime(), bfi.ModTime())
   368  // }