Implement --immutable option

This commit is contained in:
Jacob McNamee
2017-09-24 20:00:00 +01:00
committed by Nick Craig-Wood
parent 5a3a56abd8
commit 2d8e75cab4
6 changed files with 81 additions and 13 deletions
+3
View File
@@ -102,6 +102,7 @@ var (
bindAddr = StringP("bind", "", "", "Local address to bind to for outgoing connections, IPv4, IPv6 or name.")
disableFeatures = StringP("disable", "", "", "Disable a comma separated list of features. Use help to see a list.")
userAgent = StringP("user-agent", "", "rclone/"+Version, "Set the user-agent to a specified string. The default is rclone/ version")
immutable = BoolP("immutable", "", false, "Do not modify files. Fail if existing files have been modified.")
streamingUploadCutoff = SizeSuffix(100 * 1024)
logLevel = LogLevelNotice
statsLogLevel = LogLevelInfo
@@ -240,6 +241,7 @@ type ConfigInfo struct {
TPSLimitBurst int
BindAddr net.IP
DisableFeatures []string
Immutable bool
StreamingUploadCutoff SizeSuffix
}
@@ -379,6 +381,7 @@ func LoadConfig() {
Config.UseListR = *useListR
Config.TPSLimit = *tpsLimit
Config.TPSLimitBurst = *tpsLimitBurst
Config.Immutable = *immutable
Config.BufferSize = bufferSize
Config.StreamingUploadCutoff = streamingUploadCutoff
+1
View File
@@ -50,6 +50,7 @@ var (
ErrorNotDeletingDirs = errors.New("not deleting directories as there were IO errors")
ErrorCantMoveOverlapping = errors.New("can't move files on overlapping remotes")
ErrorDirectoryNotEmpty = errors.New("directory not empty")
ErrorImmutableModified = errors.New("immutable file modified")
)
// RegInfo provides information about a filesystem
+7 -2
View File
@@ -180,8 +180,13 @@ func equal(src ObjectInfo, dst Object, sizeOnly, checkSum bool) bool {
if Config.DryRun {
Logf(src, "Not updating modification time as --dry-run")
} else {
// Size and hash the same but mtime different so update the
// mtime of the dst object here
// Size and hash the same but mtime different
// Error if objects are treated as immutable
if Config.Immutable {
Errorf(dst, "Timestamp mismatch between immutable objects")
return false
}
// Update the mtime of the dst object here
err := dst.SetModTime(srcModTime)
if err == ErrorCantSetModTime {
Debugf(dst, "src and dst identical but can't set mod time without re-uploading")
+17 -11
View File
@@ -264,20 +264,26 @@ func (s *syncCopyMove) pairChecker(in ObjectPairChan, out ObjectPairChan, wg *sy
// Check to see if can store this
if src.Storable() {
if NeedTransfer(pair.dst, pair.src) {
// If destination already exists, then we must move it into --backup-dir if required
if pair.dst != nil && s.backupDir != nil {
remoteWithSuffix := pair.dst.Remote() + s.suffix
overwritten, _ := s.backupDir.NewObject(remoteWithSuffix)
err := Move(s.backupDir, overwritten, remoteWithSuffix, pair.dst)
if err != nil {
s.processError(err)
// If files are treated as immutable, fail if destination exists and does not match
if Config.Immutable && pair.dst != nil {
Errorf(pair.dst, "Source and destination exist but do not match: immutable file modified")
s.processError(ErrorImmutableModified)
} else {
// If destination already exists, then we must move it into --backup-dir if required
if pair.dst != nil && s.backupDir != nil {
remoteWithSuffix := pair.dst.Remote() + s.suffix
overwritten, _ := s.backupDir.NewObject(remoteWithSuffix)
err := Move(s.backupDir, overwritten, remoteWithSuffix, pair.dst)
if err != nil {
s.processError(err)
} else {
// If successful zero out the dst as it is no longer there and copy the file
pair.dst = nil
out <- pair
}
} else {
// If successful zero out the dst as it is no longer there and copy the file
pair.dst = nil
out <- pair
}
} else {
out <- pair
}
} else {
// If moving need to delete the files we don't need to copy
+33
View File
@@ -996,3 +996,36 @@ func TestSyncUTFNorm(t *testing.T) {
file1.Path = file2.Path
fstest.CheckItems(t, r.fremote, file1)
}
// Test --immutable
func TestSyncImmutable(t *testing.T) {
r := NewRun(t)
defer r.Finalise()
fs.Config.Immutable = true
defer func() { fs.Config.Immutable = false }()
// Create file on source
file1 := r.WriteFile("existing", "potato", t1)
fstest.CheckItems(t, r.flocal, file1)
fstest.CheckItems(t, r.fremote)
// Should succeed
fs.Stats.ResetCounters()
err := fs.Sync(r.fremote, r.flocal)
require.NoError(t, err)
fstest.CheckItems(t, r.flocal, file1)
fstest.CheckItems(t, r.fremote, file1)
// Modify file data and timestamp on source
file2 := r.WriteFile("existing", "tomato", t2)
fstest.CheckItems(t, r.flocal, file2)
fstest.CheckItems(t, r.fremote, file1)
// Should fail with ErrorImmutableModified and not modify local or remote files
fs.Stats.ResetCounters()
err = fs.Sync(r.fremote, r.flocal)
assert.EqualError(t, err, fs.ErrorImmutableModified.Error())
fstest.CheckItems(t, r.flocal, file2)
fstest.CheckItems(t, r.fremote, file1)
}