16306: Merge branch 'master'
[arvados.git] / sdk / go / arvados / fs_base.go
index fa43cca0fab610f1ebf09a26f827792e538ffb27..aa75fee7c4d0f5bf47826d12300b51cdcf08a424 100644 (file)
@@ -31,6 +31,10 @@ var (
        ErrPermission        = os.ErrPermission
 )
 
+type syncer interface {
+       Sync() error
+}
+
 // A File is an *os.File-like interface for reading and writing files
 // in a FileSystem.
 type File interface {
@@ -58,6 +62,9 @@ type FileSystem interface {
        // while locking multiple inodes.
        locker() sync.Locker
 
+       // throttle for limiting concurrent background writers
+       throttle() *throttle
+
        // create a new node with nil parent.
        newNode(name string, perm os.FileMode, modTime time.Time) (node inode, err error)
 
@@ -86,7 +93,19 @@ type FileSystem interface {
        Remove(name string) error
        RemoveAll(name string) error
        Rename(oldname, newname string) error
+
+       // Write buffered data from memory to storage, returning when
+       // all updates have been saved to persistent storage.
        Sync() error
+
+       // Write buffered data from memory to storage, but don't wait
+       // for all writes to finish before returning. If shortBlocks
+       // is true, flush everything; otherwise, if there's less than
+       // a full block of buffered data at the end of a stream, leave
+       // it buffered in memory in case more data can be appended. If
+       // path is "", flush all dirs/streams; otherwise, flush only
+       // the specified dir/stream.
+       Flush(path string, shortBlocks bool) error
 }
 
 type inode interface {
@@ -284,16 +303,37 @@ func (n *treenode) Readdir() (fi []os.FileInfo, err error) {
        return
 }
 
+func (n *treenode) Sync() error {
+       n.RLock()
+       defer n.RUnlock()
+       for _, inode := range n.inodes {
+               syncer, ok := inode.(syncer)
+               if !ok {
+                       return ErrInvalidOperation
+               }
+               err := syncer.Sync()
+               if err != nil {
+                       return err
+               }
+       }
+       return nil
+}
+
 type fileSystem struct {
        root inode
        fsBackend
        mutex sync.Mutex
+       thr   *throttle
 }
 
 func (fs *fileSystem) rootnode() inode {
        return fs.root
 }
 
+func (fs *fileSystem) throttle() *throttle {
+       return fs.thr
+}
+
 func (fs *fileSystem) locker() sync.Locker {
        return &fs.mutex
 }
@@ -452,7 +492,7 @@ func (fs *fileSystem) Rename(oldname, newname string) error {
        // call nca.FS().Rename() instead of proceeding. Until then
        // it's awkward for filesystems to implement their own Rename
        // methods effectively: the only one that runs is the one on
-       // the root filesystem exposed to the caller (webdav, fuse,
+       // the root FileSystem exposed to the caller (webdav, fuse,
        // etc).
 
        // When acquiring locks on multiple inodes, avoid deadlock by
@@ -556,7 +596,14 @@ func (fs *fileSystem) remove(name string, recursive bool) error {
 }
 
 func (fs *fileSystem) Sync() error {
-       log.Printf("TODO: sync fileSystem")
+       if syncer, ok := fs.root.(syncer); ok {
+               return syncer.Sync()
+       }
+       return ErrInvalidOperation
+}
+
+func (fs *fileSystem) Flush(string, bool) error {
+       log.Printf("TODO: flush fileSystem")
        return ErrInvalidOperation
 }
 
@@ -589,3 +636,7 @@ func rlookup(start inode, path string) (node inode, err error) {
        }
        return
 }
+
+func permittedName(name string) bool {
+       return name != "" && name != "." && name != ".." && !strings.Contains(name, "/")
+}