[PATCH 1/2] resolve symlinks when creating lockfiles

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



From: Bradford C. Smith <bradford.carl.smith@xxxxxxxxx>

Without this fix, the lockfile code will replace a symlink with a real file.

Signed-off-by: "Bradford C. Smith" <bradford.carl.smith@xxxxxxxxx>
---
 lockfile.c |   87 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 files changed, 86 insertions(+), 1 deletions(-)

diff --git a/lockfile.c b/lockfile.c
index fb8f13b..4c35224 100644
--- a/lockfile.c
+++ b/lockfile.c
@@ -25,10 +25,95 @@ static void remove_lock_file_on_signal(int signo)
 	raise(signo);
 }
 
+/**
+ * p = absolute or relative path name
+ *
+ * Return a pointer into p showing the beginning of the last path name
+ * element.  If p is empty or the root directory ("/"), just return p.
+ */
+static char * last_path_elm(char * p)
+{
+	int	p_len = strlen(p);
+	char *	r;
+
+	if (p_len < 1) return p;
+	/* r points to last non-null character in p */
+	r = p + p_len - 1;
+	/* first skip any trailing slashes */
+	while (*r == '/' && r > p) r--;
+	/* then go back to the first non-slash */
+	while (r > p && *(r-1) != '/') r--;
+	return r;
+}
+
+/**
+ * p = char array containing path to existing file or symlink
+ * s = size of p
+ *
+ * If p indicates a valid symlink to an existing file, overwrite p with
+ * the path to the real file.  Otherwise, leave p unmodified.
+ *
+ * Always returns p in any case.
+ *
+ * NOTE: This is a best-effort routine.  It will give no indication of
+ * failure if it is unable to fully resolve p.  However, it is
+ * guaranteed to leave p in one of the following states if there isn't
+ * enough room in p or some other failure occurs:
+ *
+ * 1. unmodified
+ *      OR
+ * 2. path to a different symlink in a chain that eventually leads to a
+ *    real file or directory.
+ */
+static char * resolve_symlink(char * p, size_t s)
+{
+	struct stat st;
+	char link[PATH_MAX];
+	int link_len;
+
+	/* To avoid an infinite loop of symlinks, try a normal stat()
+	 * first.  This will fail if p is a symlink that cannot be
+	 * resolved, so we won't waste our time following a bad link. */
+	if (stat(p, &st)) return p;
+	/* if I can stat() the file, I sure ought to be able to lstat()
+	 * it, but if something bizarre happens, just return p.  */
+	if (lstat(p, &st)) return p;
+	/* if not a link, return p unmodified */
+	if (!S_ISLNK(st.st_mode)) return p;
+	link_len = st.st_size;
+	/* link is too big, so just return p */
+	if (link_len >= sizeof(link)) return p;
+	/* fail if readlink fails, and just return p */
+	if (link_len != readlink(p, link, sizeof(link))) return p;
+	/* readlink never null-terminates */
+	link[link_len] = '\0';
+	if (link[0] == '/') {
+		/* absolute path simply replaces p */
+		/* fail if link won't fit in p */
+		if (link_len >= s) return p;
+		strcpy(p, link);
+	} else {
+		/* link is relative path, so we must replace the last
+		 * element of p with it. */
+		char * r = last_path_elm(p);
+		/* make sure there's room in p for us to replace the
+		 * last element with the link contents */
+		if (r - p + link_len >= s) return p;
+		strcpy(r, link);
+	}
+	/* try again in case we've resolved to another symlink */
+	return resolve_symlink(p, s);
+}
+
 static int lock_file(struct lock_file *lk, const char *path)
 {
 	int fd;
-	sprintf(lk->filename, "%s.lock", path);
+	if (strlen(path) >= sizeof(lk->filename)) return -1;
+	strcpy(lk->filename, path);
+	/* subtract 5 from size to make sure there's room for adding
+	 * ".lock" for the lock file name */
+	resolve_symlink(lk->filename, sizeof(lk->filename)-5);
+	strcat(lk->filename, ".lock");
 	fd = open(lk->filename, O_RDWR | O_CREAT | O_EXCL, 0666);
 	if (0 <= fd) {
 		if (!lock_file_list) {
-- 
1.5.3.rc2.30.g1c06-dirty

-
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [Linux Kernel Development]     [Gcc Help]     [IETF Annouce]     [DCCP]     [Netdev]     [Networking]     [Security]     [V4L]     [Bugtraq]     [Yosemite]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux SCSI]     [Fedora Users]

  Powered by Linux