Package: trn
Version: 3.6-18.1
Tags: patch

Trn's technique for rewriting a local KILL file (kfile.c::rewrite_kfile()) 
is as follows:

 * unlink old KILL file (still open)
 * open new KILL file
 * read lines from old KILL file, writing into new KILL file
 * close both files

This is unsafe, in that any crash between the first and last steps will 
lose the contents of the file.

The attached patch fixes this problem by writing the KILL file under a new 
name, and then renaming it into place (atomically if possible).  I've only 
minimally tested it so far.

[ I actually noticed this because I was running trn on a system where 
  unlinked files aren't readable at all, so all my lovely rules got eaten, 
  but I appreciate that such systems aren't relevant to Debian. ]

-- 
Ben Harris, University of Cambridge Computing Service.
--- kfile.c.orig	1994-11-19 06:01:19.000000000 +0000
+++ kfile.c	2008-10-29 22:27:40.000000000 +0000
@@ -339,6 +339,7 @@
 ART_NUM thru;
 {
     bool no_kills = 0, has_star_commands = FALSE;
+    char *oldkf, *newkf;
 
     if (localkfp) {
 	fseek(localkfp,0L,0);		/* rewind current file */
@@ -352,12 +353,16 @@
 	    no_kills = 1;
     }
     strcpy(buf,filexp(getval("KILLLOCAL",killlocal)));
+    oldkf = savestr(buf);
+    strcat(buf, ".tmp");
+    newkf = savestr(buf);
     if (!localkfp)
-	makedir(buf,MD_FILE);
-    UNLINK(buf);			/* to prevent file reuse */
-    if (no_kills)
+	makedir(newkf,MD_FILE);
+    if (no_kills) {
+        UNLINK(buf);
 	open_kfile(KF_LOCAL);		/* close file and reset open flag */
-    else if (newkfp = fopen(buf,"w")) {
+    }
+    else if (newkfp = fopen(newkf,"w")) {
 	fprintf(newkfp,"THRU %ld\n",(long)thru);
 	while (localkfp && fgets(buf,LBUFLEN,localkfp) != Nullch) {
 	    if (strnEQ(buf,"THRU",4))
@@ -381,12 +386,21 @@
 	/* Append all the still-valid thread commands */
 	hashwalk(msgid_hash, write_thread_commands, 0);
 	fclose(newkfp);
+#ifdef HAS_RENAME
+	rename(newkf, oldkf);
+#else
+	UNLINK(oldkf);
+	safelink(newkf, oldkf);
+        UNLINK(newkf);
+#endif
 	open_kfile(KF_LOCAL);		/* and reopen local file */
     }
     else
-	printf(cantcreate,buf) FLUSH;
+	printf(cantcreate,newkf) FLUSH;
     localkf_changes = 0;
     has_normal_kills = FALSE;
+    free(newkf);
+    free(oldkf);
 }
 
 /* edit KILL file for newsgroup */

Reply via email to