Radish alpha
H
rad:z3QDZAW2FAfuLvihrhiyDC9fAD8G9
HardenedBSD Package Manager
Radicle
Git
Capsicumize pkg clean
Baptiste Daroussin committed 10 years ago
commit 9677df1bb37ee97a66b363f03ac37acee210b455
parent 8ee1152
1 file changed +168 -82
modified src/clean.c
@@ -2,6 +2,7 @@
 * Copyright (c) 2011-2012 Julien Laffaye <jlaffaye@FreeBSD.org>
 * Copyright (c) 2013-2014 Matthew Seaman <matthew@FreeBSD.org>
 * Copyright (c) 2014 Vsevolod Stakhov <vsevolod@FreeBSD.org>
+
 * Copyright (c) 2016 Baptiste Daroussin <bapt@FreeBSD.org>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
@@ -34,9 +35,12 @@
/* For MIN */
#include <sys/param.h>

+
#ifdef HAVE_CAPSICUM
+
#include <sys/capability.h>
+
#endif
+

#include <assert.h>
#include <err.h>
-
#include <fts.h>
#include <getopt.h>
#ifdef HAVE_LIBUTIL_H
#include <libutil.h>
@@ -48,6 +52,9 @@
#include <unistd.h>
#include <khash.h>
#include <kvec.h>
+
#include <fcntl.h>
+
#include <dirent.h>
+
#include <errno.h>

#include <bsd_compat.h>

@@ -62,11 +69,13 @@ typedef kvec_t(char *) dl_list;
#define SIZE_MISMATCH	(1U<<3)
#define ALL		(1U<<4)

-
static int
-
add_to_dellist(dl_list *dl,  const char *path)
+
static size_t
+
add_to_dellist(int fd, dl_list *dl,  const char *path)
{
	static bool first_entry = true;
+
	struct stat st;
	char *store_path;
+
	size_t sz = 0;

	assert(path != NULL);

@@ -81,9 +90,11 @@ add_to_dellist(dl_list *dl, const char *path)
		printf("\t%s\n", store_path);
	}

+
	if (fstatat(fd, path, &st, AT_SYMLINK_NOFOLLOW) != -1 && S_ISREG(st.st_mode))
+
		sz = st.st_size;
	kv_push(char *, *dl, store_path);

-
	return (EPKG_OK);
+
	return (sz);
}

static void
@@ -97,17 +108,29 @@ free_dellist(dl_list *dl)
}

