libalpm
Arch Linux Package Manager Library
|
00001 /* 00002 * remove.c 00003 * 00004 * Copyright (c) 2006-2011 Pacman Development Team <pacman-dev@archlinux.org> 00005 * Copyright (c) 2002-2006 by Judd Vinet <jvinet@zeroflux.org> 00006 * Copyright (c) 2005 by Aurelien Foret <orelien@chez.com> 00007 * Copyright (c) 2005 by Christian Hamar <krics@linuxforum.hu> 00008 * Copyright (c) 2006 by David Kimpe <dnaku@frugalware.org> 00009 * Copyright (c) 2005, 2006 by Miklos Vajna <vmiklos@frugalware.org> 00010 * 00011 * This program is free software; you can redistribute it and/or modify 00012 * it under the terms of the GNU General Public License as published by 00013 * the Free Software Foundation; either version 2 of the License, or 00014 * (at your option) any later version. 00015 * 00016 * This program is distributed in the hope that it will be useful, 00017 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00018 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00019 * GNU General Public License for more details. 00020 * 00021 * You should have received a copy of the GNU General Public License 00022 * along with this program. If not, see <http://www.gnu.org/licenses/>. 00023 */ 00024 00025 #include <stdlib.h> 00026 #include <errno.h> 00027 #include <string.h> 00028 #include <limits.h> 00029 #include <unistd.h> 00030 #include <sys/stat.h> 00031 00032 /* libalpm */ 00033 #include "remove.h" 00034 #include "alpm_list.h" 00035 #include "alpm.h" 00036 #include "trans.h" 00037 #include "util.h" 00038 #include "log.h" 00039 #include "backup.h" 00040 #include "package.h" 00041 #include "db.h" 00042 #include "deps.h" 00043 #include "handle.h" 00044 #include "conflict.h" 00045 00046 int SYMEXPORT alpm_remove_pkg(alpm_handle_t *handle, alpm_pkg_t *pkg) 00047 { 00048 const char *pkgname; 00049 alpm_trans_t *trans; 00050 alpm_pkg_t *copy; 00051 00052 /* Sanity checks */ 00053 CHECK_HANDLE(handle, return -1); 00054 ASSERT(pkg != NULL, RET_ERR(handle, ALPM_ERR_WRONG_ARGS, -1)); 00055 ASSERT(handle == pkg->handle, RET_ERR(handle, ALPM_ERR_WRONG_ARGS, -1)); 00056 trans = handle->trans; 00057 ASSERT(trans != NULL, RET_ERR(handle, ALPM_ERR_TRANS_NULL, -1)); 00058 ASSERT(trans->state == STATE_INITIALIZED, 00059 RET_ERR(handle, ALPM_ERR_TRANS_NOT_INITIALIZED, -1)); 00060 00061 pkgname = pkg->name; 00062 00063 if(_alpm_pkg_find(trans->remove, pkgname)) { 00064 RET_ERR(handle, ALPM_ERR_TRANS_DUP_TARGET, -1); 00065 } 00066 00067 _alpm_log(handle, ALPM_LOG_DEBUG, "adding package %s to the transaction remove list\n", 00068 pkgname); 00069 if(_alpm_pkg_dup(pkg, ©) == -1) { 00070 return -1; 00071 } 00072 trans->remove = alpm_list_add(trans->remove, copy); 00073 return 0; 00074 } 00075 00076 static int remove_prepare_cascade(alpm_handle_t *handle, alpm_list_t *lp) 00077 { 00078 alpm_trans_t *trans = handle->trans; 00079 00080 while(lp) { 00081 alpm_list_t *i; 00082 for(i = lp; i; i = i->next) { 00083 alpm_depmissing_t *miss = i->data; 00084 alpm_pkg_t *info = _alpm_db_get_pkgfromcache(handle->db_local, miss->target); 00085 if(info) { 00086 alpm_pkg_t *copy; 00087 if(!_alpm_pkg_find(trans->remove, info->name)) { 00088 _alpm_log(handle, ALPM_LOG_DEBUG, "pulling %s in target list\n", 00089 info->name); 00090 if(_alpm_pkg_dup(info, ©) == -1) { 00091 return -1; 00092 } 00093 trans->remove = alpm_list_add(trans->remove, copy); 00094 } 00095 } else { 00096 _alpm_log(handle, ALPM_LOG_ERROR, 00097 _("could not find %s in database -- skipping\n"), miss->target); 00098 } 00099 } 00100 alpm_list_free_inner(lp, (alpm_list_fn_free)_alpm_depmiss_free); 00101 alpm_list_free(lp); 00102 lp = alpm_checkdeps(handle, _alpm_db_get_pkgcache(handle->db_local), 00103 trans->remove, NULL, 1); 00104 } 00105 return 0; 00106 } 00107 00108 static void remove_prepare_keep_needed(alpm_handle_t *handle, alpm_list_t *lp) 00109 { 00110 alpm_trans_t *trans = handle->trans; 00111 00112 /* Remove needed packages (which break dependencies) from target list */ 00113 while(lp != NULL) { 00114 alpm_list_t *i; 00115 for(i = lp; i; i = i->next) { 00116 alpm_depmissing_t *miss = i->data; 00117 void *vpkg; 00118 alpm_pkg_t *pkg = _alpm_pkg_find(trans->remove, miss->causingpkg); 00119 if(pkg == NULL) { 00120 continue; 00121 } 00122 trans->remove = alpm_list_remove(trans->remove, pkg, _alpm_pkg_cmp, 00123 &vpkg); 00124 pkg = vpkg; 00125 if(pkg) { 00126 _alpm_log(handle, ALPM_LOG_WARNING, _("removing %s from target list\n"), 00127 pkg->name); 00128 _alpm_pkg_free(pkg); 00129 } 00130 } 00131 alpm_list_free_inner(lp, (alpm_list_fn_free)_alpm_depmiss_free); 00132 alpm_list_free(lp); 00133 lp = alpm_checkdeps(handle, _alpm_db_get_pkgcache(handle->db_local), 00134 trans->remove, NULL, 1); 00135 } 00136 } 00137 00138 /** Transaction preparation for remove actions. 00139 * This functions takes a pointer to a alpm_list_t which will be 00140 * filled with a list of alpm_depmissing_t* objects representing 00141 * the packages blocking the transaction. 00142 * @param handle the context handle 00143 * @param data a pointer to an alpm_list_t* to fill 00144 * @return 0 on success, -1 on error 00145 */ 00146 int _alpm_remove_prepare(alpm_handle_t *handle, alpm_list_t **data) 00147 { 00148 alpm_list_t *lp; 00149 alpm_trans_t *trans = handle->trans; 00150 alpm_db_t *db = handle->db_local; 00151 00152 if((trans->flags & ALPM_TRANS_FLAG_RECURSE) 00153 && !(trans->flags & ALPM_TRANS_FLAG_CASCADE)) { 00154 _alpm_log(handle, ALPM_LOG_DEBUG, "finding removable dependencies\n"); 00155 if(_alpm_recursedeps(db, trans->remove, 00156 trans->flags & ALPM_TRANS_FLAG_RECURSEALL)) { 00157 return -1; 00158 } 00159 } 00160 00161 if(!(trans->flags & ALPM_TRANS_FLAG_NODEPS)) { 00162 EVENT(handle, ALPM_EVENT_CHECKDEPS_START, NULL, NULL); 00163 00164 _alpm_log(handle, ALPM_LOG_DEBUG, "looking for unsatisfied dependencies\n"); 00165 lp = alpm_checkdeps(handle, _alpm_db_get_pkgcache(db), trans->remove, NULL, 1); 00166 if(lp != NULL) { 00167 00168 if(trans->flags & ALPM_TRANS_FLAG_CASCADE) { 00169 if(remove_prepare_cascade(handle, lp)) { 00170 return -1; 00171 } 00172 } else if(trans->flags & ALPM_TRANS_FLAG_UNNEEDED) { 00173 /* Remove needed packages (which would break dependencies) 00174 * from target list */ 00175 remove_prepare_keep_needed(handle, lp); 00176 } else { 00177 if(data) { 00178 *data = lp; 00179 } else { 00180 alpm_list_free_inner(lp, (alpm_list_fn_free)_alpm_depmiss_free); 00181 alpm_list_free(lp); 00182 } 00183 RET_ERR(handle, ALPM_ERR_UNSATISFIED_DEPS, -1); 00184 } 00185 } 00186 } 00187 00188 /* re-order w.r.t. dependencies */ 00189 _alpm_log(handle, ALPM_LOG_DEBUG, "sorting by dependencies\n"); 00190 lp = _alpm_sortbydeps(handle, trans->remove, 1); 00191 /* free the old alltargs */ 00192 alpm_list_free(trans->remove); 00193 trans->remove = lp; 00194 00195 /* -Rcs == -Rc then -Rs */ 00196 if((trans->flags & ALPM_TRANS_FLAG_CASCADE) 00197 && (trans->flags & ALPM_TRANS_FLAG_RECURSE)) { 00198 _alpm_log(handle, ALPM_LOG_DEBUG, "finding removable dependencies\n"); 00199 if(_alpm_recursedeps(db, trans->remove, 00200 trans->flags & ALPM_TRANS_FLAG_RECURSEALL)) { 00201 return -1; 00202 } 00203 } 00204 00205 if(!(trans->flags & ALPM_TRANS_FLAG_NODEPS)) { 00206 EVENT(handle, ALPM_EVENT_CHECKDEPS_DONE, NULL, NULL); 00207 } 00208 00209 return 0; 00210 } 00211 00212 static int can_remove_file(alpm_handle_t *handle, const alpm_file_t *file, 00213 alpm_list_t *skip_remove) 00214 { 00215 char filepath[PATH_MAX]; 00216 00217 if(alpm_list_find(skip_remove, file->name, _alpm_fnmatch)) { 00218 /* return success because we will never actually remove this file */ 00219 return 1; 00220 } 00221 00222 snprintf(filepath, PATH_MAX, "%s%s", handle->root, file->name); 00223 /* If we fail write permissions due to a read-only filesystem, abort. 00224 * Assume all other possible failures are covered somewhere else */ 00225 if(_alpm_access(handle, NULL, filepath, W_OK) == -1) { 00226 if(errno != EACCES && errno != ETXTBSY && access(filepath, F_OK) == 0) { 00227 /* only return failure if the file ACTUALLY exists and we can't write to 00228 * it - ignore "chmod -w" simple permission failures */ 00229 _alpm_log(handle, ALPM_LOG_ERROR, _("cannot remove file '%s': %s\n"), 00230 filepath, strerror(errno)); 00231 return 0; 00232 } 00233 } 00234 00235 return 1; 00236 } 00237 00238 /* Helper function for iterating through a package's file and deleting them 00239 * Used by _alpm_remove_commit. */ 00240 static int unlink_file(alpm_handle_t *handle, alpm_pkg_t *oldpkg, 00241 alpm_pkg_t *newpkg, const alpm_file_t *fileobj, alpm_list_t *skip_remove, 00242 int nosave) 00243 { 00244 struct stat buf; 00245 char file[PATH_MAX]; 00246 00247 snprintf(file, PATH_MAX, "%s%s", handle->root, fileobj->name); 00248 00249 /* check the remove skip list before removing the file. 00250 * see the big comment block in db_find_fileconflicts() for an 00251 * explanation. */ 00252 if(alpm_list_find(skip_remove, fileobj->name, _alpm_fnmatch)) { 00253 _alpm_log(handle, ALPM_LOG_DEBUG, 00254 "%s is in skip_remove, skipping removal\n", file); 00255 return 1; 00256 } 00257 00258 /* we want to do a lstat here, and not a _alpm_lstat. 00259 * if a directory in the package is actually a directory symlink on the 00260 * filesystem, we want to work with the linked directory instead of the 00261 * actual symlink */ 00262 if(lstat(file, &buf)) { 00263 _alpm_log(handle, ALPM_LOG_DEBUG, "file %s does not exist\n", file); 00264 return 1; 00265 } 00266 00267 if(S_ISDIR(buf.st_mode)) { 00268 ssize_t files = _alpm_files_in_directory(handle, file, 0); 00269 /* if we have files, no need to remove the directory */ 00270 if(files > 0) { 00271 _alpm_log(handle, ALPM_LOG_DEBUG, "keeping directory %s (contains files)\n", 00272 file); 00273 } else if(files < 0) { 00274 _alpm_log(handle, ALPM_LOG_DEBUG, 00275 "keeping directory %s (could not count files)\n", file); 00276 } else if(newpkg && _alpm_filelist_contains(alpm_pkg_get_files(newpkg), 00277 fileobj->name)) { 00278 _alpm_log(handle, ALPM_LOG_DEBUG, 00279 "keeping directory %s (in new package)\n", file); 00280 } else { 00281 /* one last check- does any other package own this file? */ 00282 alpm_list_t *local, *local_pkgs; 00283 int found = 0; 00284 local_pkgs = _alpm_db_get_pkgcache(handle->db_local); 00285 for(local = local_pkgs; local && !found; local = local->next) { 00286 alpm_pkg_t *local_pkg = local->data; 00287 alpm_filelist_t *filelist; 00288 00289 /* we duplicated the package when we put it in the removal list, so we 00290 * so we can't use direct pointer comparison here. */ 00291 if(oldpkg->name_hash == local_pkg->name_hash 00292 && strcmp(oldpkg->name, local_pkg->name) == 0) { 00293 continue; 00294 } 00295 filelist = alpm_pkg_get_files(local_pkg); 00296 if(_alpm_filelist_contains(filelist, fileobj->name)) { 00297 _alpm_log(handle, ALPM_LOG_DEBUG, 00298 "keeping directory %s (owned by %s)\n", file, local_pkg->name); 00299 found = 1; 00300 } 00301 } 00302 if(!found) { 00303 if(rmdir(file)) { 00304 _alpm_log(handle, ALPM_LOG_DEBUG, 00305 "directory removal of %s failed: %s\n", file, strerror(errno)); 00306 return -1; 00307 } else { 00308 _alpm_log(handle, ALPM_LOG_DEBUG, 00309 "removed directory %s (no remaining owners)\n", file); 00310 } 00311 } 00312 } 00313 } else { 00314 /* if the file needs backup and has been modified, back it up to .pacsave */ 00315 alpm_backup_t *backup = _alpm_needbackup(fileobj->name, oldpkg); 00316 if(backup) { 00317 if(nosave) { 00318 _alpm_log(handle, ALPM_LOG_DEBUG, "transaction is set to NOSAVE, not backing up '%s'\n", file); 00319 } else { 00320 char *filehash = alpm_compute_md5sum(file); 00321 int cmp = filehash ? strcmp(filehash, backup->hash) : 0; 00322 FREE(filehash); 00323 if(cmp != 0) { 00324 char *newpath; 00325 size_t len = strlen(file) + 8 + 1; 00326 MALLOC(newpath, len, RET_ERR(handle, ALPM_ERR_MEMORY, -1)); 00327 snprintf(newpath, len, "%s.pacsave", file); 00328 if(rename(file, newpath)) { 00329 _alpm_log(handle, ALPM_LOG_ERROR, _("could not rename %s to %s (%s)\n"), 00330 file, newpath, strerror(errno)); 00331 alpm_logaction(handle, "error: could not rename %s to %s (%s)\n", 00332 file, newpath, strerror(errno)); 00333 free(newpath); 00334 return -1; 00335 } 00336 _alpm_log(handle, ALPM_LOG_WARNING, _("%s saved as %s\n"), file, newpath); 00337 alpm_logaction(handle, "warning: %s saved as %s\n", file, newpath); 00338 free(newpath); 00339 return 0; 00340 } 00341 } 00342 } 00343 00344 _alpm_log(handle, ALPM_LOG_DEBUG, "unlinking %s\n", file); 00345 00346 if(unlink(file) == -1) { 00347 _alpm_log(handle, ALPM_LOG_ERROR, _("cannot remove %s (%s)\n"), 00348 file, strerror(errno)); 00349 alpm_logaction(handle, "error: cannot remove %s (%s)\n", 00350 file, strerror(errno)); 00351 return -1; 00352 } 00353 } 00354 return 0; 00355 } 00356 00357 static int remove_package_files(alpm_handle_t *handle, 00358 alpm_pkg_t *oldpkg, alpm_pkg_t *newpkg, 00359 size_t targ_count, size_t pkg_count) 00360 { 00361 alpm_list_t *skip_remove; 00362 alpm_filelist_t *filelist; 00363 size_t i; 00364 int err = 0; 00365 int nosave = handle->trans->flags & ALPM_TRANS_FLAG_NOSAVE; 00366 00367 if(newpkg) { 00368 alpm_filelist_t *newfiles; 00369 alpm_list_t *b; 00370 skip_remove = alpm_list_join( 00371 alpm_list_strdup(handle->trans->skip_remove), 00372 alpm_list_strdup(handle->noupgrade)); 00373 /* Add files in the NEW backup array to the skip_remove array 00374 * so this removal operation doesn't kill them */ 00375 /* old package backup list */ 00376 newfiles = alpm_pkg_get_files(newpkg); 00377 for(b = alpm_pkg_get_backup(newpkg); b; b = b->next) { 00378 const alpm_backup_t *backup = b->data; 00379 /* safety check (fix the upgrade026 pactest) */ 00380 if(!_alpm_filelist_contains(newfiles, backup->name)) { 00381 continue; 00382 } 00383 _alpm_log(handle, ALPM_LOG_DEBUG, "adding %s to the skip_remove array\n", 00384 backup->name); 00385 skip_remove = alpm_list_add(skip_remove, strdup(backup->name)); 00386 } 00387 } else { 00388 skip_remove = alpm_list_strdup(handle->trans->skip_remove); 00389 } 00390 00391 filelist = alpm_pkg_get_files(oldpkg); 00392 for(i = 0; i < filelist->count; i++) { 00393 alpm_file_t *file = filelist->files + i; 00394 if(!can_remove_file(handle, file, skip_remove)) { 00395 _alpm_log(handle, ALPM_LOG_DEBUG, 00396 "not removing package '%s', can't remove all files\n", 00397 oldpkg->name); 00398 FREELIST(skip_remove); 00399 RET_ERR(handle, ALPM_ERR_PKG_CANT_REMOVE, -1); 00400 } 00401 } 00402 00403 _alpm_log(handle, ALPM_LOG_DEBUG, "removing %zd files\n", filelist->count); 00404 00405 if(!newpkg) { 00406 /* init progress bar, but only on true remove transactions */ 00407 PROGRESS(handle, ALPM_PROGRESS_REMOVE_START, oldpkg->name, 0, 00408 pkg_count, targ_count); 00409 } 00410 00411 /* iterate through the list backwards, unlinking files */ 00412 for(i = filelist->count; i > 0; i--) { 00413 alpm_file_t *file = filelist->files + i - 1; 00414 if(unlink_file(handle, oldpkg, newpkg, file, skip_remove, nosave) < 0) { 00415 err++; 00416 } 00417 00418 if(!newpkg) { 00419 /* update progress bar after each file */ 00420 int percent = ((filelist->count - i) * 100) / filelist->count; 00421 PROGRESS(handle, ALPM_PROGRESS_REMOVE_START, oldpkg->name, 00422 percent, pkg_count, targ_count); 00423 } 00424 } 00425 FREELIST(skip_remove); 00426 00427 if(!newpkg) { 00428 /* set progress to 100% after we finish unlinking files */ 00429 PROGRESS(handle, ALPM_PROGRESS_REMOVE_START, oldpkg->name, 100, 00430 pkg_count, targ_count); 00431 } 00432 00433 return err; 00434 } 00435 00436 int _alpm_remove_single_package(alpm_handle_t *handle, 00437 alpm_pkg_t *oldpkg, alpm_pkg_t *newpkg, 00438 size_t targ_count, size_t pkg_count) 00439 { 00440 const char *pkgname = oldpkg->name; 00441 const char *pkgver = oldpkg->version; 00442 00443 if(newpkg) { 00444 _alpm_log(handle, ALPM_LOG_DEBUG, "removing old package first (%s-%s)\n", 00445 pkgname, pkgver); 00446 } else { 00447 EVENT(handle, ALPM_EVENT_REMOVE_START, oldpkg, NULL); 00448 _alpm_log(handle, ALPM_LOG_DEBUG, "removing package %s-%s\n", 00449 pkgname, pkgver); 00450 00451 /* run the pre-remove scriptlet if it exists */ 00452 if(alpm_pkg_has_scriptlet(oldpkg) && 00453 !(handle->trans->flags & ALPM_TRANS_FLAG_NOSCRIPTLET)) { 00454 char *scriptlet = _alpm_local_db_pkgpath(handle->db_local, 00455 oldpkg, "install"); 00456 _alpm_runscriptlet(handle, scriptlet, "pre_remove", pkgver, NULL, 0); 00457 free(scriptlet); 00458 } 00459 } 00460 00461 if(!(handle->trans->flags & ALPM_TRANS_FLAG_DBONLY)) { 00462 /* TODO check returned errors if any */ 00463 remove_package_files(handle, oldpkg, newpkg, targ_count, pkg_count); 00464 } 00465 00466 /* run the post-remove script if it exists */ 00467 if(!newpkg && alpm_pkg_has_scriptlet(oldpkg) && 00468 !(handle->trans->flags & ALPM_TRANS_FLAG_NOSCRIPTLET)) { 00469 char *scriptlet = _alpm_local_db_pkgpath(handle->db_local, 00470 oldpkg, "install"); 00471 _alpm_runscriptlet(handle, scriptlet, "post_remove", pkgver, NULL, 0); 00472 free(scriptlet); 00473 } 00474 00475 if(!newpkg) { 00476 EVENT(handle, ALPM_EVENT_REMOVE_DONE, oldpkg, NULL); 00477 } 00478 00479 /* remove the package from the database */ 00480 _alpm_log(handle, ALPM_LOG_DEBUG, "removing database entry '%s'\n", pkgname); 00481 if(_alpm_local_db_remove(handle->db_local, oldpkg) == -1) { 00482 _alpm_log(handle, ALPM_LOG_ERROR, _("could not remove database entry %s-%s\n"), 00483 pkgname, pkgver); 00484 } 00485 /* remove the package from the cache */ 00486 if(_alpm_db_remove_pkgfromcache(handle->db_local, oldpkg) == -1) { 00487 _alpm_log(handle, ALPM_LOG_ERROR, _("could not remove entry '%s' from cache\n"), 00488 pkgname); 00489 } 00490 00491 return 0; 00492 } 00493 00494 int _alpm_remove_packages(alpm_handle_t *handle, int run_ldconfig) 00495 { 00496 alpm_list_t *targ; 00497 size_t pkg_count, targ_count; 00498 alpm_trans_t *trans = handle->trans; 00499 int ret = 0; 00500 00501 pkg_count = alpm_list_count(trans->remove); 00502 targ_count = 1; 00503 00504 for(targ = trans->remove; targ; targ = targ->next) { 00505 alpm_pkg_t *pkg = targ->data; 00506 00507 if(trans->state == STATE_INTERRUPTED) { 00508 return ret; 00509 } 00510 00511 if(_alpm_remove_single_package(handle, pkg, NULL, 00512 targ_count, pkg_count) == -1) { 00513 handle->pm_errno = ALPM_ERR_TRANS_ABORT; 00514 /* running ldconfig at this point could possibly screw system */ 00515 run_ldconfig = 0; 00516 ret = -1; 00517 } 00518 00519 targ_count++; 00520 } 00521 00522 if(run_ldconfig) { 00523 /* run ldconfig if it exists */ 00524 _alpm_ldconfig(handle); 00525 } 00526 00527 return ret; 00528 } 00529 00530 /* vim: set ts=2 sw=2 noet: */