X-Git-Url: https://git.arvados.org/arvados.git/blobdiff_plain/32f7abdccc3ad293266e8df3e50344614ecd0dac..0821f5481edd016a3744bb50d97a9e5b99cd1a0f:/services/keepstore/volume_unix.go diff --git a/services/keepstore/volume_unix.go b/services/keepstore/volume_unix.go index eca0aeed92..5982fb0484 100644 --- a/services/keepstore/volume_unix.go +++ b/services/keepstore/volume_unix.go @@ -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,16 +397,14 @@ 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 { return os.Remove(p) } - return os.Rename(p, fmt.Sprintf("%v.trash.%d", p, time.Now().Unix()+int64(trashLifetime))) + return os.Rename(p, fmt.Sprintf("%v.trash.%d", p, time.Now().Add(trashLifetime).Unix())) } // Untrash moves block from trash back into store @@ -420,17 +415,31 @@ func (v *UnixVolume) Untrash(loc string) (err error) { return MethodDisabledError } + 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) - files, _ := ioutil.ReadDir(v.blockDir(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 { + if err == nil { break } } } + if foundTrash == false { + return os.ErrNotExist + } + return } @@ -495,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 } @@ -526,50 +538,49 @@ func (v *UnixVolume) translateError(err error) error { } } -var trashRegexp = regexp.MustCompile(`.*([0-9a-fA-F]{32}).trash.(\d+)`) +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 { - log.Printf("EmptyTrash error for %v: %v", path, err) - } else if !info.Mode().IsDir() { - matches := trashRegexp.FindStringSubmatch(path) - if len(matches) == 3 { - deadline, err := strconv.Atoi(matches[2]) - 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++ - } - } - } + log.Printf("EmptyTrash: filepath.Walk: %v: %v", path, err) + return nil + } + 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 }) if err != nil { log.Printf("EmptyTrash error for %v: %v", v.String(), err) - } else { - 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) }