[pacman-dev] [PATCH] New feature: files verification

Dan McGee dan at archlinux.org
Tue Jul 21 23:58:26 EDT 2009


This implements FS#13877. Add a new option "-Qk" which checks if all of the
files for a given package (or packages) are really on the system (i.e. not
accidentally deleted). This can be combined with filters and other display
options. It also respects both the --quiet and --verbose flags to give
varying levels of output.

Based on the original patch by Charly Coste <changaco at laposte.net>, thanks
for your work!

Signed-off-by: Dan McGee <dan at archlinux.org>
---

Some additional notes and example output:

dmcgee at galway ~/projects/pacman (master)
$ ./src/pacman/pacman -Qkq
wpa_supplicant /etc/wpa_supplicant.conf

dmcgee at galway ~/projects/pacman (master)
$ ./src/pacman/pacman -Qk
wpa_supplicant: missing /etc/wpa_supplicant.conf (No such file or directory)
wpa_supplicant: 22 total, 1 missing file(s)

$ ./src/pacman/pacman -Qkmv
Root      : /
Conf File : /etc/pacman.conf
DB Path   : /var/lib/pacman/
Cache Dirs: /var/cache/pacman/pkg/  /home/makepkg/packages/  
Lock File : /var/lib/pacman/db.lck
Log File  : /var/log/pacman.log
Targets   : None
bjfilter: 5 total, 0 missing file(s)
clearlooks: 51 total, 0 missing file(s)
ebtables: 37 total, 0 missing file(s)
icc: 1729 total, 0 missing file(s)
intel-compilers-common: 104 total, 0 missing file(s)
jre_beta: 825 total, 0 missing file(s)
kcachegrind: 60 total, 0 missing file(s)
libcnbj: 36 total, 0 missing file(s)
metasploit3: 16330 total, 0 missing file(s)
mixxx: 2072 total, 0 missing file(s)
munin-node: 159 total, 0 missing file(s)
picasa-beta: 1133 total, 0 missing file(s)
pstocanonbj: 24 total, 0 missing file(s)
python-markdown: 65 total, 0 missing file(s)
rng-tools: 15 total, 0 missing file(s)
tkinfo: 9 total, 0 missing file(s)
weka: 11 total, 0 missing file(s)

OK, that last one looks a bit silly with the paths at the top, doesn't it. Any
ideas? I'd be fine with showing the 0 errors lines all the time, it would just
require some grep foo for people to screen those out. That way, you can do
things like this (note that the output is slightly edited from what this patch
will produce, it is showing the output even with 0 missing files):

'''''
$ ./src/pacman/pacman -Qiik pacman-git
Name           : pacman-git
Version        : 20090715-1
URL            : http://www.archlinux.org/pacman/
Licenses       : GPL  
Groups         : None
Provides       : pacman=3.2.2  
Depends On     : gcc-libs  bash  libarchive>=2.6.0  libfetch  
                 pacman-mirrorlist  
Optional Deps  : fakeroot: for makepkg usage as normal user
                 python: for rankmirrors script usage
Required By    : pacman-contrib  pkgstats  
Conflicts With : pacman  
Replaces       : None
Installed Size : 2036.00 K
Packager       : Dan McGee <dan at archlinux.org>
Architecture   : x86_64
Build Date     : Wed 15 Jul 2009 09:15:00 PM CDT
Install Date   : Wed 15 Jul 2009 11:40:07 PM CDT
Install Reason : Explicitly installed
Install Script : No
Description    : A library-based package manager with dependency support
Backup Files:
MODIFIED	/etc/pacman.conf
MODIFIED	/etc/makepkg.conf

pacman-git: 114 total, 0 missing file(s)
'''''

-Dan

 doc/pacman.8.txt    |    4 ++
 src/pacman/conf.h   |    1 +
 src/pacman/pacman.c |    7 ++++-
 src/pacman/query.c  |   83 ++++++++++++++++++++++++++++++++++++++++++++-------
 4 files changed, 83 insertions(+), 12 deletions(-)

diff --git a/doc/pacman.8.txt b/doc/pacman.8.txt
index af85a15..ccff167 100644
--- a/doc/pacman.8.txt
+++ b/doc/pacman.8.txt
@@ -196,6 +196,10 @@ Query Options[[QO]]
 	'\--info' or '-i' flags will also display the list of backup files and
 	their modification states.
 
