gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

unionFile.go (7018B)


      1 package afero
      2 
      3 import (
      4 	"io"
      5 	"os"
      6 	"path/filepath"
      7 	"syscall"
      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 UnionFile struct {
     24 	Base   File
     25 	Layer  File
     26 	Merger DirsMerger
     27 	off    int
     28 	files  []os.FileInfo
     29 }
     30 
     31 func (f *UnionFile) 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 BADFD
     42 }
     43 
     44 func (f *UnionFile) 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 			if _, seekErr := f.Base.Seek(int64(n), io.SeekCurrent); seekErr != nil {
     51 				// only overwrite err in case the seek fails: we need to
     52 				// report an eventual io.EOF to the caller
     53 				err = seekErr
     54 			}
     55 		}
     56 		return n, err
     57 	}
     58 	if f.Base != nil {
     59 		return f.Base.Read(s)
     60 	}
     61 	return 0, BADFD
     62 }
     63 
     64 func (f *UnionFile) ReadAt(s []byte, o int64) (int, error) {
     65 	if f.Layer != nil {
     66 		n, err := f.Layer.ReadAt(s, o)
     67 		if (err == nil || err == io.EOF) && f.Base != nil {
     68 			_, err = f.Base.Seek(o+int64(n), io.SeekStart)
     69 		}
     70 		return n, err
     71 	}
     72 	if f.Base != nil {
     73 		return f.Base.ReadAt(s, o)
     74 	}
     75 	return 0, BADFD
     76 }
     77 
     78 func (f *UnionFile) Seek(o int64, w int) (pos int64, err error) {
     79 	if f.Layer != nil {
     80 		pos, err = f.Layer.Seek(o, w)
     81 		if (err == nil || err == io.EOF) && f.Base != nil {
     82 			_, err = f.Base.Seek(o, w)
     83 		}
     84 		return pos, err
     85 	}
     86 	if f.Base != nil {
     87 		return f.Base.Seek(o, w)
     88 	}
     89 	return 0, BADFD
     90 }
     91 
     92 func (f *UnionFile) Write(s []byte) (n int, err error) {
     93 	if f.Layer != nil {
     94 		n, err = f.Layer.Write(s)
     95 		if err == nil && f.Base != nil { // hmm, do we have fixed size files where a write may hit the EOF mark?
     96 			_, err = f.Base.Write(s)
     97 		}
     98 		return n, err
     99 	}
    100 	if f.Base != nil {
    101 		return f.Base.Write(s)
    102 	}
    103 	return 0, BADFD
    104 }
    105 
    106 func (f *UnionFile) WriteAt(s []byte, o int64) (n int, err error) {
    107 	if f.Layer != nil {
    108 		n, err = f.Layer.WriteAt(s, o)
    109 		if err == nil && f.Base != nil {
    110 			_, err = f.Base.WriteAt(s, o)
    111 		}
    112 		return n, err
    113 	}
    114 	if f.Base != nil {
    115 		return f.Base.WriteAt(s, o)
    116 	}
    117 	return 0, BADFD
    118 }
    119 
    120 func (f *UnionFile) Name() string {
    121 	if f.Layer != nil {
    122 		return f.Layer.Name()
    123 	}
    124 	return f.Base.Name()
    125 }
    126 
    127 // DirsMerger is how UnionFile weaves two directories together.
    128 // It takes the FileInfo slices from the layer and the base and returns a
    129 // single view.
    130 type DirsMerger func(lofi, bofi []os.FileInfo) ([]os.FileInfo, error)
    131 
    132 var defaultUnionMergeDirsFn = func(lofi, bofi []os.FileInfo) ([]os.FileInfo, error) {
    133 	files := make(map[string]os.FileInfo)
    134 
    135 	for _, fi := range lofi {
    136 		files[fi.Name()] = fi
    137 	}
    138 
    139 	for _, fi := range bofi {
    140 		if _, exists := files[fi.Name()]; !exists {
    141 			files[fi.Name()] = fi
    142 		}
    143 	}
    144 
    145 	rfi := make([]os.FileInfo, len(files))
    146 
    147 	i := 0
    148 	for _, fi := range files {
    149 		rfi[i] = fi
    150 		i++
    151 	}
    152 
    153 	return rfi, nil
    154 }
    155 
    156 // Readdir will weave the two directories together and
    157 // return a single view of the overlayed directories.
    158 // At the end of the directory view, the error is io.EOF if c > 0.
    159 func (f *UnionFile) Readdir(c int) (ofi []os.FileInfo, err error) {
    160 	var merge DirsMerger = f.Merger
    161 	if merge == nil {
    162 		merge = defaultUnionMergeDirsFn
    163 	}
    164 
    165 	if f.off == 0 {
    166 		var lfi []os.FileInfo
    167 		if f.Layer != nil {
    168 			lfi, err = f.Layer.Readdir(-1)
    169 			if err != nil {
    170 				return nil, err
    171 			}
    172 		}
    173 
    174 		var bfi []os.FileInfo
    175 		if f.Base != nil {
    176 			bfi, err = f.Base.Readdir(-1)
    177 			if err != nil {
    178 				return nil, err
    179 			}
    180 
    181 		}
    182 		merged, err := merge(lfi, bfi)
    183 		if err != nil {
    184 			return nil, err
    185 		}
    186 		f.files = append(f.files, merged...)
    187 	}
    188 	files := f.files[f.off:]
    189 
    190 	if c <= 0 {
    191 		return files, nil
    192 	}
    193 
    194 	if len(files) == 0 {
    195 		return nil, io.EOF
    196 	}
    197 
    198 	if c > len(files) {
    199 		c = len(files)
    200 	}
    201 
    202 	defer func() { f.off += c }()
    203 	return files[:c], nil
    204 }
    205 
    206 func (f *UnionFile) Readdirnames(c int) ([]string, error) {
    207 	rfi, err := f.Readdir(c)
    208 	if err != nil {
    209 		return nil, err
    210 	}
    211 	var names []string
    212 	for _, fi := range rfi {
    213 		names = append(names, fi.Name())
    214 	}
    215 	return names, nil
    216 }
    217 
    218 func (f *UnionFile) Stat() (os.FileInfo, error) {
    219 	if f.Layer != nil {
    220 		return f.Layer.Stat()
    221 	}
    222 	if f.Base != nil {
    223 		return f.Base.Stat()
    224 	}
    225 	return nil, BADFD
    226 }
    227 
    228 func (f *UnionFile) Sync() (err error) {
    229 	if f.Layer != nil {
    230 		err = f.Layer.Sync()
    231 		if err == nil && f.Base != nil {
    232 			err = f.Base.Sync()
    233 		}
    234 		return err
    235 	}
    236 	if f.Base != nil {
    237 		return f.Base.Sync()
    238 	}
    239 	return BADFD
    240 }
    241 
    242 func (f *UnionFile) Truncate(s int64) (err error) {
    243 	if f.Layer != nil {
    244 		err = f.Layer.Truncate(s)
    245 		if err == nil && f.Base != nil {
    246 			err = f.Base.Truncate(s)
    247 		}
    248 		return err
    249 	}
    250 	if f.Base != nil {
    251 		return f.Base.Truncate(s)
    252 	}
    253 	return BADFD
    254 }
    255 
    256 func (f *UnionFile) WriteString(s string) (n int, err error) {
    257 	if f.Layer != nil {
    258 		n, err = f.Layer.WriteString(s)
    259 		if err == nil && f.Base != nil {
    260 			_, err = f.Base.WriteString(s)
    261 		}
    262 		return n, err
    263 	}
    264 	if f.Base != nil {
    265 		return f.Base.WriteString(s)
    266 	}
    267 	return 0, BADFD
    268 }
    269 
    270 func copyFile(base Fs, layer Fs, name string, bfh File) error {
    271 	// First make sure the directory exists
    272 	exists, err := Exists(layer, filepath.Dir(name))
    273 	if err != nil {
    274 		return err
    275 	}
    276 	if !exists {
    277 		err = layer.MkdirAll(filepath.Dir(name), 0o777) // FIXME?
    278 		if err != nil {
    279 			return err
    280 		}
    281 	}
    282 
    283 	// Create the file on the overlay
    284 	lfh, err := layer.Create(name)
    285 	if err != nil {
    286 		return err
    287 	}
    288 	n, err := io.Copy(lfh, bfh)
    289 	if err != nil {
    290 		// If anything fails, clean up the file
    291 		layer.Remove(name)
    292 		lfh.Close()
    293 		return err
    294 	}
    295 
    296 	bfi, err := bfh.Stat()
    297 	if err != nil || bfi.Size() != n {
    298 		layer.Remove(name)
    299 		lfh.Close()
    300 		return syscall.EIO
    301 	}
    302 
    303 	err = lfh.Close()
    304 	if err != nil {
    305 		layer.Remove(name)
    306 		lfh.Close()
    307 		return err
    308 	}
    309 	return layer.Chtimes(name, bfi.ModTime(), bfi.ModTime())
    310 }
    311 
    312 func copyToLayer(base Fs, layer Fs, name string) error {
    313 	bfh, err := base.Open(name)
    314 	if err != nil {
    315 		return err
    316 	}
    317 	defer bfh.Close()
    318 
    319 	return copyFile(base, layer, name, bfh)
    320 }
    321 
    322 func copyFileToLayer(base Fs, layer Fs, name string, flag int, perm os.FileMode) error {
    323 	bfh, err := base.OpenFile(name, flag, perm)
    324 	if err != nil {
    325 		return err
    326 	}
    327 	defer bfh.Close()
    328 
    329 	return copyFile(base, layer, name, bfh)
    330 }