X-Git-Url: https://git.arvados.org/arvados.git/blobdiff_plain/12e80e523f1178c0db49a3c9d856d17bb7855dfe..5ea2ee645b5508c9c14cc2abe6cb8c2f24039c83:/services/keepstore/volume_unix.go diff --git a/services/keepstore/volume_unix.go b/services/keepstore/volume_unix.go index 6960098c5a..84877c0034 100644 --- a/services/keepstore/volume_unix.go +++ b/services/keepstore/volume_unix.go @@ -103,13 +103,25 @@ func (v *UnixVolume) Touch(loc string) error { if err != nil { return err } - lockfile(f) + defer f.Close() + if e := lockfile(f); e != nil { + return e + } defer unlockfile(f) now := time.Now().Unix() utime := syscall.Utimbuf{now, now} return syscall.Utime(p, &utime) } +func (v *UnixVolume) Mtime(loc string) (time.Time, error) { + p := v.blockPath(loc) + if fi, err := os.Stat(p); err != nil { + return time.Time{}, err + } else { + return fi.ModTime(), nil + } +} + // Read retrieves a block identified by the locator string "loc", and // returns its contents as a byte slice. // @@ -243,24 +255,35 @@ func (v *UnixVolume) Index(prefix string) (output string) { } func (v *UnixVolume) Delete(loc string) error { + // Touch() must be called before calling Write() on a block. Touch() + // also uses lockfile(). This avoids a race condition between Write() + // and Delete() because either (a) the file will be deleted and Touch() + // will signal to the caller that the file is not present (and needs to + // be re-written), or (b) Touch() will update the file's timestamp and + // Delete() will read the correct up-to-date timestamp and choose not to + // delete the file. + p := v.blockPath(loc) f, err := os.OpenFile(p, os.O_RDWR|os.O_APPEND, 0644) if err != nil { return err } - lockfile(f) + defer f.Close() + if e := lockfile(f); e != nil { + return e + } defer unlockfile(f) - // Return PermissionError if the block has been PUT more recently - // than -permission_ttl. This guards against a race condition - // where a block is old enough that Data Manager has added it to - // the trash list, but the user submitted a PUT for the block - // since then. + // If the block has been PUT more recently than -permission_ttl, + // return success without removing the block. This guards against + // a race condition where a block is old enough that Data Manager + // has added it to the trash list, but the user submitted a PUT + // for the block since then. if fi, err := os.Stat(p); err != nil { return err } else { if time.Since(fi.ModTime()) < permission_ttl { - return PermissionError + return nil } } return os.Remove(p)