Merge branch '9857-cwl-acceptlist-re' refs #9857
[arvados.git] / services / keepstore / volume_unix.go
index 9183a398f34ca89cc40eb6b6c5f1c2122f831a84..5982fb0484eae0a37ef09e24b9526492c5b0459f 100644 (file)
@@ -138,9 +138,8 @@ func (v *UnixVolume) Touch(loc string) error {
                return e
        }
        defer unlockfile(f)
-       now := time.Now().Unix()
-       utime := syscall.Utimbuf{now, now}
-       return syscall.Utime(p, &utime)
+       ts := syscall.NsecToTimespec(time.Now().UnixNano())
+       return syscall.UtimesNano(p, []syscall.Timespec{ts, ts})
 }
 
 // Mtime returns the stored timestamp for the given locator.
@@ -181,26 +180,24 @@ func (v *UnixVolume) stat(path string) (os.FileInfo, error) {
        return stat, err
 }
 
-// Get retrieves a block identified by the locator string "loc", and
-// returns its contents as a byte slice.
-//
-// Get returns a nil buffer IFF it returns a non-nil error.
-func (v *UnixVolume) Get(loc string) ([]byte, error) {
+// Get retrieves a block, copies it to the given slice, and returns
+// the number of bytes copied.
+func (v *UnixVolume) Get(loc string, buf []byte) (int, error) {
        path := v.blockPath(loc)
        stat, err := v.stat(path)
        if err != nil {
-               return nil, v.translateError(err)
+               return 0, v.translateError(err)
+       }
+       if stat.Size() > int64(len(buf)) {
+               return 0, TooLongError
        }
-       buf := bufs.Get(int(stat.Size()))
+       var read int
+       size := int(stat.Size())
        err = v.getFunc(path, func(rdr io.Reader) error {
-               _, err = io.ReadFull(rdr, buf)
+               read, err = io.ReadFull(rdr, buf[:size])
                return err
        })
-       if err != nil {
-               bufs.Put(buf)
-               return nil, err
-       }
-       return buf, nil
+       return read, err
 }
 
 // Compare returns nil if Get(loc) would return the same content as
@@ -309,7 +306,7 @@ var blockFileRe = regexp.MustCompile(`^[0-9a-f]{32}$`)
 //     e4de7a2810f5554cd39b36d8ddb132ff+67108864 1388701136
 //
 func (v *UnixVolume) IndexTo(prefix string, w io.Writer) error {
-       var lastErr error = nil
+       var lastErr error
        rootdir, err := os.Open(v.root)
        if err != nil {
                return err
@@ -355,7 +352,7 @@ func (v *UnixVolume) IndexTo(prefix string, w io.Writer) error {
                        _, err = fmt.Fprint(w,
                                name,
                                "+", fileInfo[0].Size(),
-                               " ", fileInfo[0].ModTime().Unix(),
+                               " ", fileInfo[0].ModTime().UnixNano(),
                                "\n")
                }
                blockdir.Close()
@@ -400,10 +397,8 @@ func (v *UnixVolume) Trash(loc string) error {
        // anyway (because the permission signatures have expired).
        if fi, err := os.Stat(p); err != nil {
                return err
-       } else {
-               if time.Since(fi.ModTime()) < blobSignatureTTL {
-                       return nil
-               }
+       } else if time.Since(fi.ModTime()) < blobSignatureTTL {
+               return nil
        }
 
        if trashLifetime == 0 {
@@ -420,16 +415,20 @@ func (v *UnixVolume) Untrash(loc string) (err error) {
                return MethodDisabledError
        }
 
-       prefix := fmt.Sprintf("%v.trash.", loc)
        files, err := ioutil.ReadDir(v.blockDir(loc))
        if err != nil {
                return err
        }
+
        if len(files) == 0 {
                return os.ErrNotExist
        }
+
+       foundTrash := false
+       prefix := fmt.Sprintf("%v.trash.", loc)
        for _, f := range files {
                if strings.HasPrefix(f.Name(), prefix) {
+                       foundTrash = true
                        err = os.Rename(v.blockPath(f.Name()), v.blockPath(loc))
                        if err == nil {
                                break
@@ -437,6 +436,10 @@ func (v *UnixVolume) Untrash(loc string) (err error) {
                }
        }
 
+       if foundTrash == false {
+               return os.ErrNotExist
+       }
+
        return
 }
 
@@ -501,11 +504,14 @@ func (v *UnixVolume) String() string {
        return fmt.Sprintf("[UnixVolume %s]", v.root)
 }
 
-// Writable returns false if all future Put, Mtime, and Delete calls are expected to fail.
+// Writable returns false if all future Put, Mtime, and Delete calls
+// are expected to fail.
 func (v *UnixVolume) Writable() bool {
        return !v.readonly
 }
 
+// Replication returns the number of replicas promised by the
+// underlying device (currently assumed to be 1).
 func (v *UnixVolume) Replication() int {
        return 1
 }
@@ -532,41 +538,43 @@ func (v *UnixVolume) translateError(err error) error {
        }
 }
 
+var unixTrashLocRegexp = regexp.MustCompile(`/([0-9a-f]{32})\.trash\.(\d+)$`)
+
 // EmptyTrash walks hierarchy looking for {hash}.trash.*
 // and deletes those with deadline < now.
-func (v *UnixVolume) EmptyTrash() error {
+func (v *UnixVolume) EmptyTrash() {
        var bytesDeleted, bytesInTrash int64
        var blocksDeleted, blocksInTrash int
 
        err := filepath.Walk(v.root, func(path string, info os.FileInfo, err error) error {
                if err != nil {
-                       return err
+                       log.Printf("EmptyTrash: filepath.Walk: %v: %v", path, err)
+                       return nil
                }
-
-               if !info.Mode().IsDir() {
-                       matches := trashLocRegexp.FindStringSubmatch(path)
-                       if len(matches) == 3 {
-                               deadline, err := strconv.ParseInt(matches[2], 10, 64)
-                               if err != nil {
-                                       log.Printf("EmptyTrash error for %v: %v", matches[1], err)
-                               } else {
-                                       if int64(deadline) <= time.Now().Unix() {
-                                               err = os.Remove(path)
-                                               if err != nil {
-                                                       log.Printf("Error deleting %v: %v", matches[1], err)
-                                                       bytesInTrash += info.Size()
-                                                       blocksInTrash++
-                                               } else {
-                                                       bytesDeleted += info.Size()
-                                                       blocksDeleted++
-                                               }
-                                       } else {
-                                               bytesInTrash += info.Size()
-                                               blocksInTrash++
-                                       }
-                               }
-                       }
+               if info.Mode().IsDir() {
+                       return nil
+               }
+               matches := unixTrashLocRegexp.FindStringSubmatch(path)
+               if len(matches) != 3 {
+                       return nil
+               }
+               deadline, err := strconv.ParseInt(matches[2], 10, 64)
+               if err != nil {
+                       log.Printf("EmptyTrash: %v: ParseInt(%v): %v", path, matches[2], err)
+                       return nil
+               }
+               bytesInTrash += info.Size()
+               blocksInTrash++
+               if deadline > time.Now().Unix() {
+                       return nil
+               }
+               err = os.Remove(path)
+               if err != nil {
+                       log.Printf("EmptyTrash: Remove %v: %v", path, err)
+                       return nil
                }
+               bytesDeleted += info.Size()
+               blocksDeleted++
                return nil
        })
 
@@ -574,7 +582,5 @@ func (v *UnixVolume) EmptyTrash() error {
                log.Printf("EmptyTrash error for %v: %v", v.String(), err)
        }
 
-       log.Printf("EmptyTrash stats for %v: Bytes deleted %v; Blocks deleted %v; Bytes remaining in trash: %v; Blocks remaining in trash: %v", v.String(), bytesDeleted, blocksDeleted, bytesInTrash, blocksInTrash)
-
-       return nil
+       log.Printf("EmptyTrash stats for %v: Deleted %v bytes in %v blocks. Remaining in trash: %v bytes in %v blocks.", v.String(), bytesDeleted, blocksDeleted, bytesInTrash-bytesDeleted, blocksInTrash-blocksDeleted)
 }