Merge branch '12913-secondary-mounts' refs #12913
[arvados.git] / sdk / go / arvados / collection_fs.go
index 1aafe81f47749a2eabb711e09fe631e7f8c56be9..d8ee2a2b1c5175697bf39369274ff6c0a42e7310 100644 (file)
@@ -496,6 +496,8 @@ func (fn *filenode) Write(p []byte, startPtr filenodePtr) (n int, ptr filenodePt
                        ptr.segmentOff = 0
                        ptr.segmentIdx++
                }
+
+               fn.fileinfo.modTime = time.Now()
        }
        return
 }
@@ -531,12 +533,22 @@ func (fn *filenode) pruneMemSegments() {
 
 // FileSystem returns a CollectionFileSystem for the collection.
 func (c *Collection) FileSystem(client *Client, kc keepClient) (CollectionFileSystem, error) {
+       var modTime time.Time
+       if c.ModifiedAt == nil {
+               modTime = time.Now()
+       } else {
+               modTime = *c.ModifiedAt
+       }
        fs := &fileSystem{dirnode: dirnode{
-               client:   client,
-               kc:       kc,
-               fileinfo: fileinfo{name: ".", mode: os.ModeDir | 0755},
-               parent:   nil,
-               inodes:   make(map[string]inode),
+               client: client,
+               kc:     kc,
+               fileinfo: fileinfo{
+                       name:    ".",
+                       mode:    os.ModeDir | 0755,
+                       modTime: modTime,
+               },
+               parent: nil,
+               inodes: make(map[string]inode),
        }}
        fs.dirnode.parent = &fs.dirnode
        if err := fs.dirnode.loadManifest(c.ManifestText); err != nil {
@@ -568,11 +580,11 @@ func (f *filehandle) Seek(off int64, whence int) (pos int64, err error) {
        size := f.inode.Size()
        ptr := f.ptr
        switch whence {
-       case os.SEEK_SET:
+       case io.SeekStart:
                ptr.off = off
-       case os.SEEK_CUR:
+       case io.SeekCurrent:
                ptr.off += off
-       case os.SEEK_END:
+       case io.SeekEnd:
                ptr.off = size + off
        }
        if ptr.off < 0 {
@@ -852,7 +864,7 @@ func (dn *dirnode) loadManifest(txt string) error {
                                return fmt.Errorf("line %d: bad locator %q", lineno, token)
                        }
 
-                       toks := strings.Split(token, ":")
+                       toks := strings.SplitN(token, ":", 3)
                        if len(toks) != 3 {
                                return fmt.Errorf("line %d: bad file segment %q", lineno, token)
                        }
@@ -943,7 +955,7 @@ func (dn *dirnode) createFileAndParents(path string) (fn *filenode, err error) {
                default:
                        switch node := dn.inodes[name].(type) {
                        case nil:
-                               dn = dn.newDirnode(name, 0755)
+                               dn = dn.newDirnode(name, 0755, dn.fileinfo.modTime)
                        case *dirnode:
                                dn = node
                        case *filenode:
@@ -954,7 +966,7 @@ func (dn *dirnode) createFileAndParents(path string) (fn *filenode, err error) {
        }
        switch node := dn.inodes[basename].(type) {
        case nil:
-               fn = dn.newFilenode(basename, 0755)
+               fn = dn.newFilenode(basename, 0755, dn.fileinfo.modTime)
        case *filenode:
                fn = node
        case *dirnode:
@@ -1066,6 +1078,10 @@ func (dn *dirnode) Rename(oldname, newname string) error {
        if !ok {
                return os.ErrNotExist
        }
+       if locked[oldinode] {
+               // oldinode cannot become a descendant of itself.
+               return ErrInvalidArgument
+       }
        if existing, ok := newdn.inodes[newname]; ok {
                // overwriting an existing file or dir
                if dn, ok := existing.(*dirnode); ok {
@@ -1162,14 +1178,15 @@ func (dn *dirnode) lookupPath(path string) (node inode) {
        return
 }
 
-func (dn *dirnode) newDirnode(name string, perm os.FileMode) *dirnode {
+func (dn *dirnode) newDirnode(name string, perm os.FileMode, modTime time.Time) *dirnode {
        child := &dirnode{
                parent: dn,
                client: dn.client,
                kc:     dn.kc,
                fileinfo: fileinfo{
-                       name: name,
-                       mode: os.ModeDir | perm,
+                       name:    name,
+                       mode:    os.ModeDir | perm,
+                       modTime: modTime,
                },
        }
        if dn.inodes == nil {
@@ -1180,12 +1197,13 @@ func (dn *dirnode) newDirnode(name string, perm os.FileMode) *dirnode {
        return child
 }
 
-func (dn *dirnode) newFilenode(name string, perm os.FileMode) *filenode {
+func (dn *dirnode) newFilenode(name string, perm os.FileMode, modTime time.Time) *filenode {
        child := &filenode{
                parent: dn,
                fileinfo: fileinfo{
-                       name: name,
-                       mode: perm,
+                       name:    name,
+                       mode:    perm,
+                       modTime: modTime,
                },
        }
        if dn.inodes == nil {
@@ -1242,9 +1260,9 @@ func (dn *dirnode) OpenFile(name string, flag int, perm os.FileMode) (*filehandl
                        return nil, os.ErrNotExist
                }
                if perm.IsDir() {
-                       n = dn.newDirnode(name, 0755)
+                       n = dn.newDirnode(name, 0755, time.Now())
                } else {
-                       n = dn.newFilenode(name, 0755)
+                       n = dn.newFilenode(name, 0755, time.Now())
                }
        } else if flag&os.O_EXCL != 0 {
                return nil, ErrFileExists
@@ -1331,9 +1349,9 @@ func (me *memSegment) ReadAt(p []byte, off int64) (n int, err error) {
 type storedSegment struct {
        kc      keepClient
        locator string
-       size    int
-       offset  int
-       length  int
+       size    int // size of stored block (also encoded in locator)
+       offset  int // position of segment within the stored block
+       length  int // bytes in this segment (offset + length <= size)
 }
 
 func (se storedSegment) Len() int {
@@ -1375,7 +1393,7 @@ func canonicalName(name string) string {
        return name
 }
 
-var manifestEscapeSeq = regexp.MustCompile(`\\([0-9]{3}|\\)`)
+var manifestEscapeSeq = regexp.MustCompile(`\\([0-7]{3}|\\)`)
 
 func manifestUnescapeFunc(seq string) string {
        if seq == `\\` {
@@ -1393,7 +1411,7 @@ func manifestUnescape(s string) string {
        return manifestEscapeSeq.ReplaceAllStringFunc(s, manifestUnescapeFunc)
 }
 
-var manifestEscapedChar = regexp.MustCompile(`[^\.\w/]`)
+var manifestEscapedChar = regexp.MustCompile(`[\000-\040:\s\\]`)
 
 func manifestEscapeFunc(seq string) string {
        return fmt.Sprintf("\\%03o", byte(seq[0]))