From a94e22d9245f78242527b983a285dadf118f1afc Mon Sep 17 00:00:00 2001 From: Aaron Griffin Date: Thu, 8 Feb 2007 08:09:34 +0000 Subject: Attempt to NOT remove packages on filesystem errors (like a read-only filesystem). See FS#5887 --- lib/libalpm/alpm.h | 1 + lib/libalpm/error.c | 2 ++ lib/libalpm/remove.c | 95 +++++++++++++++++++++++++++++++++++++--------------- 3 files changed, 71 insertions(+), 27 deletions(-) diff --git a/lib/libalpm/alpm.h b/lib/libalpm/alpm.h index 3522604f..a5b5e1a4 100644 --- a/lib/libalpm/alpm.h +++ b/lib/libalpm/alpm.h @@ -450,6 +450,7 @@ enum _pmerrno_t { PM_ERR_PKG_LOAD, PM_ERR_PKG_INSTALLED, PM_ERR_PKG_CANT_FRESH, + PM_ERR_PKG_CANT_REMOVE, PM_ERR_PKG_INVALID_NAME, PM_ERR_PKG_CORRUPTED, PM_ERR_PKG_REPO_NOT_FOUND, diff --git a/lib/libalpm/error.c b/lib/libalpm/error.c index 334012d1..a6d20acf 100644 --- a/lib/libalpm/error.c +++ b/lib/libalpm/error.c @@ -97,6 +97,8 @@ char SYMEXPORT *alpm_strerror(int err) return _("package already installed"); case PM_ERR_PKG_CANT_FRESH: return _("package not installed or lesser version"); + case PM_ERR_PKG_CANT_REMOVE: + return _("cannot remove all files for package"); case PM_ERR_PKG_INVALID_NAME: return _("package name is not valid"); case PM_ERR_PKG_CORRUPTED: diff --git a/lib/libalpm/remove.c b/lib/libalpm/remove.c index afe5532b..9933d4cc 100644 --- a/lib/libalpm/remove.c +++ b/lib/libalpm/remove.c @@ -162,8 +162,40 @@ static int str_cmp(const void *s1, const void *s2) return(strcmp(s1, s2)); } + +static int can_remove_file(const char *path) +{ + alpm_list_t *i; + char file[PATH_MAX+1]; + + snprintf(file, PATH_MAX, "%s%s", handle->root, path); + + for(i = handle->trans->skiplist; i; i = i->next) { + if(strcmp(file, i->data) == 0) { + /* skipping this file, return success because "removing" this + * file does nothing */ + return(1); + } + } + /* If we fail write permissions due to a read-only filesystem, abort. + * Assume all other possible failures are covered somewhere else */ + if(access(file, W_OK) == -1) { + if(access(file, F_OK) == 0) { + /* only return failure if the file ACTUALLY exists and we don't have + * permissions */ + _alpm_log(PM_LOG_WARNING, _("cannot remove file '%s': %s"), file, strerror(errno)); + return(0); + } + } + + return(1); +} + /* Helper function for iterating through a package's file and deleting them * Used by _alpm_remove_commit + * + * TODO the parameters are a bit out of control here. This function doesn't + * need to report PROGRESS, do it in the parent function. */ static void unlink_file(pmpkg_t *info, alpm_list_t *lp, alpm_list_t *targ, pmtrans_t *trans, int filenum, int *position) @@ -171,34 +203,35 @@ static void unlink_file(pmpkg_t *info, alpm_list_t *lp, alpm_list_t *targ, struct stat buf; int nb = 0; double percent = 0.0; - char *file = lp->data; - char line[PATH_MAX+1]; + char file[PATH_MAX+1]; char *checksum = _alpm_needbackup(lp->data, info->backup); ALPM_LOG_FUNC; - if ( *position != 0 ) { + if(*position != 0) { percent = (double)*position / filenum; - } if ( checksum ) { + } + if(checksum) { nb = 1; FREE(checksum); - } if ( !nb && trans->type == PM_TRANS_TYPE_UPGRADE ) { + } + if(!nb && trans->type == PM_TRANS_TYPE_UPGRADE) { /* check noupgrade */ - if ( alpm_list_find_str(handle->noupgrade, file) ) { + if(alpm_list_find_str(handle->noupgrade, lp->data)) { nb = 1; } } - snprintf(line, PATH_MAX, "%s%s", handle->root, file); - if ( lstat(line, &buf) ) { - _alpm_log(PM_LOG_DEBUG, _("file %s does not exist"), line); + snprintf(file, PATH_MAX, "%s%s", handle->root, (char *)lp->data); + if(lstat(file, &buf)) { + _alpm_log(PM_LOG_DEBUG, _("file %s does not exist"), file); return; } - if ( S_ISDIR(buf.st_mode) ) { - if ( rmdir(line) ) { + if(S_ISDIR(buf.st_mode)) { + if(rmdir(file)) { /* this is okay, other pakcages are probably using it (like /usr) */ - _alpm_log(PM_LOG_DEBUG, _("keeping directory %s"), line); + _alpm_log(PM_LOG_DEBUG, _("keeping directory %s"), file); } else { - _alpm_log(PM_LOG_DEBUG, _("removing directory %s"), line); + _alpm_log(PM_LOG_DEBUG, _("removing directory %s"), file); } } else { /* check the "skip list" before removing the file. @@ -206,35 +239,36 @@ static void unlink_file(pmpkg_t *info, alpm_list_t *lp, alpm_list_t *targ, * explanation. */ int skipit = 0; alpm_list_t *j; - for ( j = trans->skiplist; j; j = j->next ) { - if ( !strcmp(file, (char*)j->data) ) { + for(j = trans->skiplist; j; j = j->next) { + if(!strcmp(lp->data, (char*)j->data)) { skipit = 1; } } - if ( skipit ) { - _alpm_log(PM_LOG_DEBUG, _("skipping removal of %s as it has moved to another package"), - line); + if(skipit) { + _alpm_log(PM_LOG_WARNING, _("skipping removal of %s as it has moved to another package"), + file); } else { /* if the file is flagged, back it up to .pacsave */ - if ( nb ) { - if ( !(trans->type == PM_TRANS_TYPE_UPGRADE) ) { + if(nb) { + if(!(trans->type == PM_TRANS_TYPE_UPGRADE)) { /* if it was an upgrade, the file would be left alone because * pacman_add() would handle it */ - if ( !(trans->type & PM_TRANS_FLAG_NOSAVE) ) { + if(!(trans->type & PM_TRANS_FLAG_NOSAVE)) { char newpath[PATH_MAX]; - snprintf(newpath, PATH_MAX, "%s.pacsave", line); - rename(line, newpath); - _alpm_log(PM_LOG_WARNING, _("%s saved as %s"), line, newpath); + snprintf(newpath, PATH_MAX, "%s.pacsave", file); + rename(file, newpath); + _alpm_log(PM_LOG_WARNING, _("%s saved as %s"), file, newpath); } } } else { - _alpm_log(PM_LOG_DEBUG, _("unlinking %s"), line); + _alpm_log(PM_LOG_DEBUG, _("unlinking %s"), file); int list_count = alpm_list_count(trans->packages); /* this way we don't have to call alpm_list_count twice during PROGRESS */ + PROGRESS(trans, PM_TRANS_PROGRESS_REMOVE_START, info->name, (double)(percent * 100), list_count, (list_count - alpm_list_count(targ) + 1)); ++(*position); - if (unlink(line) == -1) { - _alpm_log(PM_LOG_ERROR, _("cannot remove file %s: %s"), file, strerror(errno)); + if(unlink(file) == -1) { + _alpm_log(PM_LOG_ERROR, _("cannot remove file %s: %s"), lp->data, strerror(errno)); } } } @@ -272,6 +306,13 @@ int _alpm_remove_commit(pmtrans_t *trans, pmdb_t *db) } if(!(trans->flags & PM_TRANS_FLAG_DBONLY)) { + for(lp = info->files; lp; lp = lp->next) { + if(!can_remove_file(lp->data)) { + _alpm_log(PM_LOG_DEBUG, _("not removing package '%s', can't remove all files"), info->name); + RET_ERR(PM_ERR_PKG_CANT_REMOVE, -1); + } + } + int filenum = alpm_list_count(info->files); _alpm_log(PM_LOG_DEBUG, _("removing files")); -- cgit v1.2.3-24-g4f1b