static int
-
delete_dellist(dl_list *dl, int total)
+
delete_dellist(int fd, const char *cachedir,  dl_list *dl, int total)
{
+
	struct stat st;
	int retcode = EX_OK;
+
	int flag = 0;
	unsigned int count = 0, processed = 0;
-
	char *file;
+
	char *file, *relpath;

	count = kv_size(*dl);
	progressbar_start("Deleting files");
-
	while (kv_size(*dl) > 0) {
-
		file = kv_pop(*dl);
-
		if (unlink(file) != 0) {
+
	for (int i = 0; i < kv_size(*dl); i++) {
+
		flag = 0;
+
		relpath = file = kv_A(*dl, i);
+
		relpath += strlen(cachedir) + 1;
+
		if (fstatat(fd, relpath, &st, AT_SYMLINK_NOFOLLOW) == -1) {
+
			++processed;
+
			progressbar_tick(processed, total);
+
			warn("can't stat %s", file);
+
			continue;
+
		}
+
		if (S_ISDIR(st.st_mode))
+
			flag = AT_REMOVEDIR;
+
		if (unlinkat(fd, relpath, flag) == -1) {
			warn("unlink(%s)", file);
			retcode = EX_SOFTWARE;
		}
@@ -127,6 +150,32 @@ delete_dellist(dl_list *dl, int total)
	return (retcode);
}

+
static kh_sum_t *
+
populate_sums(struct pkgdb *db)
+
{
+
	struct pkg *p = NULL;
+
	struct pkgdb_it *it = NULL;
+
	const char *sum;
+
	char *cksum;
+
	size_t slen;
+
	kh_sum_t *suml = NULL;
+
	khint_t k;
+
	int ret;
+

+
	suml = kh_init_sum();
+
	it = pkgdb_repo_search(db, "*", MATCH_GLOB, FIELD_NAME, FIELD_NONE, NULL);
+
	while (pkgdb_it_next(it, &p, PKG_LOAD_BASIC) == EPKG_OK) {
+
		pkg_get(p, PKG_CKSUM, &sum);
+
		slen = MIN(strlen(sum), PKG_FILE_CKSUM_CHARS);
+
		cksum = strndup(sum, slen);
+
		k = kh_put_sum(suml, cksum, &ret);
+
		if (ret != 0)
+
			kh_value(suml, k) = cksum;
+
	}
+

+
	return (suml);
+
}
+

/*
 * Extract hash from filename in format <name>-<version>-<hash>.txz
 */
@@ -152,6 +201,81 @@ extract_filename_sum(const char *fname, char sum[])
	return (true);
}

+
static int
+
recursive_analysis(int fd, struct pkgdb *db, const char *dir, dl_list *dl,
+
    kh_sum_t **sumlist, bool all, size_t *total)
+
{
+
	DIR *d;
+
	struct dirent *ent;
+
	int newfd;
+
	char path[MAXPATHLEN], csum[PKG_FILE_CKSUM_CHARS + 1],
+
		link_buf[MAXPATHLEN];
+
	const char *name;
+
	ssize_t link_len;
+
	size_t nbfiles = 0, added = 0;
+
	khint_t k;
+

+
	d = fdopendir(dup(fd));
+
	if (d == NULL) {
+
		warnx("Impossible to open the directory %s", dir);
+
		return (0);
+
	}
+

+
	while ((ent = readdir(d)) != NULL) {
+
		if (strcmp(ent->d_name, ".") == 0 ||
+
		    strcmp(ent->d_name, "..") == 0)
+
			continue;
+
		snprintf(path, sizeof(path), "%s/%s", dir, ent->d_name);
+
		if (ent->d_type == DT_DIR) {
+
			nbfiles++;
+
			newfd = openat(fd, ent->d_name, O_DIRECTORY, 0);
+
			if (newfd == -1) {
+
				warnx("Impossible to open the directory %s",
+
				    path);
+
				continue;
+
			}
+
			if (recursive_analysis(newfd, db, path, dl, sumlist,
+
			    all, total) == 0 || all) {
+
				add_to_dellist(fd, dl, path);
+
				added++;
+
			}
+
			close(newfd);
+
			continue;
+
		}
+
		if (ent->d_type != DT_LNK && ent->d_type != DT_REG)
+
			continue;
+
		nbfiles++;
+
		if (all) {
+
			*total += add_to_dellist(fd, dl, path);
+
			continue;
+
		}
+
		if (*sumlist == NULL) {
+
			*sumlist = populate_sums(db);
+
		}
+
		name = ent->d_name;
+
		if (ent->d_type == DT_LNK) {
+
			/* Dereference the symlink and check it for being
+
			 * recognized checksum file, or delete the symlink
+
			 * later. */
+
			if ((link_len = readlinkat(fd, ent->d_name, link_buf,
+
			    sizeof(link_buf))) == -1)
+
				continue;
+
			link_buf[link_len - 1] = '\0';
+
			name = link_buf;
+
		}
+

+
		k = kh_end(*sumlist);
+
		if (extract_filename_sum(name, csum))
+
			k = kh_get_sum(*sumlist, csum);
+
		if (k == kh_end(*sumlist)) {
+
			added++;
+
			*total += add_to_dellist(fd, dl, path);
+
		}
+
	}
+
	closedir(d);
+
	return (nbfiles - added);
+
}
+

void
usage_clean(void)
{
@@ -163,24 +287,20 @@ int
exec_clean(int argc, char **argv)
{
	struct pkgdb	*db = NULL;
-
	struct pkgdb_it	*it = NULL;
-
	struct pkg	*p = NULL;
	kh_sum_t	*sumlist = NULL;
-
	FTS		*fts = NULL;
-
	FTSENT		*ent = NULL;
	dl_list		 dl;
-
	const char	*cachedir, *sum, *name;
-
	char		*paths[2], csum[PKG_FILE_CKSUM_CHARS + 1],
-
			link_buf[MAXPATHLEN];
+
	const char	*cachedir;
	bool		 all = false;
-
	int		 retcode, ret;
-
	int		 ch, cnt = 0;
-
	size_t		 total = 0, slen;
-
	ssize_t		 link_len;
+
	int		 retcode;
+
	int		 ch;
+
	int		 cachefd = -1;
+
	size_t		 total = 0;
	char		 size[8];
	char		*cksum;
-
	khint_t		k;
	struct pkg_manifest_key *keys = NULL;
+
#ifdef HAVE_CAPSICUM
+
	cap_rights_t rights;
+
#endif

	struct option longopts[] = {
		{ "all",	no_argument,	NULL,	'a' },
@@ -213,9 +333,11 @@ exec_clean(int argc, char **argv)
	argv += optind;

	cachedir = pkg_object_string(pkg_config_get("PKG_CACHEDIR"));
-

-
	paths[0] = __DECONST(char*, cachedir);
-
	paths[1] = NULL;
+
	cachefd = open(cachedir, O_DIRECTORY);
+
	if (cachefd == -1) {
+
		warn("Impossible to open %s", cachedir);
+
		return (EX_IOERR);
+
	}

	retcode = pkgdb_access(PKGDB_MODE_READ, PKGDB_DB_REPO);

@@ -237,69 +359,33 @@ exec_clean(int argc, char **argv)

	if (pkgdb_obtain_lock(db, PKGDB_LOCK_READONLY) != EPKG_OK) {
		pkgdb_close(db);
-
		warnx("Cannot get a read lock on a database, it is locked by another process");
+
		warnx("Cannot get a read lock on a database, it is locked by "
+
		    "another process");
		return (EX_TEMPFAIL);
	}

-
	kv_init(dl);
-
	if ((fts = fts_open(paths, FTS_PHYSICAL, NULL)) == NULL) {
-
		warn("fts_open(%s)", cachedir);
-
		goto cleanup;
-
	}
-

-
	/* Build the list of out-of-date or obsolete packages */
-

-
	pkg_manifest_keys_new(&keys);
-
	while ((ent = fts_read(fts)) != NULL) {
-
		if (ent->fts_info != FTS_F && ent->fts_info != FTS_SL)
-
			continue;
-

-
		if (all) {
-
			retcode = add_to_dellist(&dl, ent->fts_path);
-
			if (retcode == EPKG_OK) {
-
				total += ent->fts_statp->st_size;
-
				++cnt;
-
			}
-
			continue;
+
#ifdef HAVE_CAPSICUM
+
		cap_rights_init(&rights, CAP_READ, CAP_LOOKUP, CAP_FSTATFS,
+
		    CAP_FSTAT, CAP_UNLINKAT);
+
		if (cap_rights_limit(cachefd, &rights) < 0 && errno != ENOSYS ) {
+
			warn("cap_rights_limit() failed");
+
			close(cachefd);
+
			return (EX_SOFTWARE);
		}

-
		if (sumlist == NULL) {
-
			sumlist = kh_init_sum();
-
			it = pkgdb_repo_search(db, "*", MATCH_GLOB, FIELD_NAME, FIELD_NONE, NULL);
-
			while (pkgdb_it_next(it, &p, PKG_LOAD_BASIC) == EPKG_OK) {
-
				pkg_get(p, PKG_CKSUM, &sum);
-
				slen = MIN(strlen(sum), PKG_FILE_CKSUM_CHARS);
-
				cksum = strndup(sum, slen);
-
				k = kh_put_sum(sumlist, cksum, &ret);
-
				if (ret != 0)
-
					kh_value(sumlist, k) = cksum;
-
			}
+
		if (cap_enter() < 0 && errno != ENOSYS) {
+
			warn("cap_enter() failed");
+
			close(cachefd);
+
			return (EX_SOFTWARE);
		}
+
#endif

-
		if (ent->fts_info == FTS_SL) {
-
			/* Dereference the symlink and check it for being
-
			 * recognized checksum file, or delete the symlink
-
			 * later. */
-
			if ((link_len = readlink(ent->fts_name, link_buf,
-
			    sizeof(link_buf))) == -1)
-
				continue;
-
			link_buf[link_len - 1] = '\0';
-
			name = link_buf;
-
		} else
-
			name = ent->fts_name;
+
	kv_init(dl);

-
		k = kh_end(sumlist);
-
		if (extract_filename_sum(name, csum))
-
			k = kh_get_sum(sumlist, csum);
-
		if (k == kh_end(sumlist)) {
-
			retcode = add_to_dellist(&dl, ent->fts_path);
-
			if (retcode == EPKG_OK) {
-
				total += ent->fts_statp->st_size;
-
				++cnt;
-
			}
-
			continue;
-
		}
-
	}
+
	/* Build the list of out-of-date or obsolete packages */
+

+
	pkg_manifest_keys_new(&keys);
+
	recursive_analysis(cachefd, db, cachedir, &dl, &sumlist, all, &total);
	if (sumlist != NULL) {
		kh_foreach_value(sumlist, cksum, free(cksum));
		kh_destroy_sum(sumlist);
@@ -320,7 +406,7 @@ exec_clean(int argc, char **argv)
	if (!dry_run) {
			if (query_yesno(false,
			  "\nProceed with cleaning the cache? ")) {
-
				retcode = delete_dellist(&dl, cnt);
+
				retcode = delete_dellist(cachefd, cachedir, &dl, kv_size(dl));
			}
	} else {
		retcode = EX_OK;
@@ -332,8 +418,8 @@ cleanup:
	pkg_manifest_keys_free(keys);
	free_dellist(&dl);

-
	if (fts != NULL)
-
		fts_close(fts);
+
	if (cachefd != -1)
+
		close(cachefd);

	return (retcode);
}