summaryrefslogtreecommitdiffstats
path: root/lib/libalpm/remove.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/libalpm/remove.c')
-rw-r--r--lib/libalpm/remove.c208
1 files changed, 151 insertions, 57 deletions
diff --git a/lib/libalpm/remove.c b/lib/libalpm/remove.c
index cc9e289d..148cb863 100644
--- a/lib/libalpm/remove.c
+++ b/lib/libalpm/remove.c
@@ -22,8 +22,6 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#include "config.h"
-
#include <stdlib.h>
#include <errno.h>
#include <string.h>
@@ -45,6 +43,14 @@
#include "handle.h"
#include "conflict.h"
+/**
+ * @brief Add a package removal action to the transaction.
+ *
+ * @param handle the context handle
+ * @param pkg the package to uninstall
+ *
+ * @return 0 on success, -1 on error
+ */
int SYMEXPORT alpm_remove_pkg(alpm_handle_t *handle, alpm_pkg_t *pkg)
{
const char *pkgname;
@@ -75,6 +81,14 @@ int SYMEXPORT alpm_remove_pkg(alpm_handle_t *handle, alpm_pkg_t *pkg)
return 0;
}
+/**
+ * @brief Add dependencies to the removal transaction for cascading.
+ *
+ * @param handle the context handle
+ * @param lp list of missing dependencies caused by the removal transaction
+ *
+ * @return 0 on success, -1 on error
+ */
static int remove_prepare_cascade(alpm_handle_t *handle, alpm_list_t *lp)
{
alpm_trans_t *trans = handle->trans;
@@ -107,6 +121,12 @@ static int remove_prepare_cascade(alpm_handle_t *handle, alpm_list_t *lp)
return 0;
}
+/**
+ * @brief Remove needed packages from the removal transaction.
+ *
+ * @param handle the context handle
+ * @param lp list of missing dependencies caused by the removal transaction
+ */
static void remove_prepare_keep_needed(alpm_handle_t *handle, alpm_list_t *lp)
{
alpm_trans_t *trans = handle->trans;
@@ -137,12 +157,16 @@ static void remove_prepare_keep_needed(alpm_handle_t *handle, alpm_list_t *lp)
}
}
-/** Transaction preparation for remove actions.
+/**
+ * @brief Transaction preparation for remove actions.
+ *
* This functions takes a pointer to a alpm_list_t which will be
* filled with a list of alpm_depmissing_t* objects representing
* the packages blocking the transaction.
+ *
* @param handle the context handle
* @param data a pointer to an alpm_list_t* to fill
+ *
* @return 0 on success, -1 on error
*/
int _alpm_remove_prepare(alpm_handle_t *handle, alpm_list_t **data)
@@ -211,12 +235,21 @@ int _alpm_remove_prepare(alpm_handle_t *handle, alpm_list_t **data)
return 0;
}
+/**
+ * @brief Check if alpm can delete a file.
+ *
+ * @param handle the context handle
+ * @param file file to be removed
+ * @param skip_remove list of files that will not be removed
+ *
+ * @return 1 if the file can be deleted, 0 if it cannot be deleted
+ */
static int can_remove_file(alpm_handle_t *handle, const alpm_file_t *file,
alpm_list_t *skip_remove)
{
char filepath[PATH_MAX];
- if(alpm_list_find_str(skip_remove, file->name)) {
+ if(alpm_list_find(skip_remove, file->name, _alpm_fnmatch)) {
/* return success because we will never actually remove this file */
return 1;
}
@@ -237,10 +270,26 @@ static int can_remove_file(alpm_handle_t *handle, const alpm_file_t *file,
return 1;
}
-/* Helper function for iterating through a package's file and deleting them
- * Used by _alpm_remove_commit. */
-static int unlink_file(alpm_handle_t *handle, alpm_pkg_t *info,
- const alpm_file_t *fileobj, alpm_list_t *skip_remove, int nosave)
+/**
+ * @brief Unlink a package file, backing it up if necessary.
+ *
+ * @note Helper function for iterating through a package's file and deleting
+ * them.
+ * @note Used by _alpm_remove_commit.
+ *
+ * @param handle the context handle
+ * @param oldpkg the package being removed
+ * @param newpkg the package replacing \a oldpkg
+ * @param fileobj file to remove
+ * @param skip_remove list of files that shouldn't be removed
+ * @param nosave whether files should be backed up
+ *
+ * @return 0 on success, -1 if there was an error unlinking the file, 1 if the
+ * file was skipped or did not exist
+ */
+static int unlink_file(alpm_handle_t *handle, alpm_pkg_t *oldpkg,
+ alpm_pkg_t *newpkg, const alpm_file_t *fileobj, alpm_list_t *skip_remove,
+ int nosave)
{
struct stat buf;
char file[PATH_MAX];
@@ -250,7 +299,7 @@ static int unlink_file(alpm_handle_t *handle, alpm_pkg_t *info,
/* check the remove skip list before removing the file.
* see the big comment block in db_find_fileconflicts() for an
* explanation. */
- if(alpm_list_find_str(skip_remove, fileobj->name)) {
+ if(alpm_list_find(skip_remove, fileobj->name, _alpm_fnmatch)) {
_alpm_log(handle, ALPM_LOG_DEBUG,
"%s is in skip_remove, skipping removal\n", file);
return 1;
@@ -274,6 +323,10 @@ static int unlink_file(alpm_handle_t *handle, alpm_pkg_t *info,
} else if(files < 0) {
_alpm_log(handle, ALPM_LOG_DEBUG,
"keeping directory %s (could not count files)\n", file);
+ } else if(newpkg && _alpm_filelist_contains(alpm_pkg_get_files(newpkg),
+ fileobj->name)) {
+ _alpm_log(handle, ALPM_LOG_DEBUG,
+ "keeping directory %s (in new package)\n", file);
} else {
/* one last check- does any other package own this file? */
alpm_list_t *local, *local_pkgs;
@@ -285,7 +338,8 @@ static int unlink_file(alpm_handle_t *handle, alpm_pkg_t *info,
/* we duplicated the package when we put it in the removal list, so we
* so we can't use direct pointer comparison here. */
- if(_alpm_pkg_cmp(info, local_pkg) == 0) {
+ if(oldpkg->name_hash == local_pkg->name_hash
+ && strcmp(oldpkg->name, local_pkg->name) == 0) {
continue;
}
filelist = alpm_pkg_get_files(local_pkg);
@@ -308,7 +362,7 @@ static int unlink_file(alpm_handle_t *handle, alpm_pkg_t *info,
}
} else {
/* if the file needs backup and has been modified, back it up to .pacsave */
- alpm_backup_t *backup = _alpm_needbackup(fileobj->name, info);
+ alpm_backup_t *backup = _alpm_needbackup(fileobj->name, oldpkg);
if(backup) {
if(nosave) {
_alpm_log(handle, ALPM_LOG_DEBUG, "transaction is set to NOSAVE, not backing up '%s'\n", file);
@@ -350,38 +404,28 @@ static int unlink_file(alpm_handle_t *handle, alpm_pkg_t *info,
return 0;
}
-int _alpm_remove_single_package(alpm_handle_t *handle,
+/**
+ * @brief Remove a package's files, optionally skipping its replacement's
+ * files.
+ *
+ * @param handle the context handle
+ * @param oldpkg package to remove
+ * @param newpkg package to replace \a oldpkg (optional)
+ * @param targ_count current index within the transaction (1-based)
+ * @param pkg_count the number of packages affected by the transaction
+ *
+ * @return 0 on success, -1 if alpm lacks permission to delete some of the
+ * files, >0 the number of files alpm was unable to delete
+ */
+static int remove_package_files(alpm_handle_t *handle,
alpm_pkg_t *oldpkg, alpm_pkg_t *newpkg,
size_t targ_count, size_t pkg_count)
{
alpm_list_t *skip_remove;
- size_t filenum = 0, position = 0;
- const char *pkgname = oldpkg->name;
- const char *pkgver = oldpkg->version;
alpm_filelist_t *filelist;
size_t i;
-
- if(newpkg) {
- _alpm_log(handle, ALPM_LOG_DEBUG, "removing old package first (%s-%s)\n",
- pkgname, pkgver);
- } else {
- EVENT(handle, ALPM_EVENT_REMOVE_START, oldpkg, NULL);
- _alpm_log(handle, ALPM_LOG_DEBUG, "removing package %s-%s\n",
- pkgname, pkgver);
-
- /* run the pre-remove scriptlet if it exists */
- if(alpm_pkg_has_scriptlet(oldpkg) &&
- !(handle->trans->flags & ALPM_TRANS_FLAG_NOSCRIPTLET)) {
- char *scriptlet = _alpm_local_db_pkgpath(handle->db_local,
- oldpkg, "install");
- _alpm_runscriptlet(handle, scriptlet, "pre_remove", pkgver, NULL, 0);
- free(scriptlet);
- }
- }
-
- if(handle->trans->flags & ALPM_TRANS_FLAG_DBONLY) {
- goto db;
- }
+ int err = 0;
+ int nosave = handle->trans->flags & ALPM_TRANS_FLAG_NOSAVE;
if(newpkg) {
alpm_filelist_t *newfiles;
@@ -412,54 +456,100 @@ int _alpm_remove_single_package(alpm_handle_t *handle,
alpm_file_t *file = filelist->files + i;
if(!can_remove_file(handle, file, skip_remove)) {
_alpm_log(handle, ALPM_LOG_DEBUG,
- "not removing package '%s', can't remove all files\n", pkgname);
+ "not removing package '%s', can't remove all files\n",
+ oldpkg->name);
+ FREELIST(skip_remove);
RET_ERR(handle, ALPM_ERR_PKG_CANT_REMOVE, -1);
}
- filenum++;
}
- _alpm_log(handle, ALPM_LOG_DEBUG, "removing %ld files\n", (unsigned long)filenum);
+ _alpm_log(handle, ALPM_LOG_DEBUG, "removing %zd files\n", filelist->count);
if(!newpkg) {
/* init progress bar, but only on true remove transactions */
- PROGRESS(handle, ALPM_PROGRESS_REMOVE_START, pkgname, 0,
+ PROGRESS(handle, ALPM_PROGRESS_REMOVE_START, oldpkg->name, 0,
pkg_count, targ_count);
}
/* iterate through the list backwards, unlinking files */
for(i = filelist->count; i > 0; i--) {
alpm_file_t *file = filelist->files + i - 1;
- int percent;
- /* TODO: check return code and handle accordingly */
- unlink_file(handle, oldpkg, file, skip_remove,
- handle->trans->flags & ALPM_TRANS_FLAG_NOSAVE);
+ if(unlink_file(handle, oldpkg, newpkg, file, skip_remove, nosave) < 0) {
+ err++;
+ }
if(!newpkg) {
/* update progress bar after each file */
- percent = (position * 100) / filenum;
- PROGRESS(handle, ALPM_PROGRESS_REMOVE_START, pkgname,
+ int percent = ((filelist->count - i) * 100) / filelist->count;
+ PROGRESS(handle, ALPM_PROGRESS_REMOVE_START, oldpkg->name,
percent, pkg_count, targ_count);
}
- position++;
}
FREELIST(skip_remove);
if(!newpkg) {
/* set progress to 100% after we finish unlinking files */
- PROGRESS(handle, ALPM_PROGRESS_REMOVE_START, pkgname, 100,
+ PROGRESS(handle, ALPM_PROGRESS_REMOVE_START, oldpkg->name, 100,
pkg_count, targ_count);
+ }
+
+ return err;
+}
+
+/**
+ * @brief Remove a package from the filesystem.
+ *
+ * @param handle the context handle
+ * @param oldpkg package to remove
+ * @param newpkg package to replace \a oldpkg (optional)
+ * @param targ_count current index within the transaction (1-based)
+ * @param pkg_count the number of packages affected by the transaction
+ *
+ * @return 0
+ */
+int _alpm_remove_single_package(alpm_handle_t *handle,
+ alpm_pkg_t *oldpkg, alpm_pkg_t *newpkg,
+ size_t targ_count, size_t pkg_count)
+{
+ const char *pkgname = oldpkg->name;
+ const char *pkgver = oldpkg->version;
- /* run the post-remove script if it exists */
+ if(newpkg) {
+ _alpm_log(handle, ALPM_LOG_DEBUG, "removing old package first (%s-%s)\n",
+ pkgname, pkgver);
+ } else {
+ EVENT(handle, ALPM_EVENT_REMOVE_START, oldpkg, NULL);
+ _alpm_log(handle, ALPM_LOG_DEBUG, "removing package %s-%s\n",
+ pkgname, pkgver);
+
+ /* run the pre-remove scriptlet if it exists */
if(alpm_pkg_has_scriptlet(oldpkg) &&
!(handle->trans->flags & ALPM_TRANS_FLAG_NOSCRIPTLET)) {
char *scriptlet = _alpm_local_db_pkgpath(handle->db_local,
oldpkg, "install");
- _alpm_runscriptlet(handle, scriptlet, "post_remove", pkgver, NULL, 0);
+ _alpm_runscriptlet(handle, scriptlet, "pre_remove", pkgver, NULL, 0);
free(scriptlet);
}
}
-db:
+ if(!(handle->trans->flags & ALPM_TRANS_FLAG_DBONLY)) {
+ /* TODO check returned errors if any */
+ remove_package_files(handle, oldpkg, newpkg, targ_count, pkg_count);
+ }
+
+ /* run the post-remove script if it exists */
+ if(!newpkg && alpm_pkg_has_scriptlet(oldpkg) &&
+ !(handle->trans->flags & ALPM_TRANS_FLAG_NOSCRIPTLET)) {
+ char *scriptlet = _alpm_local_db_pkgpath(handle->db_local,
+ oldpkg, "install");
+ _alpm_runscriptlet(handle, scriptlet, "post_remove", pkgver, NULL, 0);
+ free(scriptlet);
+ }
+
+ if(!newpkg) {
+ EVENT(handle, ALPM_EVENT_REMOVE_DONE, oldpkg, NULL);
+ }
+
/* remove the package from the database */
_alpm_log(handle, ALPM_LOG_DEBUG, "removing database entry '%s'\n", pkgname);
if(_alpm_local_db_remove(handle->db_local, oldpkg) == -1) {
@@ -472,14 +562,18 @@ db:
pkgname);
}
- if(!newpkg) {
- /* TODO: awesome! we're passing invalid pointers. */
- EVENT(handle, ALPM_EVENT_REMOVE_DONE, oldpkg, NULL);
- }
-
+ /* TODO: useful return values */
return 0;
}
+/**
+ * @brief Remove packages in the current transaction.
+ *
+ * @param handle the context handle
+ * @param run_ldconfig whether to run ld_config after removing the packages
+ *
+ * @return 0 on success, -1 if errors occurred while removing files
+ */
int _alpm_remove_packages(alpm_handle_t *handle, int run_ldconfig)
{
alpm_list_t *targ;