[pacman-dev] [PATCH 3/3] Only extract new db entries

Xavier Chantry shiningxc at gmail.com
Sun Oct 11 09:42:50 EDT 2009


This implements FS#15198. The idea apparently came from Csaba Henk
<csaba-ml <at> creo.hu> which submitted a patch to frugalware, so thanks to
him, even though I did not look at the code :)

The idea is to only extract folders for new packages into the package
database and clean up the old directories. This is essentially implementing
Xyne's "rebase" script within pacman.

If using -Syy, just remove and extract everything.

If using -Sy :
1. Generate list of directories in db
2. Generate list of directories in archive
3. Compare both
4. Clean up old directories
5. Extract new directories

Original-work-by: Allan McRae <allan at archlinux.org>
Signed-off-by: Xavier Chantry <shiningxc at gmail.com>
---
 lib/libalpm/be_files.c |  158 +++++++++++++++++++++++++++++++++++++++++++----
 lib/libalpm/util.c     |    3 +-
 2 files changed, 146 insertions(+), 15 deletions(-)

diff --git a/lib/libalpm/be_files.c b/lib/libalpm/be_files.c
index 53bbda1..fcd5ead 100644
--- a/lib/libalpm/be_files.c
+++ b/lib/libalpm/be_files.c
@@ -33,6 +33,10 @@
 #include <limits.h> /* PATH_MAX */
 #include <locale.h> /* setlocale */
 
+/* libarchive */
+#include <archive.h>
+#include <archive_entry.h>
+
 /* libalpm */
 #include "db.h"
 #include "alpm_list.h"
@@ -137,6 +141,102 @@ static int checkdbdir(pmdb_t *db)
 	return(0);
 }
 
+/* create list of directories in db */
+static alpm_list_t *dirlist_from_tar(const char *archive)
+{
+	alpm_list_t *dirlist = NULL;
+	struct archive *_archive;
+	struct archive_entry *entry;
+
+	if((_archive = archive_read_new()) == NULL)
+		RET_ERR(PM_ERR_LIBARCHIVE, NULL);
+
+	archive_read_support_compression_all(_archive);
+	archive_read_support_format_all(_archive);
+
+	if(archive_read_open_filename(_archive, archive,
+				ARCHIVE_DEFAULT_BYTES_PER_BLOCK) != ARCHIVE_OK) {
+		_alpm_log(PM_LOG_ERROR, _("could not open %s: %s\n"), archive,
+				archive_error_string(_archive));
+		RET_ERR(PM_ERR_PKG_OPEN, NULL);
+	}
+
+	while(archive_read_next_header(_archive, &entry) == ARCHIVE_OK) {
+		const struct stat *st;
+		const char *entryname; /* the name of the file in the archive */
+
+		st = archive_entry_stat(entry);
+		entryname = archive_entry_pathname(entry);
+
+		if(S_ISDIR(st->st_mode)) {
+			char *name = strdup(entryname);
+			dirlist = alpm_list_add(dirlist, name);
+		}
+	}
+	archive_read_finish(_archive);
+
+	dirlist = alpm_list_msort(dirlist, alpm_list_count(dirlist), _alpm_str_cmp);
+	return(dirlist);
+}
+
+/* create list of directories in db */
+static alpm_list_t *dirlist_from_fs(const char *syncdbpath)
+{
+	DIR *dbdir;
+	struct dirent *ent = NULL;
+	alpm_list_t *dirlist = NULL;
+	struct stat sbuf;
+	char path[PATH_MAX];
+
+	dbdir = opendir(syncdbpath);
+	if (dbdir != NULL) {
+		while((ent = readdir(dbdir)) != NULL) {
+			char *name = ent->d_name;
+			if(strcmp(name, ".") == 0 || strcmp(name, "..") == 0) {
+				continue;
+			}
+
+			/* stat the entry, make sure it's a directory */
+			snprintf(path, PATH_MAX, "%s%s", syncdbpath, name);
+			if(stat(path, &sbuf) != 0 || !S_ISDIR(sbuf.st_mode)) {
+				continue;
+			}
+
+			int len = strlen(name);
+			char *entry = malloc(len + 2);
+			strcpy(entry, name);
+			entry[len] = '/';
+			entry[len+1] = '\0';
+			dirlist = alpm_list_add(dirlist, entry);
+		}
+	}
+	closedir(dbdir);
+
+	dirlist = alpm_list_msort(dirlist, alpm_list_count(dirlist), _alpm_str_cmp);
+	return(dirlist);
+}
+
+/* remove old directories from dbdir */
+static int remove_olddir(const char *syncdbpath, alpm_list_t *dirlist)
+{
+	alpm_list_t *i;
+	for (i = dirlist; i; i = i->next) {
+		const char *name = i->data;
+		char *dbdir;
+		int len = strlen(syncdbpath) + strlen(name) + 2;
+		MALLOC(dbdir, len, RET_ERR(PM_ERR_MEMORY, -1));
+		snprintf(dbdir, len, "%s%s", syncdbpath, name);
+		_alpm_log(PM_LOG_DEBUG, "removing: %s\n", dbdir);
+		if(_alpm_rmrf(dbdir) != 0) {
+			_alpm_log(PM_LOG_ERROR, _("could not remove database directory %s\n"), dbdir);
+			free(dbdir);
+			RET_ERR(PM_ERR_DB_REMOVE, -1);
+		}
+		free(dbdir);
+	}
+	return(0);
+}
+
 /** Update a package database
  *
  * An update of the package database \a db will be attempted. Unless
@@ -229,27 +329,54 @@ int SYMEXPORT alpm_db_update(int force, pmdb_t *db)
 		return(-1);
 	} else {
 		const char *syncdbpath = _alpm_db_path(db);
-		/* remove the old dir */
-		if(_alpm_rmrf(syncdbpath) != 0) {
-			_alpm_log(PM_LOG_ERROR, _("could not remove database %s\n"), db->treename);
-			RET_ERR(PM_ERR_DB_REMOVE, -1);
-		}
-
-		/* Cache needs to be rebuilt */
-		_alpm_db_free_pkgcache(db);
 
 		/* form the path to the db location */
 		len = strlen(dbpath) + strlen(db->treename) + strlen(DBEXT) + 1;
 		MALLOC(dbfilepath, len, RET_ERR(PM_ERR_MEMORY, -1));
 		sprintf(dbfilepath, "%s%s" DBEXT, dbpath, db->treename);
 
