Backup the existing LyX file before attempting to write the new one.

This avoids dataloss in case we are unable to write the new one after
all.

A more sophisticated approach, due to Georg, is in master, but it needs
more testing that it will be able to get before the release of 2.1.1.
That should be committed to 2.1.x when it is ready and this patch backed
out again.
This commit is contained in:
Richard Heck 2014-06-09 17:35:17 -04:00
parent a72e68dd09
commit e49f4124c7
3 changed files with 80 additions and 31 deletions

View File

@ -21,11 +21,11 @@ known reports seem to involve documents that contain tables, and the corrupt
file always ends with: \begin_inset Tabular. Users who work with tables a lot file always ends with: \begin_inset Tabular. Users who work with tables a lot
may therefore wish to disable auto-save. may therefore wish to disable auto-save.
To prevent dataloss, we have changed the way LyX saves files. Rather than To prevent dataloss, we have changed the way LyX saves files if the user
over-write the original file immediately, LyX now saves the new file to a has not enabled backups. LyX now renames the existing file before
temporary name and then renames the new file to the original filename only attempting to save the new one (in effect, making a temporary backup).
after it has been written successfully. Then, if the save fails, the original file can be restored.
LyX is a document processor that encourages an approach to writing based LyX is a document processor that encourages an approach to writing based
on the structure of your documents and not simply their appearance. It is on the structure of your documents and not simply their appearance. It is
released under a Free and Open Source Software license. released under a Free and Open Source Software license.

View File

@ -1276,44 +1276,90 @@ bool Buffer::save() const
// We don't need autosaves in the immediate future. (Asger) // We don't need autosaves in the immediate future. (Asger)
resetAutosaveTimers(); resetAutosaveTimers();
// none of the backup activity that follows is necessary if the
// file does not exist
if (!fileName().exists())
return writeFile(fileName());
// this will hold the name of the location to which we backup
// the existing file, if we do that.
FileName backupName; FileName backupName;
bool madeBackup = false;
// make a backup if the file already exists // make a backup if the file already exists
if (lyxrc.make_backup && fileName().exists()) { if (lyxrc.make_backup) {
backupName = FileName(absFileName() + '~'); backupName = FileName(absFileName() + '~');
if (!lyxrc.backupdir_path.empty()) { if (!lyxrc.backupdir_path.empty()) {
string const mangledName = string const mangledName =
subst(subst(backupName.absFileName(), '/', '!'), ':', '!'); subst(subst(backupName.absFileName(), '/', '!'), ':', '!');
backupName = FileName(addName(lyxrc.backupdir_path, backupName = FileName(addName(lyxrc.backupdir_path,
mangledName)); mangledName));
} }
} else {
// we make a backup anyway, to avoid over-writing the original file
// before the new one has been saved.
//
// FIXME A more sophisticated approach is in master and should
// be moved into 2.1.x once it has been properly tested.
string const savepath = fileName().onlyPath().absFileName();
string const fname = fileName().onlyFileName();
string savename;
int fnum = 0;
// Except file is symlink do not copy because of #6587. // try to find a temporary filename to which we can backup the
// Hard links have bad luck. // existing LyX file
if (fileName().isSymLink()) do {
madeBackup = fileName().copyTo(backupName); fnum += 1;
else if (fnum > 1024) {
madeBackup = fileName().moveTo(backupName); Alert::error(_("Write failure"),
bformat(_("Cannot find temporary filename for:\n %1$s.\n"
"Even %2$s exists!"),
from_utf8(fileName().absFileName()),
from_utf8(backupName.absFileName())));
return false;
}
savename = "lyxbak-" + convert<string>(fnum) + "-" + fname;
backupName.set(addName(savepath, savename));
} while (backupName.exists());
}
if (!madeBackup) { LYXERR(Debug::FILES, "Backup being made at " << backupName);
Alert::error(_("Backup failure"), // We make the backup by moving the original file unless it is
bformat(_("Cannot create backup file %1$s.\n" // a symlink, in which case we copy it because of #6587.
"Please check whether the directory exists and is writable."), // Hard links are broken: The new file we write will not be hard
from_utf8(backupName.absFileName()))); // linked as the original file was. Sorry!
//LYXERR(Debug::DEBUG, "Fs error: " << fe.what()); bool const madeBackup = fileName().isSymLink() ?
} fileName().copyTo(backupName) :
fileName().moveTo(backupName);
if (!madeBackup) {
int const ret = Alert::prompt(_("Backup failure"),
bformat(_("Cannot create backup file:\n %1$s.\n"
"Do you want to try to save the file anyway?\n"
"This will over-write the original file."),
from_utf8(backupName.absFileName())),
1, 1, _("&Overwrite"), _("&Cancel"));
if (ret != 0)
return false;
} }
if (writeFile(d->filename)) { if (writeFile(d->filename)) {
markClean(); markClean();
if (madeBackup && !lyxrc.make_backup)
backupName.removeFile();
return true; return true;
} else {
// Saving failed, so backup is not backup
if (madeBackup)
backupName.moveTo(d->filename);
return false;
} }
// else saving failed, so backup is not backup
if (madeBackup) {
if (!backupName.moveTo(d->filename)) {
Alert::error(_("Write failure"),
bformat(_("Cannot restore original file to:\n %1$s.\n"
"But it can be found at:\n %2$s."),
from_utf8(fileName().absFileName()),
from_utf8(backupName.absFileName())));
}
}
return false;
} }

View File

@ -28,10 +28,14 @@ What's new
* DOCUMENT INPUT/OUTPUT * DOCUMENT INPUT/OUTPUT
- We now flush the output stream more frequently, as a temporary measure - We now flush the output stream more frequently, as a temporary measure
to help us gather information about the crash mentioned above. to help us gather information about the crash mentioned above.
- To prevent dataloss, we have changed the way LyX saves files if the user
has not enabled backups. LyX now renames the existing file before
attempting to save the new one (in effect, making a temporary backup).
Then, if the save fails, the original file can be restored.
* TEX2LYX IMPROVEMENTS * TEX2LYX IMPROVEMENTS
@ -82,10 +86,9 @@ What's new
* LYX2LYX * LYX2LYX
We have fixed several significant issues involving conversion of 2.0 We have fixed several significant issues involving conversion of 2.0 format into
format into 2.1 format, and conversely. This mostly affects the new 2.1 format, and conversely. This mostly affects the new argument insets and, in
argument insets and, in particular, beamer documents. These are particular, beamer documents. These are detailed below.
detailed below.
- Fix conversion of beamer block titles ending with non-ERT insets to 2.1 format. - Fix conversion of beamer block titles ending with non-ERT insets to 2.1 format.