From b55abdce7aebb142ce79da3aa3645afe7693a3c4 Mon Sep 17 00:00:00 2001
From: Dan McGee <dan@archlinux.org>
Date: Sun, 4 Nov 2007 18:02:25 -0600
Subject: libalpm: use an lstat wrapper so we never dereference dir symlinks

Linux lstat follows POSIX standards and dereferences a symlink pointing
to a directory if there is a trailing slash. For purposes of libalpm, we
don't want this so make a lstat wrapper that suppresses this behavior.

Signed-off-by: Dan McGee <dan@archlinux.org>
---
 lib/libalpm/add.c      |  4 ++--
 lib/libalpm/conflict.c |  4 ++--
 lib/libalpm/remove.c   |  2 +-
 lib/libalpm/util.c     | 26 +++++++++++++++++++++++++-
 lib/libalpm/util.h     |  2 ++
 5 files changed, 32 insertions(+), 6 deletions(-)

(limited to 'lib')

diff --git a/lib/libalpm/add.c b/lib/libalpm/add.c
index a10b20d1..a76f0266 100644
--- a/lib/libalpm/add.c
+++ b/lib/libalpm/add.c
@@ -371,7 +371,7 @@ static int extract_single_file(struct archive *archive,
 	 *  S            |   7   |  8  |  9
 	 *  D            |   10  |  11 |  12
 	 *
-	 *  1,2,3- just extract, no magic necessary. lstat will fail here.
+	 *  1,2,3- extract, no magic necessary. lstat (_alpm_lstat) will fail here.
 	 *  4,5,6,7,8- conflict checks should have caught this. either overwrite
 	 *      or backup the file.
 	 *  9- follow the symlink, hopefully it is a directory, check it.
@@ -381,7 +381,7 @@ static int extract_single_file(struct archive *archive,
 	 *  12- skip extraction, dir already exists.
 	 */
 	struct stat lsbuf;
-	if(lstat(filename, &lsbuf) != 0) {
+	if(_alpm_lstat(filename, &lsbuf) != 0) {
 		/* cases 1,2,3: couldn't stat an existing file, skip all backup checks */
 	} else {
 		/* do a stat as well, so we can see what symlinks point to */
diff --git a/lib/libalpm/conflict.c b/lib/libalpm/conflict.c
index 0baef8d1..539e06ab 100644
--- a/lib/libalpm/conflict.c
+++ b/lib/libalpm/conflict.c
@@ -334,7 +334,7 @@ alpm_list_t *_alpm_db_find_conflicts(pmdb_t *db, pmtrans_t *trans, char *root)
 			snprintf(path, PATH_MAX, "%s%s", root, filestr);
 
 			/* stat the file - if it exists, do some checks */
-			if(lstat(path, &buf) != 0) {
+			if(_alpm_lstat(path, &buf) != 0) {
 				continue;
 			}
 			if(S_ISDIR(buf.st_mode)) {
@@ -350,7 +350,7 @@ alpm_list_t *_alpm_db_find_conflicts(pmdb_t *db, pmtrans_t *trans, char *root)
 					unsigned ok = 0;
 					for(k = dbpkg->files; k; k = k->next) {
 						snprintf(str, PATH_MAX, "%s%s", root, (char*)k->data);
-						if(!lstat(str, &buf2) && buf.st_ino == buf2.st_ino) {
+						if(!_alpm_lstat(str, &buf2) && buf.st_ino == buf2.st_ino) {
 							ok = 1;
 							_alpm_log(PM_LOG_DEBUG, "conflict was a symlink: %s\n", path);
 							break;
diff --git a/lib/libalpm/remove.c b/lib/libalpm/remove.c
index 2c8bdc49..6bbb4655 100644
--- a/lib/libalpm/remove.c
+++ b/lib/libalpm/remove.c
@@ -206,7 +206,7 @@ static void unlink_file(pmpkg_t *info, alpm_list_t *lp, pmtrans_t *trans)
 		}
 	}
 
-	if(lstat(file, &buf)) {
+	if(_alpm_lstat(file, &buf)) {
 		_alpm_log(PM_LOG_DEBUG, "file %s does not exist\n", file);
 		return;
 	}
diff --git a/lib/libalpm/util.c b/lib/libalpm/util.c
index 016c0f40..5df3a025 100644
--- a/lib/libalpm/util.c
+++ b/lib/libalpm/util.c
@@ -432,7 +432,7 @@ int _alpm_rmrf(const char *path)
 	char name[PATH_MAX];
 	struct stat st;
 
-	if(lstat(path, &st) == 0) {
+	if(_alpm_lstat(path, &st) == 0) {
 		if(!S_ISDIR(st.st_mode)) {
 			if(!unlink(path)) {
 				return(0);
@@ -597,6 +597,30 @@ const char *_alpm_filecache_setup(void)
 	return(alpm_list_getdata(tmp));
 }
 
+/** lstat wrapper that treats /path/dirsymlink/ the same as /path/dirsymlink.
+ * Linux lstat follows POSIX semantics and still performs a dereference on
+ * the first, and for uses of lstat in libalpm this is not what we want.
+ * @param path path to file to lstat
+ * @param buf structure to fill with stat information
+ * @return the return code from lstat
+ */
+int _alpm_lstat(const char *path, struct stat *buf)
+{
+	int ret;
+	char *newpath = strdup(path);
+	int len = strlen(newpath);
+
+	/* strip the trailing slash if one exists */
+	if(len != 0 && newpath[len - 1] == '/') {
+			newpath[len - 1] = '\0';
+	}
+
+	ret = lstat(path, buf);
+
+	FREE(newpath);
+	return(ret);
+}
+
 /** Get the md5 sum of file.
  * @param filename name of the file
  * @return the checksum on success, NULL on error
diff --git a/lib/libalpm/util.h b/lib/libalpm/util.h
index e8b1d719..5d0b3693 100644
--- a/lib/libalpm/util.h
+++ b/lib/libalpm/util.h
@@ -30,6 +30,7 @@
 #include <stdio.h>
 #include <stdarg.h>
 #include <time.h>
+#include <sys/stat.h> /* struct stat */
 
 #ifdef ENABLE_NLS
 #include <libintl.h> /* here so it doesn't need to be included elsewhere */
@@ -62,6 +63,7 @@ void _alpm_time2string(time_t t, char *buffer);
 int _alpm_str_cmp(const void *s1, const void *s2);
 char *_alpm_filecache_find(const char *filename);
 const char *_alpm_filecache_setup(void);
+int _alpm_lstat(const char *path, struct stat *buf);
 
 #ifndef HAVE_STRVERSCMP
 int strverscmp(const char *, const char *);
-- 
cgit v1.2.3-2-g168b