If a file/directory/symlink does not already exist on the filesystem, then it is extracted as normal. Never extract a package entry over a directory. When extracting a directory: - If a symlink already exists on the filesystem then the directory is not extracted. BUT if the symlink does not point to a directory we have a problem! What do we do in this situation? If we abort or continue we can end up breaking the system! When extracting a symlink: - If a directory already exists on the filesystem then the symlink is not extracted. When extracting a reqular file - Do the normal backup checks. For all other cases the conflict checks have probably failed. Signed-off-by: Andrew Fyfe <andrew@neptune-one.net> --- lib/libalpm/add.c | 97 +++++++++++++++++++++++++++++++++++++--------------- 1 files changed, 69 insertions(+), 28 deletions(-) diff --git a/lib/libalpm/add.c b/lib/libalpm/add.c index 870d1f8..dc9b1e6 100644 --- a/lib/libalpm/add.c +++ b/lib/libalpm/add.c @@ -439,6 +439,7 @@ int _alpm_add_commit(pmtrans_t *trans, pmdb_t *db) int needbackup = 0, notouch = 0; char *hash_orig = NULL; struct stat buf; + mode_t entrytype = archive_entry_filetype(entry); entryname = archive_entry_pathname(entry); @@ -459,11 +460,7 @@ int _alpm_add_commit(pmtrans_t *trans, pmdb_t *db) memset(filename, 0, PATH_MAX); /* just to be sure */ - if(strcmp(entryname, ".PKGINFO") == 0 - || strcmp(entryname, ".FILELIST") == 0) { - archive_read_data_skip(archive); - continue; - } else if(strcmp(entryname, ".INSTALL") == 0) { + if(strcmp(entryname, ".INSTALL") == 0) { /* the install script goes inside the db */ snprintf(filename, PATH_MAX, "%s/%s-%s/install", db->path, newpkg->name, newpkg->version); @@ -497,33 +494,77 @@ int _alpm_add_commit(pmtrans_t *trans, pmdb_t *db) continue; } - /* check is file already exists */ - if(stat(filename, &buf) == 0 && !S_ISDIR(buf.st_mode)) { - /* it does, is it a backup=() file? - * always check the newpkg first, so when we do add a backup=() file, - * we don't have to wait a full upgrade cycle */ - needbackup = alpm_list_find_str(alpm_pkg_get_backup(newpkg), entryname); - - if(is_upgrade) { - hash_orig = _alpm_needbackup(entryname, alpm_pkg_get_backup(oldpkg)); - if(hash_orig) { - needbackup = 1; + if(lstat(filename, &buf) == 0) { + /* Entry exists on filesystem... */ + + if(S_ISDIR(buf.st_mode)) { + /* Don't extract anything over an existing directory.*/ + if(!S_ISDIR(entrytype)) { + _alpm_log(PM_LOG_WARNING, _("%s won't be extracted over an existing directory."), entryname); } + + _alpm_log(PM_LOG_DEBUG, _("%s won't be extracted over an existing directory."), entryname); + archive_read_data_skip(archive); + continue; } - /* this is kind of gross. if we force hash_orig to be non-NULL we can - * catch the pro-active backup=() case (when the backup entry is in - * the new package, and not the old */ - if(needbackup && !hash_orig) { - hash_orig = strdup(""); + if(S_ISDIR(entrytype) && S_ISLNK(buf.st_mode)) { + /* Don't extract a directory over an existing symlink. */ + if(stat(filename, &buf) == 0 && !S_ISDIR(buf.st_mode)) { + /* Something's going to break! A symlink exists where we + * want to extract this directory, and it doesn't point to + * a directory. + * + * This will cause problems if this package or any other + * package try to extract files to this directory. */ + + /* TODO: How do we handle this? If we continue we leeave a + * broken system, if we abort we leave a broken system! */ + _alpm_log(PM_LOG_ERROR, _("%s is a symlink, but it does not point to a directory!"), entryname); + } + + _alpm_log(PM_LOG_DEBUG, _("%s (directory) won't be extracted over existing symlink."), entryname); + archive_read_data_skip(archive); + continue; } - - /* NoUpgrade skips all this backup stuff, because it's just never - * touched */ - if(alpm_list_find_str(handle->noupgrade, entryname)) { - notouch = 1; - needbackup = 0; + + if(S_ISLNK(entrytype) && S_ISDIR(buf.st_mode)) { + /* Don't extract a symlink over a directory. */ + _alpm_log(PM_LOG_DEBUG, _("%s (symlink) won't be extracted over existing directory."), entryname); + archive_read_data_skip(archive); + continue; } + + if(S_ISREG(entrytype)) { + /* always check the newpkg first, so when we do add a backup=() file, + * we don't have to wait a full upgrade cycle */ + needbackup = alpm_list_find_str(alpm_pkg_get_backup(newpkg), entryname); + + if(is_upgrade) { + hash_orig = _alpm_needbackup(entryname, alpm_pkg_get_backup(oldpkg)); + if(hash_orig) { + needbackup = 1; + } + } + + /* this is kind of gross. if we force hash_orig to be non-NULL we can + * catch the pro-active backup=() case (when the backup entry is in + * the new package, and not the old */ + if(needbackup && !hash_orig) { + hash_orig = strdup(""); + } + + /* NoUpgrade skips all this backup stuff, because it's just never + * touched */ + if(alpm_list_find_str(handle->noupgrade, entryname)) { + notouch = 1; + needbackup = 0; + } + } + /* else { + * we shouldn't be here! This should have already been + * caught by the conflict check. + * } */ } if(needbackup) { @@ -705,7 +746,7 @@ int _alpm_add_commit(pmtrans_t *trans, pmdb_t *db) * 1) if it's an upgrade, existing files are removed when the old pkg is removed * 2) if there is a file conflict, but --force is used, then files are also removed : see above */ - int ret = archive_read_extract(archive, entry, archive_flags | ARCHIVE_EXTRACT_NO_OVERWRITE); + int ret = archive_read_extract(archive, entry, archive_flags); if(ret == ARCHIVE_WARN) { /* operation succeeded but a non-critical error was encountered */ _alpm_log(PM_LOG_DEBUG, _("warning extracting %s (%s)"), -- 1.5.2.3