+*-k \--check*::
+	Check that all files owned by the given package(s) are present on the
+	system. If packages are not specified, check all installed packages.
+
 *-l, \--list*::
 	List all files owned by a given package. Multiple packages can be
 	specified on the command line.
diff --git a/src/pacman/conf.h b/src/pacman/conf.h
index 39802ca..6523d49 100644
--- a/src/pacman/conf.h
+++ b/src/pacman/conf.h
@@ -51,6 +51,7 @@ typedef struct __config_t {
 	unsigned short op_q_search;
 	unsigned short op_q_changelog;
 	unsigned short op_q_upgrade;
+	unsigned short op_q_check;
 
 	unsigned short op_s_clean;
 	unsigned short op_s_downloadonly;
diff --git a/src/pacman/pacman.c b/src/pacman/pacman.c
index 7f86489..48d45ad 100644
--- a/src/pacman/pacman.c
+++ b/src/pacman/pacman.c
@@ -108,6 +108,7 @@ static void usage(int op, const char * const myname)
 			printf(_("  -e, --explicit       list packages explicitly installed [filter]\n"));
 			printf(_("  -g, --groups         view all members of a package group\n"));
 			printf(_("  -i, --info           view package information (-ii for backup files)\n"));
+			printf(_("  -k, --check          check that the files owned by the package(s) are present\n"));
 			printf(_("  -l, --list           list the contents of the queried package\n"));
 			printf(_("  -m, --foreign        list installed packages not found in sync db(s) [filter]\n"));
 			printf(_("  -o, --owns <file>    query the package that owns <file>\n"));
@@ -345,6 +346,7 @@ static int parseargs(int argc, char *argv[])
 		{"help",       no_argument,       0, 'h'},
 		{"info",       no_argument,       0, 'i'},
 		{"dbonly",     no_argument,       0, 'k'},
+		{"check",      no_argument,       0, 'k'},
 		{"list",       no_argument,       0, 'l'},
 		{"foreign",    no_argument,       0, 'm'},
 		{"nosave",     no_argument,       0, 'n'},
@@ -473,7 +475,10 @@ static int parseargs(int argc, char *argv[])
 			case 'g': (config->group)++; break;
 			case 'h': config->help = 1; break;
 			case 'i': (config->op_q_info)++; (config->op_s_info)++; break;
-			case 'k': config->flags |= PM_TRANS_FLAG_DBONLY; break;
+			case 'k':
+				config->flags |= PM_TRANS_FLAG_DBONLY;
+				config->op_q_check = 1;
+				break;
 			case 'l': config->op_q_list = 1; break;
 			case 'm': config->op_q_foreign = 1; break;
 			case 'n': config->flags |= PM_TRANS_FLAG_NOSAVE; break;
diff --git a/src/pacman/query.c b/src/pacman/query.c
index 4997202..b70c713 100644
--- a/src/pacman/query.c
+++ b/src/pacman/query.c
@@ -309,8 +309,58 @@ static int filter(pmpkg_t *pkg)
 	return(1);
 }
 
