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: save-buffer: avoid data loss on interrupt Date: Tue, 13 Dec 2011 18:13:16 +0100 Message-ID: <87pqfsqtsj.fsf@rho.meyering.net> References: <87zkf282ht.fsf@rho.meyering.net> NNTP-Posting-Host: lo.gmane.org Mime-Version: 1.0 Content-Type: text/plain X-Trace: dough.gmane.org 1323796410 17962 80.91.229.12 (13 Dec 2011 17:13:30 GMT) X-Complaints-To: usenet@dough.gmane.org NNTP-Posting-Date: Tue, 13 Dec 2011 17:13:30 +0000 (UTC) To: Emacs development discussions Original-X-From: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Tue Dec 13 18:13:26 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 1RaVuw-0003xu-NX for ged-emacs-devel@m.gmane.org; Tue, 13 Dec 2011 18:13:26 +0100 Original-Received: from localhost ([::1]:56629 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1RaVuw-0001Li-1U for ged-emacs-devel@m.gmane.org; Tue, 13 Dec 2011 12:13:26 -0500 Original-Received: from eggs.gnu.org ([140.186.70.92]:38735) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1RaVuu-0001Ld-3g for emacs-devel@gnu.org; Tue, 13 Dec 2011 12:13:25 -0500 Original-Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1RaVuo-00042r-Cr for emacs-devel@gnu.org; Tue, 13 Dec 2011 12:13:24 -0500 Original-Received: from mx.meyering.net ([88.168.87.75]:56775) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1RaVuo-00042g-4V for emacs-devel@gnu.org; Tue, 13 Dec 2011 12:13:18 -0500 Original-Received: from rho.meyering.net (localhost.localdomain [127.0.0.1]) by rho.meyering.net (Acme Bit-Twister) with ESMTP id 7C92E6007E for ; Tue, 13 Dec 2011 18:13:16 +0100 (CET) In-Reply-To: <87zkf282ht.fsf@rho.meyering.net> (Jim Meyering's message of "Fri, 09 Dec 2011 11:26:38 +0100") Original-Lines: 58 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:146691 Archived-At: 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) >From 3fc1719d0365707f25f512a87aa1c84e1b4fece5 Mon Sep 17 00:00:00 2001 From: Jim Meyering Date: Tue, 13 Dec 2011 17:47:28 +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/files.el | 4 +++- 1 files changed, 3 insertions(+), 1 deletions(-) diff --git a/lisp/files.el b/lisp/files.el index 40b6e7d..535715c 100644 --- a/lisp/files.el +++ b/lisp/files.el @@ -4467,7 +4467,9 @@ 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 + (= (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