diff options
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/libalpm/be_files.c | 166 | ||||
| -rw-r--r-- | lib/libalpm/util.c | 3 | 
2 files changed, 156 insertions, 13 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); diff --git a/lib/libalpm/util.c b/lib/libalpm/util.c index 27e861e4..d290e71c 100644 --- a/lib/libalpm/util.c +++ b/lib/libalpm/util.c @@ -333,12 +333,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);  			}  		} | 
