summaryrefslogtreecommitdiff
path: root/lib/libalpm/be_files.c
diff options
context:
space:
mode:
authorXavier Chantry <shiningxc@gmail.com>2009-10-10 03:11:27 +1000
committerDan McGee <dan@archlinux.org>2010-03-14 19:12:24 -0500
commit2f4ee4341d1f97d95a99147556888212d1cd4507 (patch)
tree28c58cae497b6992f99ffb8af22dcd85cc6ef9e3 /lib/libalpm/be_files.c
parent69b3a811a138a84aa09e8e81b2912f6fcd0b47b9 (diff)
downloadpacman-2f4ee4341d1f97d95a99147556888212d1cd4507.tar.xz
Only extract new DB entries
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@archlinux.org> Signed-off-by: Xavier Chantry <shiningxc@gmail.com> [Dan: fix compile error, s/int/size_t/] Signed-off-by: Dan McGee <dan@archlinux.org>
Diffstat (limited to 'lib/libalpm/be_files.c')
-rw-r--r--lib/libalpm/be_files.c166
1 files changed, 154 insertions, 12 deletions
diff --git a/lib/libalpm/be_files.c b/lib/libalpm/be_files.c
index f624d7cf..ec0dbf11 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"
@@ -67,6 +71,103 @@ static int checkdbdir(pmdb_t *db)
return(0);
}
+/* create list of directories in db */
+static int dirlist_from_tar(const char *archive, alpm_list_t **dirlist)
+{
+ struct archive *_archive;
+ struct archive_entry *entry;
+
+ if((_archive = archive_read_new()) == NULL)
+ RET_ERR(PM_ERR_LIBARCHIVE, -1);
+
+ 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, -1);
+ }
+
+ 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(0);
+}
+
+/* create list of directories in db */
+static int dirlist_from_fs(const char *syncdbpath, alpm_list_t **dirlist)
+{
+ DIR *dbdir;
+ struct dirent *ent = NULL;
+ struct stat sbuf;
+ char path[PATH_MAX];
+
+ dbdir = opendir(syncdbpath);
+ if (dbdir != NULL) {
+ while((ent = readdir(dbdir)) != NULL) {
+ char *name = ent->d_name;
+ size_t len;
+ char *entry;
+
+ 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;
+ }
+
+ len = strlen(name);
+ MALLOC(entry, len + 2, RET_ERR(PM_ERR_MEMORY, -1));
+ 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(0);
+}
+
+/* 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;
+ size_t 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
@@ -146,9 +247,17 @@ int SYMEXPORT alpm_db_update(int force, pmdb_t *db)
/* pm_errno was set by the download code */
_alpm_log(PM_LOG_DEBUG, "failed to sync db: %s\n", alpm_strerrorlast());
return(-1);
- } else {
- const char *syncdbpath = _alpm_db_path(db);
- /* remove the old dir */
+ }
+
+ const char *syncdbpath = _alpm_db_path(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);
+
+ if(force) {
+ /* if forcing update, remove the old dir and extract the db */
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);
@@ -156,23 +265,56 @@ int SYMEXPORT alpm_db_update(int force, pmdb_t *db)
_alpm_log(PM_LOG_DEBUG, "database dir %s removed\n", _alpm_db_path(db));
}
-
/* 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);
+
+ free(dbfilepath);
if(ret) {
- free(dbfilepath);
RET_ERR(PM_ERR_SYSTEM, -1);
}
- free(dbfilepath);
+ return(0);
+ }
+
+ /* if not forcing, only remove and extract what is necessary */
+ alpm_list_t *onlyold = NULL;
+ alpm_list_t *onlynew = NULL;
+ alpm_list_t *olddirlist = NULL;
+ alpm_list_t *newdirlist = NULL;
+
+ ret = dirlist_from_tar(dbfilepath, &newdirlist);
+ if(ret) {
+ goto cleanup;
+ }
+ ret = dirlist_from_fs(syncdbpath, &olddirlist);
+ if(ret) {
+ goto cleanup;
+ }
+
+ alpm_list_diff_sorted(olddirlist, newdirlist, _alpm_str_cmp, &onlyold, &onlynew);
+
+ ret = remove_olddir(syncdbpath, onlyold);
+
+ /* Cache needs to be rebuilt */
+ _alpm_db_free_pkgcache(db);
+
+ if(ret) {
+ goto cleanup;
+ }
+ checkdbdir(db);
+ ret = _alpm_unpack(dbfilepath, syncdbpath, onlynew, 0);
+
+cleanup:
+ free(dbfilepath);
+ alpm_list_free(onlyold);
+ alpm_list_free(onlynew);
+ FREELIST(olddirlist);
+ FREELIST(newdirlist);
+
+ if(ret) {
+ RET_ERR(PM_ERR_SYSTEM, -1);
}
return(0);