Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Correct saving files to DropBox/Drive/OneDrive #1385

Merged
merged 4 commits into from
Jan 28, 2018
Merged

Conversation

droidmonkey
Copy link
Member

@droidmonkey droidmonkey commented Jan 14, 2018

Description

  • Added backup before save config setting

  • Added non-atomic save config setting

  • Adds prompt to fallback to non-atomic saves if saving fails 3 times:

failed_saves

  • Updated basic settings to be in logical groups:

new_settings

Fixes #197 and closes #1159

Motivation and context

The risk of data loss is minimal and better than not being able to save at all.

How has this been tested?

Manually and automatically

Types of changes

  • ✅ Breaking change (fix or feature that would cause existing functionality to change)

Checklist:

  • ✅ I have read the CONTRIBUTING document. [REQUIRED]
  • ✅ My code follows the code style of this project. [REQUIRED]
  • ✅ All new and existing tests passed. [REQUIRED]
  • ✅ I have compiled and verified my code with -DWITH_ASAN=ON. [REQUIRED]

saveFile.close(); // flush to disk

if (keepOld) {
QFile::remove(filePath + ".old");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

.old should be inserted between the base filename and the .kdbx extension.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why you make this difficult? 😝

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

😏

}

QFile::remove(filePath);
if (saveFile.rename(filePath)) {
Copy link
Member

@phoerious phoerious Jan 15, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you sure this is going to work? If the temp folder is on a different partition, then renaming a file means copying it. Given this is rare on Windows, I'd still see it as a potential problem, because corruption could occur during copying.
Another problem I see is that on platforms other than Windows, the temp location is often /tmp, which is world-readable (the file may not be, but you can still at least list it). The database is encrypted, but we still shouldn't move it to such a location without the user's consent. Maybe make the QTemporaryFile fix Windows-only and continue using QSaveFile on other platforms. Outside of Dropbox folders it's by far the safer option and this problem is Windows-only.

EDIT: ah, I see the same problem was mentioned in the linked Qt bug report.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yah I got some education from that bug thread, good to know. I did consider the option of only implementing the temp file scheme in windows.

Another issue is if a database is saved to a non-writable directory, but the database itself is writable, this implementation will actually delete the database and return.

Copy link
Member

@phoerious phoerious Jan 15, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's indeed a major problem. In that case add a safeguard to check write permissions to the folder and show an error if you can't create new files.

@droidmonkey
Copy link
Member Author

This PR is ready for final review & merge.

@droidmonkey droidmonkey force-pushed the refactor/saving branch 2 times, most recently from 7b4a883 to 5dad2da Compare January 27, 2018 23:48
Copy link
Contributor

@TheZ3ro TheZ3ro left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems good to me

Copy link
Member

@phoerious phoerious left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few more minor changes

@@ -114,6 +114,8 @@ void Config::init(const QString& fileName)
m_defaults.insert("AutoSaveAfterEveryChange", false);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we make this a default in 2.3? I don't really see a reason why this should be off by default.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes good idea

backupFilePath.replace(".kdbx", ".old.kdbx", Qt::CaseInsensitive);
if (!backupFilePath.endsWith(".old.kdbx")) {
// Fallback in case of poorly named file
backupFilePath = filePath + ".old";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could do this a lot safer with a regular expression. ;-)

QString errorMessage = db->saveToFile(filePath);
// TODO: Make this async, but lock out the database widget to prevent re-entrance
bool useAtomicSaves = config()->get("UseAtomicSaves").toBool();
QString errorMessage = db->saveToFile(filePath, useAtomicSaves, config()->get("BackupBeforeSave").toBool());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At least the UseAtomicSaves settings should have true as default value in Config::init().

Copy link
Member Author

@droidmonkey droidmonkey Jan 28, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It does, i added a default value of true here

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, forget what I said. 😕

@@ -336,6 +341,22 @@ bool DatabaseTabWidget::saveDatabase(Database* db, QString filePath)
} else {
dbStruct.modified = true;
updateTabName(db);

if (++dbStruct.saveAttempts > 2 && useAtomicSaves) {
Copy link
Member

@phoerious phoerious Jan 28, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would check for write permissions first and show a dedicated error message. Otherwise the user will disable atomic saves for nothing if the problem is actually missing write permissions.
I know this is an edge case, because without write permissions, the DB will be opened in read-only mode, but it can still occur if write permissions are somehow removed mid-air.

Copy link
Member Author

@droidmonkey droidmonkey Jan 28, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can't easily check for write conditions at the directory level on windows. I did a fair bit of research on the matter while trying to diagnose. The only edge case that the read-only open fails at is opening a writable file in a non writable directory. That doesnt seem like a common scenario.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, leave as is then.

<item>
<widget class="QCheckBox" name="useAtomicSavesCheckBox">
<property name="text">
<string>Safely save database files (may be incompatible with DropBox, etc)</string>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dropbox's official spelling is with lowercase b.

auto choice = MessageBox::question(this, tr("Disable safe saves?"),
tr("KeePassXC has failed to save the database multiple times. "
"This is likely caused by file sync services holding a lock on "
"the save file.\nDisable safe saves and try again?"),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@droidmonkey shouldn't we also use the atomic saves terminology here?

Copy link
Member

@phoerious phoerious Jan 28, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was thinking that, too. But I don't know how well users will understand it. Atomic in a non-nuclear context is a very domain-specific word.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Atomic is a developer term. I wanted to dumb it down on the user interface.

Copy link

@calmarc calmarc Jan 29, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

User (me) googled it: "write to tmp-file and then (atomic->) move over". (had no idea before searching for the term - sounded a little intimidating to me :) X Atomic save (write temporary file - on success secure replace original with it)

* Replaces QSaveFile with QTemporaryFile
* Added backup before save config setting
* This method may cause data loss (see comments)
* User is prompted to disable safe saves after three failed attempts
* Completely retooled basic settings to group settings logically
* Added setting for "atomic saves"
@phoerious phoerious merged commit bed921c into develop Jan 28, 2018
@phoerious phoerious deleted the refactor/saving branch January 28, 2018 20:19
phoerious added a commit that referenced this pull request Feb 27, 2018
- Add support for KDBX 4.0, Argon2 and ChaCha20 [#148, #1179, #1230, #1494]
- Add SSH Agent feature [#1098, #1450, #1463]
- Add preview panel with details of the selected entry [#879, #1338]
- Add more and configurable columns to entry table and allow copying of values by double click [#1305]
- Add KeePassXC-Browser API as a replacement for KeePassHTTP [#608]
- Deprecate KeePassHTTP [#1392]
- Add support for Steam one-time passwords [#1206]
- Add support for multiple Auto-Type sequences for a single entry [#1390]
- Adjust YubiKey HMAC-SHA1 challenge-response key generation for KDBX 4.0 [#1060]
- Replace qHttp with cURL for website icon downloads [#1460]
- Remove lock file [#1231]
- Add option to create backup file before saving [#1385]
- Ask to save a generated password before closing the entry password generator [#1499]
- Resolve placeholders recursively [#1078]
- Add Auto-Type button to the toolbar [#1056]
- Improve window focus handling for Auto-Type dialogs [#1204, #1490]
- Auto-Type dialog and password generator can now be exited with ESC [#1252, #1412]
- Add optional dark tray icon [#1154]
- Add new "Unsafe saving" option to work around saving problems with file sync services [#1385]
- Add IBus support to AppImage and additional image formats to Windows builds [#1534, #1537]
- Add diceware password generator to CLI [#1406]
- Add --key-file option to CLI [#816, #824]
- Add DBus interface for opening and closing KeePassXC databases [#283]
- Add KDBX compression options to database settings [#1419]
- Discourage use of old fixed-length key files in favor of arbitrary files [#1326, #1327]
- Correct reference resolution in entry fields [#1486]
- Fix window state and recent databases not being remembered on exit [#1453]
- Correct history item generation when configuring TOTP for an entry [#1446]
- Correct multiple TOTP bugs [#1414]
- Automatic saving after every change is now a default [#279]
- Allow creation of new entries during search [#1398]
- Correct menu issues on macOS [#1335]
- Allow compilation on OpenBSD [#1328]
- Improve entry attachments view [#1139, #1298]
- Fix auto lock for Gnome and Xfce [#910, #1249]
- Don't remember key files in file dialogs when the setting is disabled [#1188]
- Improve database merging and conflict resolution [#807, #1165]
- Fix macOS pasteboard issues [#1202]
- Improve startup times on some platforms [#1205]
- Hide the notes field by default [#1124]
- Toggle main window by clicking tray icon with the middle mouse button [#992]
- Fix custom icons not copied over when databases are merged [#1008]
- Allow use of DEL key to delete entries [#914]
- Correct intermittent crash due to stale history items [#1527]
- Sanitize newline characters in title, username and URL fields [#1502]
- Reopen previously opened databases in correct order [#774]
- Use system's zxcvbn library if available [#701]
- Implement various i18n improvements [#690, #875, #1436]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Auto backup database on save Saving DB fails intermittently on windows
5 participants