From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.org!not-for-mail From: Jim Meyering Newsgroups: gmane.emacs.devel Subject: Re: save-buffer: avoid data loss on interrupt Date: Thu, 15 Dec 2011 13:58:40 +0100 Message-ID: <87liqekn3z.fsf@rho.meyering.net> References: <87zkf282ht.fsf@rho.meyering.net> <87pqfsqtsj.fsf@rho.meyering.net> NNTP-Posting-Host: lo.gmane.org Mime-Version: 1.0 Content-Type: text/plain X-Trace: dough.gmane.org 1323953940 14482 80.91.229.12 (15 Dec 2011 12:59:00 GMT) X-Complaints-To: usenet@dough.gmane.org NNTP-Posting-Date: Thu, 15 Dec 2011 12:59:00 +0000 (UTC) To: Emacs development discussions Original-X-From: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Thu Dec 15 13:58:56 2011 Return-path: Envelope-to: ged-emacs-devel@m.gmane.org Original-Received: from lists.gnu.org ([140.186.70.17]) by lo.gmane.org with esmtp (Exim 4.69) (envelope-from ) id 1RbAti-0003MG-Uy for ged-emacs-devel@m.gmane.org; Thu, 15 Dec 2011 13:58:55 +0100 Original-Received: from localhost ([::1]:42584 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1RbAti-0002Aa-Du for ged-emacs-devel@m.gmane.org; Thu, 15 Dec 2011 07:58:54 -0500 Original-Received: from eggs.gnu.org ([140.186.70.92]:55823) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1RbAtc-00029k-HB for emacs-devel@gnu.org; Thu, 15 Dec 2011 07:58:52 -0500 Original-Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1RbAtW-0005zL-9t for emacs-devel@gnu.org; Thu, 15 Dec 2011 07:58:48 -0500 Original-Received: from mx.meyering.net ([88.168.87.75]:35143) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1RbAtV-0005zG-SP for emacs-devel@gnu.org; Thu, 15 Dec 2011 07:58:42 -0500 Original-Received: from rho.meyering.net (localhost.localdomain [127.0.0.1]) by rho.meyering.net (Acme Bit-Twister) with ESMTP id D22F6600AE for ; Thu, 15 Dec 2011 13:58:40 +0100 (CET) In-Reply-To: <87pqfsqtsj.fsf@rho.meyering.net> (Jim Meyering's message of "Tue, 13 Dec 2011 18:13:16 +0100") Original-Lines: 98 X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 88.168.87.75 X-BeenThere: emacs-devel@gnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: "Emacs development discussions." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Original-Sender: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Xref: news.gmane.org gmane.emacs.devel:146724 Archived-At: Jim Meyering wrote: > Jim Meyering wrote: >> TL;DR, for a regular file with no other hard links in a writable directory, >> why is the default to rewrite in place (non-atomically), rather than to write >> to temporary-in-same-dir and then to rename, thus updating atomically? >> -------------------------------------- > ... > > Note that this change helps avoid data loss when emacs is killed > while rewriting an input file. > > So far, no one responded to the above, so here's a proof-of-concept patch. > This is a significant enough change that explanation like what's in the > commit log below belongs at least in etc/NEWS or in documentation. > This change is obviously not appropriate before the release. > > (Obviously, before pushing it, I would also make the commit log > contents into a ChangeLog entry) ... > Date: Tue, 13 Dec 2011 17:47:28 +0100 > Subject: [PATCH] make save-buffer mostly immune to interrupts: avoid data loss FYI, here's a patch that works also when the file in question does not exist. The preceding patch used file-nlinks without first ensuring that the file actually exists. >From e7f560e9dcb4c2128a33fa825c1809064e40f56f Mon Sep 17 00:00:00 2001 From: Jim Meyering Date: Thu, 15 Dec 2011 13:53:58 +0100 Subject: [PATCH] make save-buffer mostly immune to interrupts: avoid data loss When you type C-xC-s, emacs usually rewrites a file in-place. Thus, if it is interrupted, you may be left with a corrupt file. Even when emacs is not interrupted, if some process is reading that file as you write it, it can easily process an incomplete version of it, which can lead to errors. You can avoid all of that by setting the buffer-local file-precious-flag for files you care about, but it's often best not to do that for a file with multiple hard links, because when you set that flag, emacs' save mechanism writes a file by first writing a temporary file in the same directory, and then renaming that (an atomic process) to the actual file you wanted to save. Renaming that way effectively unlinks a file with multiple hard links. * files.el (basic-save-buffer-2): Save via write-temp-and-rename not just when file-precious-flag, but also when the number of hard links is 1. --- lisp/ChangeLog | 18 ++++++++++++++++++ lisp/files.el | 5 ++++- 2 files changed, 22 insertions(+), 1 deletions(-) diff --git a/lisp/ChangeLog b/lisp/ChangeLog index 74eb98c..c419cd7 100644 --- a/lisp/ChangeLog +++ b/lisp/ChangeLog @@ -7,6 +7,24 @@ (makefile-makepp-font-lock-keywords): Add new patterns. (makefile-match-function-end): Match new [...] and [[...]]. +2011-12-13 Jim Meyering + + make save-buffer mostly immune to interrupts: avoid data loss + When you type C-xC-s, emacs usually rewrites a file in-place. Thus, + if it is interrupted, you may be left with a corrupt file. Even when + emacs is not interrupted, if some process is reading that file as you + write it, it can easily process an incomplete version of it, which can + lead to errors. You can avoid all of that by setting the buffer-local + file-precious-flag for files you care about, but it's often best not to + do that for a file with multiple hard links, because when you set that + flag, emacs' save mechanism writes a file by first writing a temporary + file in the same directory, and then renaming that (an atomic process) + to the actual file you wanted to save. Renaming that way effectively + unlinks a file with multiple hard links. + * files.el (basic-save-buffer-2): Save via write-temp-and-rename + not just when file-precious-flag, but also when the number of hard + links is 1. + 2011-12-11 Juanma Barranquero * ses.el (ses-call-printer-return, ses-cell-property-get) diff --git a/lisp/files.el b/lisp/files.el index 40b6e7d..e374f69 100644 --- a/lisp/files.el +++ b/lisp/files.el @@ -4467,7 +4467,10 @@ Before and after saving the buffer, this function runs (setq setmodes (backup-buffer))) (let* ((dir (file-name-directory buffer-file-name)) (dir-writable (file-writable-p dir))) - (if (or (and file-precious-flag dir-writable) + (if (or (and dir-writable + (or file-precious-flag + (not (file-exists-p buffer-file-name)) + (= (file-nlinks buffer-file-name) 1))) (and break-hardlink-on-save (file-exists-p buffer-file-name) (> (file-nlinks buffer-file-name) 1) -- 1.7.8.163.g9859a