In attachment a tentative of patch (but as it's already 2 am, I don't
guarantee anything).
--- leafpad-0.8.18.1_orig/src/file.c 2010-12-23 14:54:16.000000000 +0100
+++ leafpad-0.8.18.1/src/file.c 2016-11-23 00:45:53.000000000 +0100
@@ -180,6 +180,7 @@
gint file_save_real(GtkWidget *view, FileInfo *fi)
{
FILE *fp;
+ gint fd;
GtkTextIter start, end;
gchar *str, *cstr;
gsize rbytes, wbytes;
@@ -217,21 +218,60 @@
return -1;
}
+ // opening the file in write mode (it will truncate the content!)
fp = fopen(fi->filename, "w");
if (!fp) {
run_dialog_message(gtk_widget_get_toplevel(view),
GTK_MESSAGE_ERROR, _("Can't open file to write"));
return -1;
}
- if (fwrite(cstr, 1, wbytes, fp) != wbytes) {
+
+ // write, and check all data could be flushed into the file stream
+ if ( (fwrite(cstr, 1, wbytes, fp) != wbytes) || fflush(fp) == EOF) {
run_dialog_message(gtk_widget_get_toplevel(view),
- GTK_MESSAGE_ERROR, _("Can't write file"));
+ GTK_MESSAGE_ERROR, _("Failed to write all the data into file.\n"
+ "The file is probably truncated on disk.\n"));
return -1;
+
+ // In theory to avoid such data loss, a pseudo atomic write strategy is :
+ // write the data in a temp file on the same file system,
+ // fflush, fsync, rename the temp file into the target file,
+ // and fsync the parent directory too.
+ //
+ // But that generate new questions:
+ // is it always possible to create temporary file on the same file system?
+ // is it easy to determine the current file system?
+ // does the user have rights to create a temp file? and overwrite target file?
+ // is there enough space on the file system for the temp file?
+ //
+ // see more at https://web.archive.org/web/20160420181318/http://lwn.net/Articles/457667/
}
+ g_free(cstr);
+ // Try to ensure that data land on the device
+ fd = fileno(fp);
+ if( fd == -1 ) {
+ run_dialog_message(gtk_widget_get_toplevel(view),
+ GTK_MESSAGE_ERROR, _("Can't retrieve file descriptor"));
+ // return -1; // <- Don't abort and let's try to at least close the file
+ } else if (fsync(fd) == -1) {
+ run_dialog_message(gtk_widget_get_toplevel(view),
+ GTK_MESSAGE_ERROR, _("Can't sync file on device"));
+ // return -1; // <- Don't abort and let's try to at least close the file
+ }
+
+ // as we fflushed earlier,
+ // fclose should not trigger a write of unflushed data anymore
+ // and probably not fail here
+ if (fclose(fp) == EOF) {
+ run_dialog_message(gtk_widget_get_toplevel(view),
+ GTK_MESSAGE_ERROR, _("Can't cleanly close file"));
+ return -1;
+ }
+
+ // notify the gtk layer that the data have been saved
+ // this will trigger a callback that can have side-effects if done before fclose(fp)
gtk_text_buffer_set_modified(buffer, FALSE);
- fclose(fp);
- g_free(cstr);
return 0;
}