config: do not remove/overwrite other files during config file save - fixes #3759

This commit is contained in:
albertony 2023-04-04 14:20:40 +02:00
parent fcdffab480
commit ec8bbb8d30
2 changed files with 27 additions and 16 deletions

View file

@ -952,15 +952,9 @@ To reduce risk of corrupting an existing configuration file, rclone
will not write directly to it when saving changes. Instead it will
first write to a new, temporary, file. If a configuration file already
existed, it will (on Unix systems) try to mirror its permissions to
the new file. Then it will rename the existing file by adding suffix
".old" to its name (e.g. `rclone.conf.old`), if a file with the same
name already exists it will simply be replaced. Next, rclone will rename
the new file to the correct name, before finally cleaning up by deleting
the original file now which has now ".old" suffix to its name. Note that
one side-effect of this is that if you happen to have a file in the same
directory and with the same name as your configuration file, but with
suffix ".old", then rclone will end up deleting this file next time it
updates its configuration file!
the new file. Then it will rename the existing file to a temporary
name as backup. Next, rclone will rename the new file to the correct name,
before finally cleaning up by deleting the backup file.
### --contimeout=TIME ###

View file

@ -106,7 +106,6 @@ func (s *Storage) Save() error {
if configPath == "" {
return fmt.Errorf("failed to save config file, path is empty")
}
dir, name := filepath.Split(configPath)
err := file.MkdirAll(dir, os.ModePerm)
if err != nil {
@ -119,7 +118,7 @@ func (s *Storage) Save() error {
defer func() {
_ = f.Close()
if err := os.Remove(f.Name()); err != nil && !os.IsNotExist(err) {
fs.Errorf(nil, "failed to remove temp config file: %v", err)
fs.Errorf(nil, "Failed to remove temp file for new config: %v", err)
}
}()
@ -154,15 +153,33 @@ func (s *Storage) Save() error {
fs.Errorf(nil, "Failed to set permissions on config file: %v", err)
}
if err = os.Rename(configPath, configPath+".old"); err != nil && !os.IsNotExist(err) {
return fmt.Errorf("failed to move previous config to backup location: %w", err)
fbackup, err := os.CreateTemp(dir, name+".old")
if err != nil {
return fmt.Errorf("failed to create temp file for old config backup: %w", err)
}
err = fbackup.Close()
if err != nil {
return fmt.Errorf("failed to close temp file for old config backup: %w", err)
}
keepBackup := true
defer func() {
if !keepBackup {
if err := os.Remove(fbackup.Name()); err != nil && !os.IsNotExist(err) {
fs.Errorf(nil, "Failed to remove temp file for old config backup: %v", err)
}
}
}()
if err = os.Rename(configPath, fbackup.Name()); err != nil {
if !os.IsNotExist(err) {
return fmt.Errorf("failed to move previous config to backup location: %w", err)
}
keepBackup = false // no existing file, no need to keep backup even if writing of new file fails
}
if err = os.Rename(f.Name(), configPath); err != nil {
return fmt.Errorf("failed to move newly written config from %s to final location: %v", f.Name(), err)
}
if err := os.Remove(configPath + ".old"); err != nil && !os.IsNotExist(err) {
fs.Errorf(nil, "Failed to remove backup config file: %v", err)
}
keepBackup = false // new file was written, no need to keep backup
// Update s.fi with the newly written file
s.fi, _ = os.Stat(configPath)