Attached is a fix for this problem. I THINK it covers all the bases but it needs testing to make sure there aren't any cases that I've missed. See the patch header for full details. For those that rather get it through git, checkout the FS7484 branch (http://neptune-one.homeip.net/git?p=pacman;a=shortlog;h=FS7484). When testing run pacman with the --debug option and look out for... "%s (directory) won't be extracted over existing directory or symlink." "%s (symlink) won't be extracted over existing directory." Andrew From: Andrew Fyfe <andrew@neptune-one.net> Date: Sun, 8 Jul 2007 00:02:48 +0000 (+0100) Subject: FS#7484 - pacman clobbers directory symlinks. X-Git-Url: http://lithium.sol/gitweb/?p=pacman;a=commitdiff_plain;h=c992a5d757efa5aee45... FS#7484 - pacman clobbers directory symlinks. If a file/directory/symlink does not already exist on the filesystem, then it is extracted as normal. When extracting a directory: - If a directory or symlink already exists on the filesystem then the directory is not extracted. When extracting a symlink: - If a directory already exists on the filesystem then the symlink is not extracted. When extracting a special file (FIFO, block dev or char dev): - If the file it is replacing is a special file no backup checks are done. For all other cases when a file exists on the filesystem the normal backuup checks are performed. Signed-off-by: Andrew Fyfe <andrew@neptune-one.net> --- diff --git a/lib/libalpm/add.c b/lib/libalpm/add.c index 870d1f8..62bc2f7 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); @@ -498,31 +495,56 @@ int _alpm_add_commit(pmtrans_t *trans, pmdb_t *db) } /* 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(stat(filename, &buf) == 0) { + if ( S_ISDIR(entrytype) && (S_ISDIR(buf.st_mode) || S_ISLNK(buf.st_mode)) ) { + /* If the package entry is a directory and there's a directory or + * symlink with the same name on the filesystem, skip this entry. */ + _alpm_log(PM_LOG_DEBUG, _("%s (directory) won't be extracted over existing directory or symlink"), 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_ISLNK(entrytype) && S_ISDIR(buf.st_mode) ) { + /* If the package entry is a symlink and the fs entry is a + * directory, skip this entry. */ + _alpm_log(PM_LOG_DEBUG, _("%s (symlink) won't be extracted over existing directory."), 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_ISFIFO(entrytype) || S_ISBLK(entrytype) || S_ISCHR(entrytype)) && + (S_ISFIFO(buf.st_mode) || S_ISBLK(buf.st_mode) || S_ISCHR(buf.st_mode)) ) { + /* If the package entry is a FIFO, block device or character device + * and the filesystem entry is a FIFO, block devicce or character device + * unlink the fs entry and extract the package entry. */ + _alpm_log(PM_LOG_DEBUG, _("%s is a FIFO/block/character device, extracting as normal."), entryname); + } + else { + /* 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; + } + } + + /* 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; + } } }