chiark / gitweb /
@@ -2,9 +2,13 @@
authorian <ian>
Sat, 21 Oct 2006 13:03:47 +0000 (13:03 +0000)
committerian <ian>
Sat, 21 Oct 2006 13:03:47 +0000 (13:03 +0000)
   Bugfixes:
+  * cdb: When cdbwr update writerecord fails, try to recover the
+    situation to sanity so we don't corrupt the log later; if this
+    fails, mark the cdb broken.
     Closes #393970.  (Bug exists only where int and ssize_t differ.)
+  * Do not coredump if fclose journal fails during compact.

cdb/writeable.c
debian/changelog

index 9eefb22..02c98cb 100644 (file)
@@ -163,7 +163,7 @@ typedef struct Rw {
   int ix, autocompact;
   int cdb_fd, lock_fd;
   struct cdb cdb; /* valid iff cdb_fd >= 0 */
-  FILE *logfile;
+  FILE *logfile; /* may be 0; if so, is broken */
   HashTable logincore;
   Pathbuf pbsome, pbother;
   off_t mainsz;
@@ -611,9 +611,9 @@ static int compact_core(Tcl_Interp *ip, Rw *rw, unsigned long logsz,
   a.reccount= reccount_r;
 
   r= fclose(rw->logfile);
+  rw->logfile= 0;
   if (r) { rc= cht_posixerr(ip, errno, "probable data loss!  failed to fclose"
                            " logfile during compact");  goto x_rc; }
-  rw->logfile= 0;
   
   rc= infocb(ip, rw, "compact-start", "log=%luby main=%luby",
             logsz, (unsigned long)rw->mainsz);
@@ -860,15 +860,22 @@ int cht_do_cdbwr_compact_auto(ClientData cd, Tcl_Interp *ip, void *rw_v) {
 static int update(Tcl_Interp *ip, Rw *rw, const char *key,
                  const Byte *data, int dlen) {
   HashValue *val;
+  const char *failed;
   int rc, r;
+  off_t recstart;
 
   if (strlen(key) >= KEYLEN_MAX)
     return cht_staticerr(ip, "key too long", "CDB KEYOVERFLOW");
 
   if (!rw->logfile) return cht_staticerr
-    (ip, "previous compact failed; cdbwr must be closed and reopened "
-     "before any further updates", "CDB BROKEN");
+    (ip, "failure during previous compact or error recovery;"
+     " cdbwr must be closed and reopened before any further updates",
+     "CDB BROKEN");
   
+  recstart= ftello(rw->logfile);
+  if (recstart < 0)
+    return cht_posixerr(ip, errno, "failed to ftello .jrn during update");
+
   val= htv_prep(dlen);  assert(val);
   memcpy(htv_fillptr(val), data, dlen);
 
@@ -883,6 +890,33 @@ static int update(Tcl_Interp *ip, Rw *rw, const char *key,
 
  x_rc:
   TFREE(val);
+  assert(rc);
+
+  /* Now, we have to try to sort out the journal so that it's
+   * truncated and positioned to where this abortively-written record
+   * started, with no buffered output and the error indicator clear.
+   *
+   * There seems to be no portable way to ensure the buffered unwritten
+   * output is discarded, so we close and reopen the stream.
+   */
+  fclose(rw->logfile);
+
+  rw->logfile= fopen(pathbuf_sfx(&rw->pbsome,".jrn"), "r+");
+  if (!rw->logfile) { failed= "fopen"; goto reset_fail; }
+
+  r= ftruncate(fileno(rw->logfile), recstart);
+  if (r) { failed= "ftruncate"; goto reset_fail; }
+
+  r= fseeko(rw->logfile, recstart, SEEK_SET);
+  if (r) { failed= "fseeko"; goto reset_fail; }
+
+  return rc;
+
+ reset_fail:
+  Tcl_AppendResult(ip, " (additionally, ", failed, " failed"
+                  " in error recovery: ", strerror(errno), ")", (char*)0);
+  if (rw->logfile) { fclose(rw->logfile); rw->logfile= 0; }
+
   return rc;
 }  
 
index a79232e..6c2f860 100644 (file)
@@ -2,9 +2,13 @@ chiark-tcl (1.0.2) unstable; urgency=low
 
   Bugfixes:
   * Do not adns_cancel in the middle of adns_forallqueries.
+  * cdb: When cdbwr update writerecord fails, try to recover the
+    situation to sanity so we don't corrupt the log later; if this
+    fails, mark the cdb broken.
   * strlen returns size_t, not int; fixed up everywhere relevant.
     Closes #393970.  (Bug exists only where int and ssize_t differ.)
   * Use correct errno value for error writing to new .main during compact.
+  * Do not coredump if fclose journal fails during compact.
 
   Portability fixes:
   * Remove unecessary assertion of val<=0xffffffffUL where uint32_t val;