Attempt to NOT remove packages on filesystem errors (like a read-only

filesystem). See FS#5887
This commit is contained in:
Aaron Griffin 2007-02-08 08:09:34 +00:00
parent a97bb3e279
commit a94e22d924
3 changed files with 71 additions and 27 deletions

View file

@ -450,6 +450,7 @@ enum _pmerrno_t {
PM_ERR_PKG_LOAD, PM_ERR_PKG_LOAD,
PM_ERR_PKG_INSTALLED, PM_ERR_PKG_INSTALLED,
PM_ERR_PKG_CANT_FRESH, PM_ERR_PKG_CANT_FRESH,
PM_ERR_PKG_CANT_REMOVE,
PM_ERR_PKG_INVALID_NAME, PM_ERR_PKG_INVALID_NAME,
PM_ERR_PKG_CORRUPTED, PM_ERR_PKG_CORRUPTED,
PM_ERR_PKG_REPO_NOT_FOUND, PM_ERR_PKG_REPO_NOT_FOUND,

View file

@ -97,6 +97,8 @@ char SYMEXPORT *alpm_strerror(int err)
return _("package already installed"); return _("package already installed");
case PM_ERR_PKG_CANT_FRESH: case PM_ERR_PKG_CANT_FRESH:
return _("package not installed or lesser version"); 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: case PM_ERR_PKG_INVALID_NAME:
return _("package name is not valid"); return _("package name is not valid");
case PM_ERR_PKG_CORRUPTED: case PM_ERR_PKG_CORRUPTED:

View file

@ -162,8 +162,40 @@ static int str_cmp(const void *s1, const void *s2)
return(strcmp(s1, 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 /* Helper function for iterating through a package's file and deleting them
* Used by _alpm_remove_commit * 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, static void unlink_file(pmpkg_t *info, alpm_list_t *lp, alpm_list_t *targ,
pmtrans_t *trans, int filenum, int *position) 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; struct stat buf;
int nb = 0; int nb = 0;
double percent = 0.0; double percent = 0.0;
char *file = lp->data; char file[PATH_MAX+1];
char line[PATH_MAX+1];
char *checksum = _alpm_needbackup(lp->data, info->backup); char *checksum = _alpm_needbackup(lp->data, info->backup);
ALPM_LOG_FUNC; ALPM_LOG_FUNC;
if ( *position != 0 ) { if(*position != 0) {
percent = (double)*position / filenum; percent = (double)*position / filenum;
} if ( checksum ) { }
if(checksum) {
nb = 1; nb = 1;
FREE(checksum); FREE(checksum);
} if ( !nb && trans->type == PM_TRANS_TYPE_UPGRADE ) { }
if(!nb && trans->type == PM_TRANS_TYPE_UPGRADE) {
/* check noupgrade */ /* check noupgrade */
if ( alpm_list_find_str(handle->noupgrade, file) ) { if(alpm_list_find_str(handle->noupgrade, lp->data)) {
nb = 1; nb = 1;
} }
} }
snprintf(line, PATH_MAX, "%s%s", handle->root, file); snprintf(file, PATH_MAX, "%s%s", handle->root, (char *)lp->data);
if ( lstat(line, &buf) ) { if(lstat(file, &buf)) {
_alpm_log(PM_LOG_DEBUG, _("file %s does not exist"), line); _alpm_log(PM_LOG_DEBUG, _("file %s does not exist"), file);
return; return;
} }
if ( S_ISDIR(buf.st_mode) ) { if(S_ISDIR(buf.st_mode)) {
if ( rmdir(line) ) { if(rmdir(file)) {
/* this is okay, other pakcages are probably using it (like /usr) */ /* 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 { } else {
_alpm_log(PM_LOG_DEBUG, _("removing directory %s"), line); _alpm_log(PM_LOG_DEBUG, _("removing directory %s"), file);
} }
} else { } else {
/* check the "skip list" before removing the file. /* 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. */ * explanation. */
int skipit = 0; int skipit = 0;
alpm_list_t *j; alpm_list_t *j;
for ( j = trans->skiplist; j; j = j->next ) { for(j = trans->skiplist; j; j = j->next) {
if ( !strcmp(file, (char*)j->data) ) { if(!strcmp(lp->data, (char*)j->data)) {
skipit = 1; skipit = 1;
} }
} }
if ( skipit ) { if(skipit) {
_alpm_log(PM_LOG_DEBUG, _("skipping removal of %s as it has moved to another package"), _alpm_log(PM_LOG_WARNING, _("skipping removal of %s as it has moved to another package"),
line); file);
} else { } else {
/* if the file is flagged, back it up to .pacsave */ /* if the file is flagged, back it up to .pacsave */
if ( nb ) { if(nb) {
if ( !(trans->type == PM_TRANS_TYPE_UPGRADE) ) { if(!(trans->type == PM_TRANS_TYPE_UPGRADE)) {
/* if it was an upgrade, the file would be left alone because /* if it was an upgrade, the file would be left alone because
* pacman_add() would handle it */ * pacman_add() would handle it */
if ( !(trans->type & PM_TRANS_FLAG_NOSAVE) ) { if(!(trans->type & PM_TRANS_FLAG_NOSAVE)) {
char newpath[PATH_MAX]; char newpath[PATH_MAX];
snprintf(newpath, PATH_MAX, "%s.pacsave", line); snprintf(newpath, PATH_MAX, "%s.pacsave", file);
rename(line, newpath); rename(file, newpath);
_alpm_log(PM_LOG_WARNING, _("%s saved as %s"), line, newpath); _alpm_log(PM_LOG_WARNING, _("%s saved as %s"), file, newpath);
} }
} }
} else { } 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 */ 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)); PROGRESS(trans, PM_TRANS_PROGRESS_REMOVE_START, info->name, (double)(percent * 100), list_count, (list_count - alpm_list_count(targ) + 1));
++(*position); ++(*position);
if (unlink(line) == -1) { if(unlink(file) == -1) {
_alpm_log(PM_LOG_ERROR, _("cannot remove file %s: %s"), file, strerror(errno)); _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)) { 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); int filenum = alpm_list_count(info->files);
_alpm_log(PM_LOG_DEBUG, _("removing files")); _alpm_log(PM_LOG_DEBUG, _("removing files"));