X-Git-Url: https://git.arvados.org/arvados.git/blobdiff_plain/418c57bce3aac1a22548e53e1018a1547d9efee4..79a9d03d3470ef013a908051a2bccddc680ecb5c:/sdk/go/arvados/fs_collection.go diff --git a/sdk/go/arvados/fs_collection.go b/sdk/go/arvados/fs_collection.go index 923615ba76..f6afadba5b 100644 --- a/sdk/go/arvados/fs_collection.go +++ b/sdk/go/arvados/fs_collection.go @@ -31,6 +31,9 @@ type CollectionFileSystem interface { // Prefix (normally ".") is a top level directory, effectively // prepended to all paths in the returned manifest. MarshalManifest(prefix string) (string, error) + + // Total data bytes in all files. + Size() int64 } type collectionFileSystem struct { @@ -139,6 +142,10 @@ func (fs *collectionFileSystem) MarshalManifest(prefix string) (string, error) { return fs.fileSystem.root.(*dirnode).marshalManifest(prefix) } +func (fs *collectionFileSystem) Size() int64 { + return fs.fileSystem.root.(*dirnode).TreeSize() +} + // filenodePtr is an offset into a file that is (usually) efficient to // seek to. Specifically, if filenode.repacked==filenodePtr.repacked // then @@ -542,9 +549,10 @@ func (dn *dirnode) Child(name string, replace func(inode) (inode, error)) (inode 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 { +// sync flushes in-memory data (for the children with the given names, +// which must be children of dn) to persistent storage. Caller must +// have write lock on dn and the named children. +func (dn *dirnode) sync(names []string) error { type shortBlock struct { fn *filenode idx int @@ -580,19 +588,11 @@ func (dn *dirnode) sync() error { return nil } - names := make([]string, 0, len(dn.inodes)) - for name := range dn.inodes { - names = append(names, name) - } - sort.Strings(names) - for _, name := range names { fn, ok := dn.inodes[name].(*filenode) if !ok { continue } - fn.Lock() - defer fn.Unlock() for idx, seg := range fn.segments { seg, ok := seg.(*memSegment) if !ok { @@ -630,18 +630,19 @@ func (dn *dirnode) marshalManifest(prefix string) (string, error) { var subdirs string var blocks []string - if err := dn.sync(); err != nil { - return "", err - } - names := make([]string, 0, len(dn.inodes)) - for name, node := range dn.inodes { + for name := range dn.inodes { names = append(names, name) + } + sort.Strings(names) + for _, name := range names { + node := dn.inodes[name] node.Lock() defer node.Unlock() } - sort.Strings(names) - + if err := dn.sync(names); err != nil { + return "", err + } for _, name := range names { switch node := dn.inodes[name].(type) { case *dirnode: @@ -821,8 +822,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] { @@ -877,6 +878,20 @@ func (dn *dirnode) createFileAndParents(path string) (fn *filenode, err error) { return } +func (dn *dirnode) TreeSize() (bytes int64) { + dn.RLock() + defer dn.RUnlock() + for _, i := range dn.inodes { + switch i := i.(type) { + case *filenode: + bytes += i.Size() + case *dirnode: + bytes += i.TreeSize() + } + } + return +} + type segment interface { io.ReaderAt Len() int