+
+// 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
+}
+
+// lockfile and unlockfile use flock(2) to manage kernel file locks.
+func lockfile(f *os.File) error {
+ return syscall.Flock(int(f.Fd()), syscall.LOCK_EX)
+}
+
+func unlockfile(f *os.File) error {
+ return syscall.Flock(int(f.Fd()), syscall.LOCK_UN)
+}
+
+// Where appropriate, translate a more specific filesystem error to an
+// error recognized by handlers, like os.ErrNotExist.
+func (v *UnixVolume) translateError(err error) error {
+ switch err.(type) {
+ case *os.PathError:
+ // stat() returns a PathError if the parent directory
+ // (not just the file itself) is missing
+ return os.ErrNotExist
+ default:
+ return err
+ }
+}
+
+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() {
+ 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: 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)
+ }
+
+ 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)
+}