summaryrefslogtreecommitdiff
path: root/lib/libalpm/be_files.c
diff options
context:
space:
mode:
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);