+ // 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.
+
+ if v.readonly {
+ return MethodDisabledError
+ }
+ if v.serialize {
+ v.mutex.Lock()
+ defer v.mutex.Unlock()
+ }
+ p := v.blockPath(loc)
+ f, err := os.OpenFile(p, os.O_RDWR|os.O_APPEND, 0644)
+ if err != nil {
+ return err
+ }
+ defer f.Close()
+ if e := lockfile(f); e != nil {
+ return e
+ }
+ defer unlockfile(f)
+
+ // If the block has been PUT in the last blob_signature_ttl
+ // seconds, return success without removing the block. This
+ // protects data from garbage collection until it is no longer
+ // possible for clients to retrieve the unreferenced blocks
+ // anyway (because the permission signatures have expired).
+ if fi, err := os.Stat(p); err != nil {
+ return err
+ } else {
+ if time.Since(fi.ModTime()) < blob_signature_ttl {
+ return nil
+ }
+ }
+ return os.Remove(p)