-static void display(pmpkg_t *pkg)
+/* Loop through the packages. For each package,
+ * loop through files to check if they exist. */
+static int check(pmpkg_t *pkg)
 {
+	alpm_list_t *i;
+	const char *root;
+	int allfiles = 0, errors = 0;
+	size_t rootlen;
+	char f[PATH_MAX];
+
+	root = alpm_option_get_root();
+	rootlen = strlen(root);
+	if(rootlen + 1 > PATH_MAX) {
+		/* we are in trouble here */
+		pm_printf(PM_LOG_ERROR, _("root path too long\n"));
+		return(1);
+	}
+	strcpy(f, root);
+
+	const char *pkgname = alpm_pkg_get_name(pkg);
+	for(i = alpm_pkg_get_files(pkg); i; i = alpm_list_next(i)) {
+		struct stat st;
+		const char *path = alpm_list_getdata(i);
+
+		if(rootlen + 1 + strlen(path) > PATH_MAX) {
+			pm_printf(PM_LOG_WARNING, _("file path too long\n"));
+			continue;
+		}
+		strcpy(f + rootlen, path);
+		allfiles++;
+		/* use lstat to prevent errors from symlinks */
+		if(lstat(f, &st) != 0) {
+			if(config->quiet) {
+				fprintf(stderr, "%s %s\n", pkgname, f);
+			} else {
+				fprintf(stderr, "%s: missing %s (%s)\n", pkgname, f, strerror(errno));
+			}
+			errors++;
+		}
+	}
+
+	if((errors > 0 && !config->quiet) || config->verbose) {
+		printf("%s: %d total, %d missing file(s)\n", pkgname, allfiles, errors);
+	}
+
+	return(errors != 0 ? 1 : 0);
+}
+
+static int display(pmpkg_t *pkg)
+{
+	int ret = 0;
+
 	if(config->op_q_info) {
 		if(config->op_q_isfile) {
 			/* omit info that isn't applicable for a file package */
@@ -325,19 +375,25 @@ static void display(pmpkg_t *pkg)
 	if(config->op_q_changelog) {
 		dump_pkg_changelog(pkg);
 	}
-	if(!config->op_q_info && !config->op_q_list && !config->op_q_changelog) {
+	if(config->op_q_check) {
+		ret = check(pkg);
+	}
+	if(!config->op_q_info && !config->op_q_list
+			&& !config->op_q_changelog && !config->op_q_check) {
 		if (!config->quiet) {
 			printf("%s %s\n", alpm_pkg_get_name(pkg), alpm_pkg_get_version(pkg));
 		} else {
 			printf("%s\n", alpm_pkg_get_name(pkg));
 		}
 	}
+	return(ret);
 }
 
 int pacman_query(alpm_list_t *targets)
 {
 	int ret = 0;
 	alpm_list_t *i;
+	pmpkg_t *pkg = NULL;
 
 	/* First: operations that do not require targets */
 
@@ -358,12 +414,12 @@ int pacman_query(alpm_list_t *targets)
 		alpm_list_t *sync_dbs = alpm_option_get_syncdbs();
 		if(sync_dbs == NULL || alpm_list_count(sync_dbs) == 0) {
 			pm_printf(PM_LOG_ERROR, _("no usable package repositories configured.\n"));
-			return(-1);
+			return(1);
 		}
 	}
 
 	/* operations on all packages in the local DB
-	 * valid: no-op (plain -Q), list, info
+	 * valid: no-op (plain -Q), list, info, check
 	 * invalid: isfile, owns */
 	if(targets == NULL) {
 		if(config->op_q_isfile || config->op_q_owns) {
@@ -372,12 +428,15 @@ int pacman_query(alpm_list_t *targets)
 		}
 
 		for(i = alpm_db_get_pkgcache(db_local); i; i = alpm_list_next(i)) {
-			pmpkg_t *pkg = alpm_list_getdata(i);
+			pkg = alpm_list_getdata(i);
 			if(filter(pkg)) {
-				display(pkg);
+				int value = display(pkg);
+				if(value != 0) {
+					ret = 1;
+				}
 			}
 		}
-		return(0);
+		return(ret);
 	}
 
 	/* Second: operations that require target(s) */
@@ -389,10 +448,9 @@ int pacman_query(alpm_list_t *targets)
 	}
 
 	/* operations on named packages in the local DB
-	 * valid: no-op (plain -Q), list, info */
+	 * valid: no-op (plain -Q), list, info, check */
 	for(i = targets; i; i = alpm_list_next(i)) {
 		char *strname = alpm_list_getdata(i);
-		pmpkg_t *pkg = NULL;
 
 		if(config->op_q_isfile) {
 			alpm_pkg_load(strname, 1, &pkg);
@@ -402,12 +460,15 @@ int pacman_query(alpm_list_t *targets)
 
 		if(pkg == NULL) {
 			pm_fprintf(stderr, PM_LOG_ERROR, _("package \"%s\" not found\n"), strname);
-			ret++;
+			ret = 1;
 			continue;
 		}
 
 		if(filter(pkg)) {
-			display(pkg);
+			int value = display(pkg);
+			if(value != 0) {
+				ret = 1;
+			}
 		}
 
 		if(config->op_q_isfile) {
-- 
1.6.3.3



More information about the pacman-dev mailing list