github.com/pbberlin/tools@v0.0.0-20160910141205-7aa5421c2169/os/fsi/dsfs/30_fs_impl.go (about)

     1  package dsfs
     2  
     3  import (
     4  	"fmt"
     5  	"log"
     6  	"os"
     7  	"strings"
     8  	"sync/atomic"
     9  	"time"
    10  
    11  	"google.golang.org/appengine/datastore"
    12  
    13  	"github.com/pbberlin/tools/os/fsi"
    14  	"github.com/pbberlin/tools/os/fsi/common"
    15  
    16  	aelog "google.golang.org/appengine/log"
    17  )
    18  
    19  func (fs dsFileSys) Name() string { return "dsfs" }
    20  
    21  func (fs dsFileSys) String() string { return fs.mount }
    22  
    23  //---------------------------------------
    24  
    25  func (fs *dsFileSys) Chmod(name string, mode os.FileMode) error {
    26  
    27  	f, err := fs.fileByPath(name)
    28  	if err == nil {
    29  		f.MMode = mode
    30  
    31  		err := f.Sync()
    32  		if err != nil {
    33  			return err
    34  		}
    35  		return nil
    36  	} else {
    37  		dir, err := fs.dirByPath(name)
    38  		if err != nil {
    39  			return err
    40  		}
    41  		dir.MMode = mode
    42  		_, err = fs.saveDirByPathExt(dir, dir.Dir+dir.BName)
    43  		if err != nil {
    44  			return err
    45  		}
    46  
    47  	}
    48  
    49  	return nil
    50  }
    51  
    52  func (fs *dsFileSys) Chtimes(name string, atime time.Time, mtime time.Time) error {
    53  
    54  	f, err := fs.fileByPath(name)
    55  	if err == nil {
    56  		f.MModTime = atime
    57  		err := f.Sync()
    58  		if err != nil {
    59  			return err
    60  		}
    61  		return nil
    62  	} else {
    63  		dir, err := fs.dirByPath(name)
    64  		if err != nil {
    65  			return err
    66  		}
    67  		dir.MModTime = atime
    68  		_, err = fs.saveDirByPathExt(dir, dir.Dir+dir.BName)
    69  		if err != nil {
    70  			return err
    71  		}
    72  	}
    73  
    74  	return nil
    75  
    76  }
    77  
    78  // Create opens for read-write.
    79  // Open opens for readonly access.
    80  func (fs *dsFileSys) Create(name string) (fsi.File, error) {
    81  
    82  	// WriteFile & Create
    83  	dir, bname := fs.SplitX(name)
    84  
    85  	f := DsFile{}
    86  	f.fSys = fs
    87  	f.BName = common.Filify(bname)
    88  	f.Dir = dir
    89  	f.MModTime = time.Now()
    90  	f.MMode = 0644
    91  
    92  	// let all the properties by set by fs.saveFileByPath
    93  	err := f.Sync()
    94  	if err != nil {
    95  		return nil, err
    96  	}
    97  
    98  	// return &f, nil
    99  	ff := fsi.File(&f)
   100  	return ff, err
   101  
   102  }
   103  
   104  // No distinction between Stat (links are followed)
   105  // and LStat (links go unresolved)
   106  // We don't support links yet, anyway
   107  func (fs *dsFileSys) Lstat(path string) (os.FileInfo, error) {
   108  	fi, err := fs.Stat(path)
   109  	return fi, err
   110  }
   111  
   112  // Strangely, neither MkdirAll nor Mkdir seem to have
   113  // any concept of current working directory.
   114  // They seem to operate relative to root.
   115  func (fs *dsFileSys) Mkdir(name string, perm os.FileMode) error {
   116  	_, err := fs.saveDirByPath(name)
   117  	return err
   118  }
   119  
   120  func (fs *dsFileSys) MkdirAll(path string, perm os.FileMode) error {
   121  	_, err := fs.saveDirByPath(path)
   122  	return err
   123  }
   124  
   125  // Open() open existing file for readonly access.
   126  // Create() should be used   for read-write.
   127  
   128  // We could make provisions to ensure exclusive access;
   129  
   130  // complies  with   os.Open()
   131  // conflicts with  vfs.Open() signature
   132  // conflicts with file.Open() interface of Afero
   133  func (fs *dsFileSys) Open(name string) (fsi.File, error) {
   134  
   135  	// explicitly requesting directory?
   136  	_, bname := fs.SplitX(name)
   137  	if strings.HasSuffix(bname, "/") {
   138  		dir, err := fs.dirByPath(name)
   139  		if err == nil {
   140  			ff := fsi.File(&dir)
   141  			return ff, nil
   142  		}
   143  	}
   144  
   145  	// otherwise: try file, then directory
   146  	f, err := fs.fileByPath(name)
   147  
   148  	if err != nil && err != datastore.ErrNoSuchEntity && err != fsi.ErrRootDirNoFile {
   149  		return nil, err
   150  	}
   151  	if err == datastore.ErrNoSuchEntity || err == fsi.ErrRootDirNoFile {
   152  		// http.FileServer requires, that
   153  		// we return a directory here.
   154  		// It's also compliant to os.Open(),
   155  		// where "os.File" means directories too.
   156  		dir, err2 := fs.dirByPath(name)
   157  		if err2 != nil {
   158  			return nil, err
   159  		}
   160  		ff := fsi.File(&dir)
   161  		return ff, nil
   162  	}
   163  
   164  	atomic.StoreInt64(&f.at, 0) // why is this not nested into f.Lock()-f.Unlock()?
   165  
   166  	if f.closed == false { // already open
   167  		// return ErrFileInUse // instead of waiting for lock?
   168  	}
   169  
   170  	f.Lock()
   171  	f.closed = false
   172  	f.Unlock()
   173  
   174  	// return &f, nil
   175  	ff := fsi.File(&f)
   176  	return ff, nil
   177  }
   178  
   179  func (fs *dsFileSys) OpenFile(name string, flag int, perm os.FileMode) (fsi.File, error) {
   180  	return fs.Open(name)
   181  }
   182  
   183  // See fsi.FileSystem interface.
   184  
   185  //
   186  // ReadDir might not find recently added directories.
   187  func (fs *dsFileSys) ReadDir(name string) ([]os.FileInfo, error) {
   188  
   189  	dirs, err := fs.dirsByPath(name)
   190  	// fs.Ctx().Infof("dsfs readdir %-20v dirs %v", name, len(dirs))
   191  	if err != nil && err != fsi.EmptyQueryResult {
   192  		return nil, err
   193  	}
   194  	fs.dirsorter(dirs)
   195  
   196  	files, err := fs.filesByPath(name)
   197  	// fs.Ctx().Infof("dsfs readdir %-20v fils %v %v", name, len(files), err)
   198  	if err != nil {
   199  		return nil, err
   200  	}
   201  	fs.filesorter(files)
   202  
   203  	for _, v := range files {
   204  		dirs = append(dirs, os.FileInfo(v))
   205  	}
   206  	return dirs, nil
   207  }
   208  
   209  func (fs *dsFileSys) Remove(name string) error {
   210  
   211  	// log.Printf("trying to remove %-20v", name)
   212  	f, err := fs.fileByPath(name)
   213  	if err == nil {
   214  		// log.Printf("   found file %v", f.Dir+f.BName)
   215  		// log.Printf("   fkey %-26v", f.Key)
   216  		err = datastore.Delete(fs.Ctx(), f.Key)
   217  		if err != nil {
   218  			return fmt.Errorf("error removing file %v", err)
   219  		}
   220  	}
   221  
   222  	d, err := fs.dirByPath(name)
   223  	if err == nil {
   224  		// log.Printf("   dkey %v", d.Key)
   225  		err = datastore.Delete(fs.Ctx(), d.Key)
   226  		d.MemCacheDelete()
   227  		if err != nil {
   228  			return fmt.Errorf("error removing dir %v", err)
   229  		}
   230  	}
   231  
   232  	return nil
   233  
   234  }
   235  
   236  func (fs *dsFileSys) RemoveAll(path string) error {
   237  
   238  	paths := []string{}
   239  	walkRemove := func(path string, f os.FileInfo, err error) error {
   240  		if err != nil {
   241  			// do nothing; don't break the walk
   242  			aelog.Errorf(fs.Ctx(), "Error walking %v => %v", path, err)
   243  		} else {
   244  			if f != nil { // && f.IsDir() to constrain
   245  				paths = append(paths, path)
   246  			}
   247  		}
   248  		return nil
   249  	}
   250  
   251  	err := common.Walk(fs, path, walkRemove)
   252  	if err != nil {
   253  		aelog.Errorf(fs.Ctx(), "Error removing %v => %v", path, err)
   254  	}
   255  
   256  	// Walk crawls directories first, files second.
   257  	// Intuitively removal in reverse order should always work. Or does it not?
   258  	for i := 0; i < len(paths); i++ {
   259  		iRev := len(paths) - 1 - i
   260  		err := fs.Remove(paths[iRev])
   261  		if err != nil {
   262  			aelog.Errorf(fs.Ctx(), "Error removing %v => %v", paths[iRev], err)
   263  			return err
   264  		}
   265  		aelog.Infof(fs.Ctx(), "removed path %v", paths[iRev])
   266  	}
   267  
   268  	return nil
   269  }
   270  
   271  func (fs *dsFileSys) Rename(oldname, newname string) error {
   272  	// we could use a walk similar to remove all
   273  	return fsi.NotImplemented
   274  }
   275  
   276  func (fs *dsFileSys) Stat(path string) (os.FileInfo, error) {
   277  
   278  	f, err := fs.fileByPath(path)
   279  	if err != nil && err != datastore.ErrNoSuchEntity && err != fsi.ErrRootDirNoFile {
   280  		log.Fatalf("OTHER ERROR %v", err)
   281  
   282  		return nil, err
   283  	}
   284  	if err == datastore.ErrNoSuchEntity || err == fsi.ErrRootDirNoFile {
   285  		// log.Printf("isno file err %-24q =>  %v", path, err)
   286  		dir, err := fs.dirByPath(path)
   287  		if err != nil {
   288  			return nil, err
   289  		}
   290  		fiDir := os.FileInfo(dir)
   291  		// log.Printf("Stat for dire %-24q => %-24v, %v", path, fiDir.Name(), err)
   292  		return fiDir, nil
   293  	}
   294  
   295  	fiFi := os.FileInfo(f)
   296  	// log.Printf("Stat for file %-24q => %-24v, %v", path, fiFi.Name(), err)
   297  	return fiFi, nil
   298  }
   299  
   300  func (fs *dsFileSys) ReadFile(path string) ([]byte, error) {
   301  
   302  	file, err := fs.fileByPath(path)
   303  	if err != nil {
   304  		return []byte{}, err
   305  	}
   306  	return file.Data, err
   307  }
   308  
   309  // Only one save operation required
   310  func (fs *dsFileSys) WriteFile(name string, data []byte, perm os.FileMode) error {
   311  
   312  	// WriteFile & Create
   313  	dir, bname := fs.SplitX(name)
   314  	f := DsFile{}
   315  	f.Dir = dir
   316  	f.BName = common.Filify(bname)
   317  	f.fSys = fs
   318  	f.MModTime = time.Now()
   319  
   320  	var err error
   321  	_, err = f.Write(data)
   322  	if err != nil {
   323  		return err
   324  	}
   325  
   326  	err = f.Sync()
   327  	if err != nil {
   328  		return err
   329  	}
   330  
   331  	return nil
   332  }