unionFile.go - afero - [fork] go afero port for 9front
 (HTM) git clone https://git.drkhsh.at/afero.git
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) 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 }