X-Git-Url: https://git.arvados.org/arvados.git/blobdiff_plain/18c8fa2337a2db04ee6060184885731f4b5c7d7b..47eb67e4c084abde49d5463d4ced8b4436a59dfd:/sdk/go/arvados/fs_collection.go diff --git a/sdk/go/arvados/fs_collection.go b/sdk/go/arvados/fs_collection.go index fc00335ce5..fbd9775b0c 100644 --- a/sdk/go/arvados/fs_collection.go +++ b/sdk/go/arvados/fs_collection.go @@ -21,28 +21,6 @@ import ( var maxBlockSize = 1 << 26 -type fsBackend interface { - keepClient - apiClient -} - -// Ideally *Client would do everything; meanwhile keepBackend -// implements fsBackend by merging the two kinds of arvados client. -type keepBackend struct { - keepClient - apiClient -} - -type keepClient interface { - ReadAt(locator string, p []byte, off int) (int, error) - PutB(p []byte) (string, int, error) -} - -type apiClient interface { - RequestAndDecode(dst interface{}, method, path string, body io.Reader, params interface{}) error - UpdateBody(rsc resource) io.Reader -} - // A CollectionFileSystem is a FileSystem that can be serialized as a // manifest and stored as a collection. type CollectionFileSystem interface { @@ -55,6 +33,11 @@ type CollectionFileSystem interface { MarshalManifest(prefix string) (string, error) } +type collectionFileSystem struct { + fileSystem + uuid string +} + // FileSystem returns a CollectionFileSystem for the collection. func (c *Collection) FileSystem(client apiClient, kc keepClient) (CollectionFileSystem, error) { var modTime time.Time @@ -64,14 +47,14 @@ func (c *Collection) FileSystem(client apiClient, kc keepClient) (CollectionFile modTime = *c.ModifiedAt } fs := &collectionFileSystem{ + uuid: c.UUID, fileSystem: fileSystem{ fsBackend: keepBackend{apiClient: client, keepClient: kc}, }, - uuid: c.UUID, } - dn := &dirnode{ + root := &dirnode{ + fs: fs, treenode: treenode{ - fs: fs, fileinfo: fileinfo{ name: ".", mode: os.ModeDir | 0755, @@ -80,17 +63,53 @@ func (c *Collection) FileSystem(client apiClient, kc keepClient) (CollectionFile inodes: make(map[string]inode), }, } - dn.parent = dn - fs.fileSystem.root = dn - if err := dn.loadManifest(c.ManifestText); err != nil { + root.SetParent(root, ".") + if err := root.loadManifest(c.ManifestText); err != nil { return nil, err } + backdateTree(root, modTime) + fs.root = root return fs, nil } -type collectionFileSystem struct { - fileSystem - uuid string +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 + } + if perm.IsDir() { + return &dirnode{ + fs: fs, + treenode: treenode{ + fileinfo: fileinfo{ + name: name, + mode: perm | os.ModeDir, + modTime: modTime, + }, + inodes: make(map[string]inode), + }, + }, nil + } else { + return &filenode{ + fs: fs, + fileinfo: fileinfo{ + name: name, + mode: perm & ^os.ModeDir, + modTime: modTime, + }, + }, nil + } } func (fs *collectionFileSystem) Sync() error { @@ -114,25 +133,6 @@ func (fs *collectionFileSystem) Sync() error { return err } -func (fs *collectionFileSystem) Child(name string, replace func(inode) inode) inode { - if name == ".arvados#collection" { - return &getternode{Getter: func() ([]byte, error) { - var coll Collection - var err error - coll.ManifestText, err = fs.MarshalManifest(".") - if err != nil { - return nil, err - } - data, err := json.Marshal(&coll) - if err == nil { - data = append(data, 10) - } - return data, err - }} - } - return fs.fileSystem.root.Child(name, replace) -} - func (fs *collectionFileSystem) MarshalManifest(prefix string) (string, error) { fs.fileSystem.root.Lock() defer fs.fileSystem.root.Unlock() @@ -232,6 +232,13 @@ func (fn *filenode) appendSegment(e segment) { fn.fileinfo.size += int64(e.Len()) } +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 { fn.RLock() defer fn.RUnlock() @@ -487,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 @@ -496,7 +503,7 @@ func (fn *filenode) pruneMemSegments() { } fn.memsize -= int64(seg.Len()) fn.segments[idx] = storedSegment{ - kc: fn.parent.(fsBackend), + kc: fn.FS(), locator: locator, size: seg.Len(), offset: 0, @@ -506,9 +513,35 @@ func (fn *filenode) pruneMemSegments() { } type dirnode struct { + fs *collectionFileSystem treenode } +func (dn *dirnode) FS() FileSystem { + return dn.fs +} + +func (dn *dirnode) Child(name string, replace func(inode) inode) inode { + if dn == dn.fs.rootnode() && name == ".arvados#collection" { + gn := &getternode{Getter: func() ([]byte, error) { + var coll Collection + var err error + coll.ManifestText, err = dn.fs.MarshalManifest(".") + if err != nil { + return nil, err + } + data, err := json.Marshal(&coll) + if err == nil { + data = append(data, '\n') + } + return data, err + }} + gn.SetParent(dn, name) + return gn + } + return dn.treenode.Child(name, replace) +} + // sync flushes in-memory data (for all files in the tree rooted at // dn) to persistent storage. Caller must hold dn.Lock(). func (dn *dirnode) sync() error { @@ -801,13 +834,14 @@ func (dn *dirnode) createFileAndParents(path string) (fn *filenode, err error) { // can't be sure parent will be a *dirnode return nil, ErrInvalidArgument } - node = node.Parent().(*dirnode) + node = node.Parent() continue } node.Child(name, func(child inode) inode { if child == nil { - node, err = node.FS().newDirnode(node, name, 0755|os.ModeDir, node.Parent().FileInfo().ModTime()) - child = node + child, err = node.FS().newNode(name, 0755|os.ModeDir, node.Parent().FileInfo().ModTime()) + child.SetParent(node, name) + node = child } else if !child.IsDir() { err = ErrFileExists } else { @@ -822,7 +856,8 @@ func (dn *dirnode) createFileAndParents(path string) (fn *filenode, err error) { node.Child(basename, func(child inode) inode { switch child := child.(type) { case nil: - child, err = node.FS().newFilenode(node, basename, 0755, node.FileInfo().ModTime()) + child, err = node.FS().newNode(basename, 0755, node.FileInfo().ModTime()) + child.SetParent(node, basename) fn = child.(*filenode) return child case *filenode: @@ -832,7 +867,7 @@ func (dn *dirnode) createFileAndParents(path string) (fn *filenode, err error) { err = ErrIsDirectory return child default: - err = ErrInvalidOperation + err = ErrInvalidArgument return child } })