[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

[PATCH 1/7] Split some useful functions from help.c to (new) util.c



A small number of functions from help.c are required for outside-of-dpkg
functioning of the conffiledb routines. As we want to be able to have
functional unit tests as well as to use these routines in other cmdline
programs (such as a hypothetical dpkg-conffile utility), we need to move
these functions out of help.c which is otherwise hopelessly intertwined with
the main dpkg code.

Therefore, the following functions have been moved to a new util.c file:

 * void debug(int which, const char *fmt, ...)
 * void ensure_pathname_nonexisting(const char *pathname)
 * int secure_unlink(const char *pathname)
 * int secure_unlink_statted(const char *pathname, const struct stat *stab)

Doxygen documentation has been added to the functions in the process.

Note that debug() still references an external int f_debug, which must be
provided by a cmdline program. It's left for discussion and/or future
implementation changes to fix that.

The Makefile.am automake template has also been updated to include this file
in dpkg_SOURCES. No further changes are required to dpkg source code.
---
 src/Makefile.am |    3 +-
 src/help.c      |   72 --------------------------
 src/util.c      |  154 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 156 insertions(+), 73 deletions(-)
 create mode 100644 src/util.c

diff --git a/src/Makefile.am b/src/Makefile.am
index a29b629..6a3c783 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -35,7 +35,8 @@ dpkg_SOURCES = \
 	remove.c \
 	select.c \
 	trigproc.c \
-	update.c
+	update.c \
+	util.c
 
 dpkg_LDADD = \
 	../lib/dpkg/libdpkg.a \
diff --git a/src/help.c b/src/help.c
index 669ad13..981e9e2 100644
--- a/src/help.c
+++ b/src/help.c
@@ -464,16 +464,6 @@ void clear_istobes(void) {
   iterpkgend(it);
 }
 
-void debug(int which, const char *fmt, ...) {
-  va_list ap;
-  if (!(f_debug & which)) return;
-  fprintf(stderr,"D0%05o: ",which);
-  va_start(ap,fmt);
-  vfprintf(stderr,fmt,ap);
-  va_end(ap);
-  putc('\n',stderr);
-}
-
 /*
  * Returns true if the directory contains conffiles belonging to pkg,
  * false otherwise.
@@ -538,68 +528,6 @@ void oldconffsetflags(const struct conffile *searchconff) {
   }
 }
 
-/*
- * If the pathname to remove is:
- *
- * 1. a sticky or set-id file, or
- * 2. an unknown object (i.e., not a file, link, directory, fifo or socket)
- *
- * we change its mode so that a malicious user cannot use it, even if it's
- * linked to another file.
- */
-int
-secure_unlink(const char *pathname)
-{
-  struct stat stab;
-
-  if (lstat(pathname,&stab)) return -1;
-
-  return secure_unlink_statted(pathname, &stab);
-}
-
-int
-secure_unlink_statted(const char *pathname, const struct stat *stab)
-{
-  if (S_ISREG(stab->st_mode) ? (stab->st_mode & 07000) :
-      !(S_ISLNK(stab->st_mode) || S_ISDIR(stab->st_mode) ||
-	S_ISFIFO(stab->st_mode) || S_ISSOCK(stab->st_mode))) {
-    if (chmod(pathname, 0600))
-      return -1;
-  }
-  if (unlink(pathname)) return -1;
-  return 0;
-}
-
-void ensure_pathname_nonexisting(const char *pathname) {
-  int c1;
-  const char *u;
-
-  u = path_skip_slash_dotslash(pathname);
-  assert(*u);
-
-  debug(dbg_eachfile,"ensure_pathname_nonexisting `%s'",pathname);
-  if (!rmdir(pathname)) return; /* Deleted it OK, it was a directory. */
-  if (errno == ENOENT || errno == ELOOP) return;
-  if (errno == ENOTDIR) {
-    /* Either it's a file, or one of the path components is.  If one
-     * of the path components is this will fail again ...
-     */
-    if (secure_unlink(pathname) == 0)
-      return; /* OK, it was */
-    if (errno == ENOTDIR) return;
-  }
-  if (errno != ENOTEMPTY && errno != EEXIST) { /* Huh ? */
-    ohshite(_("unable to securely remove '%.255s'"), pathname);
-  }
-  c1= m_fork();
-  if (!c1) {
-    execlp(RM, "rm", "-rf", "--", pathname, NULL);
-    ohshite(_("failed to exec rm for cleanup"));
-  }
-  debug(dbg_eachfile,"ensure_pathname_nonexisting running rm -rf");
-  subproc_wait_check(c1, "rm cleanup", 0);
-}
-
 void log_action(const char *action, struct pkginfo *pkg) {
   log_message("%s %s %s %s", action, pkg->name,
 	      versiondescribe(&pkg->installed.version, vdew_nonambig),
diff --git a/src/util.c b/src/util.c
new file mode 100644
index 0000000..8a698f1
--- /dev/null
+++ b/src/util.c
@@ -0,0 +1,154 @@
+/**
+ * @file util.c
+ *
+ * Miscellaneous utility functions.
+ *
+ * Copyright © 1995 Ian Jackson <ian@chiark.greenend.org.uk>
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2,
+ * or (at your option) any later version.
+ *
+ * This is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with dpkg; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <config.h>
+#include <compat.h>
+
+#include <assert.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "main.h"
+
+#include "dpkg/dpkg.h"
+#include "dpkg/i18n.h"
+#include "dpkg/path.h"
+#include "dpkg/subproc.h"
+
+/**
+ * General purpose and configurable debug output.
+ *
+ * @param which The logging category/severity, which should correspond to
+ *        one or more bitwise maskings of enum debugflags. This parameter
+ *        is then masked against the externally linked f_debug flag from
+ *        the main program (which in turn is set by -Dh in the dpkg(1) main
+ *        program, for example). In the case of a match the given format
+ *        string and arguments are printed to stderr.
+ * @param fmt A standard printf(1) compatible format string, followed by any
+ *        formatting variables.
+ */
+void
+debug(int which, const char *fmt, ...)
+{
+	va_list ap;
+	if (!(f_debug & which)) return;
+	fprintf(stderr,"D0%05o: ",which);
+	va_start(ap,fmt);
+	vfprintf(stderr,fmt,ap);
+	va_end(ap);
+	putc('\n',stderr);
+}
+
+/**
+ * Delete a pathname and anything that may be beneath it.
+ *
+ * This function is a generic catch-all delete utility, which will remove
+ * anything that may exist at the given pathname.  If The pathname is
+ * a nonempty directory, it will be recursively removed with a fork/exec
+ * of rm -rf.
+ *
+ * @param pathname the path to be removed.
+ */
+void
+ensure_pathname_nonexisting(const char *pathname)
+{
+	int c1;
+	const char *u;
+
+	u = path_skip_slash_dotslash(pathname);
+	assert(*u);
+
+	debug(dbg_eachfile,"ensure_pathname_nonexisting `%s'",pathname);
+	if (!rmdir(pathname)) return; /* Deleted it OK, it was a directory. */
+	if (errno == ENOENT || errno == ELOOP) return;
+	if (errno == ENOTDIR) {
+		/* Either it's a file, or one of the path components is.  If one
+		 * of the path components is this will fail again ...
+		 */
+		if (secure_unlink(pathname) == 0)
+			return; /* OK, it was */
+		if (errno == ENOTDIR) return;
+	}
+	if (errno != ENOTEMPTY && errno != EEXIST) { /* Huh ? */
+		ohshite(_("unable to securely remove '%.255s'"), pathname);
+	}
+	c1= m_fork();
+	if (!c1) {
+		execlp(RM, "rm", "-rf", "--", pathname, NULL);
+		ohshite(_("failed to exec rm for cleanup"));
+	}
+	debug(dbg_eachfile,"ensure_pathname_nonexisting running rm -rf");
+	subproc_wait_check(c1,"rm cleanup",0);
+}
+
+/**
+ * Safely unlink a given pathname, with checks on file type and mode.
+ *
+ * If the pathname to remove is:
+ *
+ * -# a sticky or set-id file, or
+ * -# an unknown object (i.e., not a file, link, directory, fifo or socket)
+ *
+ * we change its mode so that a malicious user cannot use it, even if it's
+ * linked to another file.
+ *
+ * @param pathname the path to unlink.
+ *
+ * @return 0 on success, -1 on error.
+ */
+int
+secure_unlink(const char *pathname)
+{
+	struct stat stab;
+
+	if (lstat(pathname,&stab))
+		return -1;
+
+	return secure_unlink_statted(pathname, &stab);
+}
+
+/**
+ * Safely unlink a given pathname, checking file type and mode against
+ * externally provided file type and mode information.
+ *
+ * See notes in secure_unlink.
+ *
+ * @param pathname the path to unlink.
+ * @param stab a struct stat containing information about the path.
+ *
+ * @return 0 on success, -1 on error.
+ */
+int
+secure_unlink_statted(const char *pathname, const struct stat *stab)
+{
+	if (S_ISREG(stab->st_mode) ? (stab->st_mode & 07000) :
+			!(S_ISLNK(stab->st_mode) || S_ISDIR(stab->st_mode) ||
+			  S_ISFIFO(stab->st_mode) || S_ISSOCK(stab->st_mode))) {
+		if (chmod(pathname, 0600))
+			return -1;
+	}
+	if (unlink(pathname))
+		return -1;
+	return 0;
+}
-- 
1.6.5.3


Reply to: