Merge branch 'thehyve/fix-crunch-documentation' Fix a typo in Crunch Dispatch install...
[arvados.git] / sdk / go / arvados / fs_collection.go
index af0068f99392b0b3107e4bdb5f2d8044053fa020..7ce37aa24e7b35bfbabec9508af3b2e308d4cc76 100644 (file)
@@ -63,14 +63,27 @@ func (c *Collection) FileSystem(client apiClient, kc keepClient) (CollectionFile
                        inodes: make(map[string]inode),
                },
        }
-       root.SetParent(root)
+       root.SetParent(root, ".")
        if err := root.loadManifest(c.ManifestText); err != nil {
                return nil, err
        }
+       backdateTree(root, modTime)
        fs.root = root
        return fs, nil
 }
 
+func backdateTree(n inode, modTime time.Time) {
+       switch n := n.(type) {
+       case *filenode:
+               n.fileinfo.modTime = modTime
+       case *dirnode:
+               n.fileinfo.modTime = modTime
+               for _, n := range n.inodes {
+                       backdateTree(n, modTime)
+               }
+       }
+}
+
 func (fs *collectionFileSystem) newNode(name string, perm os.FileMode, modTime time.Time) (node inode, err error) {
        if name == "" || name == "." || name == ".." {
                return nil, ErrInvalidArgument
@@ -219,10 +232,11 @@ func (fn *filenode) appendSegment(e segment) {
        fn.fileinfo.size += int64(e.Len())
 }
 
-func (fn *filenode) SetParent(p inode) {
-       fn.RLock()
-       defer fn.RUnlock()
+func (fn *filenode) SetParent(p inode, name string) {
+       fn.Lock()
+       defer fn.Unlock()
        fn.parent = p
+       fn.fileinfo.name = name
 }
 
 func (fn *filenode) Parent() inode {
@@ -480,7 +494,7 @@ func (fn *filenode) pruneMemSegments() {
                if !ok || seg.Len() < maxBlockSize {
                        continue
                }
-               locator, _, err := fn.parent.(fsBackend).PutB(seg.buf)
+               locator, _, err := fn.FS().PutB(seg.buf)
                if err != nil {
                        // TODO: stall (or return errors from)
                        // subsequent writes until flushing
@@ -489,7 +503,7 @@ func (fn *filenode) pruneMemSegments() {
                }
                fn.memsize -= int64(seg.Len())
                fn.segments[idx] = storedSegment{
-                       kc:      fn.parent.FS(),
+                       kc:      fn.FS(),
                        locator: locator,
                        size:    seg.Len(),
                        offset:  0,
@@ -507,7 +521,7 @@ func (dn *dirnode) FS() FileSystem {
        return dn.fs
 }
 
-func (dn *dirnode) Child(name string, replace func(inode) inode) inode {
+func (dn *dirnode) Child(name string, replace func(inode) (inode, error)) (inode, error) {
        if dn == dn.fs.rootnode() && name == ".arvados#collection" {
                gn := &getternode{Getter: func() ([]byte, error) {
                        var coll Collection
@@ -518,12 +532,12 @@ func (dn *dirnode) Child(name string, replace func(inode) inode) inode {
                        }
                        data, err := json.Marshal(&coll)
                        if err == nil {
-                               data = append(data, 10)
+                               data = append(data, '\n')
                        }
                        return data, err
                }}
-               gn.SetParent(dn)
-               return gn
+               gn.SetParent(dn, name)
+               return gn, nil
        }
        return dn.treenode.Child(name, replace)
 }
@@ -807,8 +821,8 @@ func (dn *dirnode) createFileAndParents(path string) (fn *filenode, err error) {
        var node inode = dn
        names := strings.Split(path, "/")
        basename := names[len(names)-1]
-       if basename == "" || basename == "." || basename == ".." {
-               err = fmt.Errorf("invalid filename")
+       if !permittedName(basename) {
+               err = fmt.Errorf("invalid file part %q in path %q", basename, path)
                return
        }
        for _, name := range names[:len(names)-1] {
@@ -823,36 +837,41 @@ func (dn *dirnode) createFileAndParents(path string) (fn *filenode, err error) {
                        node = node.Parent()
                        continue
                }
-               node.Child(name, func(child inode) inode {
+               node, err = node.Child(name, func(child inode) (inode, error) {
                        if child == nil {
-                               node, err = node.FS().newNode(name, 0755|os.ModeDir, node.Parent().FileInfo().ModTime())
-                               child = node
+                               child, err := node.FS().newNode(name, 0755|os.ModeDir, node.Parent().FileInfo().ModTime())
+                               if err != nil {
+                                       return nil, err
+                               }
+                               child.SetParent(node, name)
+                               return child, nil
                        } else if !child.IsDir() {
-                               err = ErrFileExists
+                               return child, ErrFileExists
                        } else {
-                               node = child
+                               return child, nil
                        }
-                       return child
                })
                if err != nil {
                        return
                }
        }
-       node.Child(basename, func(child inode) inode {
+       _, err = node.Child(basename, func(child inode) (inode, error) {
                switch child := child.(type) {
                case nil:
                        child, err = node.FS().newNode(basename, 0755, node.FileInfo().ModTime())
+                       if err != nil {
+                               return nil, err
+                       }
+                       child.SetParent(node, basename)
                        fn = child.(*filenode)
-                       return child
+                       return child, nil
                case *filenode:
                        fn = child
-                       return child
+                       return child, nil
                case *dirnode:
-                       err = ErrIsDirectory
-                       return child
+                       return child, ErrIsDirectory
                default:
-                       err = ErrInvalidArgument
-                       return child
+                       return child, ErrInvalidArgument
                }
        })
        return