Parcourir la source

dpkg: Keep the diversions and statoverride database files open

The database files must be kept open during an entire dpkg run, because
we need to check if the files have changed, and rely on the inode number,
which could get eagerly reused by the filesystem if these files get
replaced multiple times in maintainer scripts.

Add a code comment explaining the rationale, so that this does not happen
again in the future.

This partially reverts commit 579b90b61b24a538cf1709d7ceda0b38fec631b3.

Closes: #731524
Guillem Jover il y a 12 ans
Parent
commit
ab56d08abe
3 fichiers modifiés avec 37 ajouts et 8 suppressions
  1. 5 1
      debian/changelog
  2. 16 3
      src/divertdb.c
  3. 16 4
      src/statdb.c

+ 5 - 1
debian/changelog

@@ -1,6 +1,10 @@
 dpkg (1.17.4) UNRELEASED; urgency=low
 dpkg (1.17.4) UNRELEASED; urgency=low
 
 
-  *
+  * Keep the diversions and statoverride database files open during dpkg
+    runs, to avoid eager inode number reuse by the filesystem if these files
+    get replaced multiple times in maintainer scripts, as we rely on the
+    inode numbers being different when checking if the databases need to
+    be reloaded. Regression introduced in 1.17.2. Closes: #731524
 
 
  -- Guillem Jover <guillem@debian.org>  Thu, 05 Dec 2013 11:08:49 +0100
  -- Guillem Jover <guillem@debian.org>  Thu, 05 Dec 2013 11:08:49 +0100
 
 

+ 16 - 3
src/divertdb.c

@@ -49,6 +49,7 @@ ensure_diversions(void)
 	static struct stat sb_prev;
 	static struct stat sb_prev;
 	struct stat sb_next;
 	struct stat sb_next;
 	char linebuf[MAXDIVERTFILENAME];
 	char linebuf[MAXDIVERTFILENAME];
+	static FILE *file_prev;
 	FILE *file;
 	FILE *file;
 	struct diversion *ov, *oicontest, *oialtname;
 	struct diversion *ov, *oicontest, *oialtname;
 
 
@@ -62,17 +63,30 @@ ensure_diversions(void)
 		if (errno != ENOENT)
 		if (errno != ENOENT)
 			ohshite(_("failed to open diversions file"));
 			ohshite(_("failed to open diversions file"));
 	} else {
 	} else {
+		setcloexec(fileno(file), diversionsname);
+
 		if (fstat(fileno(file), &sb_next))
 		if (fstat(fileno(file), &sb_next))
 			ohshite(_("failed to fstat diversions file"));
 			ohshite(_("failed to fstat diversions file"));
 
 
-		if (sb_prev.st_dev == sb_next.st_dev &&
+		/*
+		 * We need to keep the database file open so that the
+		 * filesystem cannot reuse the inode number (f.ex. during
+		 * multiple dpkg-divert invocations in a maintainer script),
+		 * otherwise the following check might turn true, and we
+		 * would skip reloading a modified database.
+		 */
+		if (file_prev &&
+		    sb_prev.st_dev == sb_next.st_dev &&
 		    sb_prev.st_ino == sb_next.st_ino) {
 		    sb_prev.st_ino == sb_next.st_ino) {
-			fclose(file);
 			onerr_abort--;
 			onerr_abort--;
 			return;
 			return;
 		}
 		}
 		sb_prev = sb_next;
 		sb_prev = sb_next;
+
+		if (file_prev)
+			fclose(file_prev);
 	}
 	}
+	file_prev = file;
 
 
 	for (ov = diversions; ov; ov = ov->next) {
 	for (ov = diversions; ov; ov = ov->next) {
 		ov->useinstead->divert->camefrom->divert = NULL;
 		ov->useinstead->divert->camefrom->divert = NULL;
@@ -112,6 +126,5 @@ ensure_diversions(void)
 		diversions = oicontest;
 		diversions = oicontest;
 	}
 	}
 
 
-	fclose(file);
 	onerr_abort--;
 	onerr_abort--;
 }
 }

+ 16 - 4
src/statdb.c

@@ -112,6 +112,7 @@ ensure_statoverrides(void)
 {
 {
 	static struct stat sb_prev;
 	static struct stat sb_prev;
 	struct stat sb_next;
 	struct stat sb_next;
+	static FILE *file_prev;
 	FILE *file;
 	FILE *file;
 	char *loaded_list, *loaded_list_end, *thisline, *nextline, *ptr;
 	char *loaded_list, *loaded_list_end, *thisline, *nextline, *ptr;
 	struct file_stat *fso;
 	struct file_stat *fso;
@@ -127,17 +128,30 @@ ensure_statoverrides(void)
 		if (errno != ENOENT)
 		if (errno != ENOENT)
 			ohshite(_("failed to open statoverride file"));
 			ohshite(_("failed to open statoverride file"));
 	} else {
 	} else {
+		setcloexec(fileno(file), statoverridename);
+
 		if (fstat(fileno(file), &sb_next))
 		if (fstat(fileno(file), &sb_next))
 			ohshite(_("failed to fstat statoverride file"));
 			ohshite(_("failed to fstat statoverride file"));
 
 
-		if (sb_prev.st_dev == sb_next.st_dev &&
+		/*
+		 * We need to keep the database file open so that the
+		 * filesystem cannot reuse the inode number (f.ex. during
+		 * multiple dpkg-statoverride invocations in a maintainer
+		 * script), otherwise the following check might turn true,
+		 * and we would skip reloading a modified database.
+		 */
+		if (file_prev &&
+		    sb_prev.st_dev == sb_next.st_dev &&
 		    sb_prev.st_ino == sb_next.st_ino) {
 		    sb_prev.st_ino == sb_next.st_ino) {
-			fclose(file);
 			onerr_abort--;
 			onerr_abort--;
 			return;
 			return;
 		}
 		}
 		sb_prev = sb_next;
 		sb_prev = sb_next;
+
+		if (file_prev)
+			fclose(file_prev);
 	}
 	}
+	file_prev = file;
 
 
 	if (!file) {
 	if (!file) {
 		onerr_abort--;
 		onerr_abort--;
@@ -147,7 +161,6 @@ ensure_statoverrides(void)
 	/* If the statoverride list is empty we don't need to bother
 	/* If the statoverride list is empty we don't need to bother
 	 * reading it. */
 	 * reading it. */
 	if (!sb_next.st_size) {
 	if (!sb_next.st_size) {
-		fclose(file);
 		onerr_abort--;
 		onerr_abort--;
 		return;
 		return;
 	}
 	}
@@ -220,6 +233,5 @@ ensure_statoverrides(void)
 		thisline = nextline;
 		thisline = nextline;
 	}
 	}
 
 
-	fclose(file);
 	onerr_abort--;
 	onerr_abort--;
 }
 }