-		/* uncompress the sync database */
-		checkdbdir(db);
-		ret = _alpm_unpack(dbfilepath, syncdbpath, NULL, 0);
-		if(ret) {
-			free(dbfilepath);
-			RET_ERR(PM_ERR_SYSTEM, -1);
+		/* remove the old dir if forcing update */
+		if(force) {
+			if(_alpm_rmrf(syncdbpath) != 0) {
+				_alpm_log(PM_LOG_ERROR, _("could not remove database %s\n"), db->treename);
+				RET_ERR(PM_ERR_DB_REMOVE, -1);
+			}
+
+			checkdbdir(db);
+
+			ret = _alpm_unpack(dbfilepath, syncdbpath, NULL, 0);
+			if(ret) {
+				free(dbfilepath);
+				RET_ERR(PM_ERR_SYSTEM, -1);
+			}
+		} else {
+			alpm_list_t *onlyold = NULL;
+			alpm_list_t *onlynew = NULL;
+			alpm_list_t *olddirlist = NULL;
+			alpm_list_t *newdirlist = NULL;
+
+			newdirlist = dirlist_from_tar(dbfilepath);
+			olddirlist = dirlist_from_fs(syncdbpath);
+
+			alpm_list_diff_sorted(olddirlist, newdirlist, _alpm_str_cmp, &onlyold, &onlynew);
+
+			ret = remove_olddir(syncdbpath, onlyold);
+			if(ret == 0) {
+				checkdbdir(db);
+				ret = _alpm_unpack(dbfilepath, syncdbpath, onlynew, 0);
+			}
+
+			alpm_list_free(olddirlist);
+			alpm_list_free(newdirlist);
+			FREELIST(onlyold);
+			FREELIST(onlynew);
+
+			if(ret) {
+				free(dbfilepath);
+				return(-1);
+			}
 		}
+
 		unlink(dbfilepath);
 		free(dbfilepath);
 
@@ -259,6 +386,9 @@ int SYMEXPORT alpm_db_update(int force, pmdb_t *db)
 					db->treename, (uintmax_t)newmtime);
 			setlastupdate(db, newmtime);
 		}
+
+		/* Cache needs to be rebuilt */
+		_alpm_db_free_pkgcache(db);
 	}
 
 	return(0);
diff --git a/lib/libalpm/util.c b/lib/libalpm/util.c
index 3777349..1f140c8 100644
--- a/lib/libalpm/util.c
+++ b/lib/libalpm/util.c
@@ -335,12 +335,13 @@ int _alpm_unpack(const char *archive, const char *prefix, alpm_list_t *list, int
 			char *found = alpm_list_find_str(list, prefix);
 			free(prefix);
 			if(!found) {
-				_alpm_log(PM_LOG_DEBUG, "skipping: %s\n", entryname);
 				if (archive_read_data_skip(_archive) != ARCHIVE_OK) {
 					ret = 1;
 					goto cleanup;
 				}
 				continue;
+			} else {
+				_alpm_log(PM_LOG_DEBUG, "extracting: %s\n", entryname);
 			}
 		}
 
-- 
1.6.4.4



More information about the pacman-dev mailing list