From e056081be1c259ea6fae8bda1cafb081b99c288c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tam=C3=A1s=20Gul=C3=A1csi?= Date: Mon, 7 Oct 2013 07:55:49 +0200 Subject: [PATCH] add capctime flag to camput I can recreate the file, set the modification time, but can't forge creation time, thus cannot recreate the exact same uploaded file twice. This change targets this problem. As this is a very rarely used flag, this is enabled only if the CAMLI_DEBUG environment variable is true - such as the other "gross" options (statcache, sqlitecache...). Change-Id: I99c7805898ed97883043de4e7dcec23a472e462c --- cmd/camput/files.go | 6 ++++++ cmd/camput/uploader.go | 9 +++++++-- pkg/schema/blob.go | 13 +++++++++++++ 3 files changed, 26 insertions(+), 2 deletions(-) diff --git a/cmd/camput/files.go b/cmd/camput/files.go index 559e40f76..7c874ab73 100644 --- a/cmd/camput/files.go +++ b/cmd/camput/files.go @@ -52,6 +52,7 @@ type fileCmd struct { filePermanodes bool // make planned permanodes for each file (based on their digest) vivify bool exifTime bool // use metadata (such as in EXIF) to find the creation time of the file + capCtime bool // use mtime as creation time of the file, if it would be bigger than modification time diskUsage bool // show "du" disk usage only (dry run mode), don't actually upload argsFromInput bool // Android mode: filenames piped into stdin, one at a time. @@ -84,6 +85,7 @@ func init() { flags.BoolVar(&cmd.havecache, "havecache", true, "Use the 'have cache', a cache keeping track of what blobs the remote server should already have from previous uploads.") flags.BoolVar(&cmd.memstats, "debug-memstats", false, "Enter debug in-memory mode; collecting stats only. Doesn't upload anything.") flags.StringVar(&cmd.histo, "debug-histogram-file", "", "Optional file to create and write the blob size for each file uploaded. For use with GNU R and hist(read.table(\"filename\")$V1). Requires debug-memstats.") + flags.BoolVar(&cmd.capCtime, "capctime", false, "For file blobs use file modification time as creation time if it would be bigger (newer) than modification time. For stable filenode creation (you can forge mtime, but can't forge ctime).") flags.BoolVar(&flagUseSQLiteChildCache, "sqlitecache", false, "Use sqlite for the statcache and havecache instead of a flat cache.") } else { cmd.havecache = true @@ -148,6 +150,7 @@ func (c *fileCmd) RunCommand(args []string) error { tag: c.tag, vivify: c.vivify, exifTime: c.exifTime, + capCtime: c.capCtime, } var ( @@ -516,6 +519,9 @@ func (up *Uploader) uploadNodeRegularFile(n *node) (*client.PutResult, error) { filebb.SetModTime(modtime) } } + if up.fileOpts.capCtime { + filebb.CapCreationTime() + } var ( size = n.fi.Size() diff --git a/cmd/camput/uploader.go b/cmd/camput/uploader.go index 0ccbaaa09..75e003b25 100644 --- a/cmd/camput/uploader.go +++ b/cmd/camput/uploader.go @@ -54,8 +54,9 @@ type fileOptions struct { // the above permanode. tag string // perform for the client the actions needing gpg signing when uploading a file. - vivify bool - exifTime bool // use the time in exif metadata as the modtime if possible. + vivify bool + exifTime bool // use the time in exif metadata as the modtime if possible. + capCtime bool // use mtime as ctime if ctime > mtime } func (o *fileOptions) tags() []string { @@ -73,6 +74,10 @@ func (o *fileOptions) wantVivify() bool { return o != nil && o.vivify } +func (o *fileOptions) wantCapCtime() bool { + return o != nil && o.capCtime +} + func (up *Uploader) uploadString(s string) (*client.PutResult, error) { return up.Upload(client.NewUploadHandleFromString(s)) } diff --git a/pkg/schema/blob.go b/pkg/schema/blob.go index 6a00a381f..9ca59acdc 100644 --- a/pkg/schema/blob.go +++ b/pkg/schema/blob.go @@ -383,6 +383,19 @@ func (bb *Builder) SetModTime(t time.Time) *Builder { return bb } +// CapCreationTime caps the "unixCtime" field to be less or equal than "unixMtime" +func (bb *Builder) CapCreationTime() *Builder { + ctime, ok := bb.m["unixCtime"].(string) + if !ok { + return bb + } + mtime, ok := bb.m["unixMtime"].(string) + if ok && ctime > mtime { + bb.m["unixCtime"] = mtime + } + return bb +} + // ModTime returns the "unixMtime" modtime field, if set. func (bb *Builder) ModTime() (t time.Time, ok bool) { s, ok := bb.m["unixMtime"].(string)