Skip to content

Commit

Permalink
fully resolve symlinks when creating lockfiles
Browse files Browse the repository at this point in the history
Make the code for resolving symlinks in lockfile.c more robust as
follows:

1. Handle relative symlinks
2. recursively resolve symlink chains up to 5

[jc: removed lstat/stat calls to do things stupid way]

Signed-off-by: Bradford C. Smith <bradford.carl.smith@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
  • Loading branch information
Bradford C. Smith authored and Junio C Hamano committed Jul 27, 2007
1 parent 7ab3cc7 commit 5d5a7a6
Showing 1 changed file with 102 additions and 14 deletions.
116 changes: 102 additions & 14 deletions lockfile.c
Original file line number Diff line number Diff line change
Expand Up @@ -25,23 +25,111 @@ 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)
{
/* r starts pointing to null at the end of the string */
char *r = strchr(p, '\0');

if (r == p)
return p; /* just return empty string */

r--; /* back up to last non-null character */

/* back up past trailing slashes, if any */
while (r > p && *r == '/')
r--;

/*
* then go backwards until I hit a slash, or the beginning of
* the string
*/
while (r > p && *(r-1) != '/')
r--;
return r;
}


/* We allow "recursive" symbolic links. Only within reason, though */
#define MAXDEPTH 5

/*
* p = path that may be a symlink
* s = full size of p
*
* If p is a symlink, attempt to overwrite p with a path to the real
* file or directory (which may or may not exist), following a chain of
* symlinks if necessary. Otherwise, leave p unmodified.
*
* This is a best-effort routine. If an error occurs, p will either be
* left unmodified or will name a different symlink in a symlink chain
* that started with p's initial contents.
*
* Always returns p.
*/

static char *resolve_symlink(char *p, size_t s)
{
int depth = MAXDEPTH;

while (depth--) {
char link[PATH_MAX];
int link_len = readlink(p, link, sizeof(link));
if (link_len < 0) {
/* not a symlink anymore */
return p;
}
else if (link_len < sizeof(link))
/* readlink() never null-terminates */
link[link_len] = '\0';
else {
warning("%s: symlink too long", p);
return p;
}

if (link[0] == '/') {
/* absolute path simply replaces p */
if (link_len < s)
strcpy(p, link);
else {
warning("%s: symlink too long", p);
return p;
}
} else {
/*
* link is a relative path, so I must replace the
* last element of p with it.
*/
char *r = (char*)last_path_elm(p);
if (r - p + link_len < s)
strcpy(r, link);
else {
warning("%s: symlink too long", p);
return p;
}
}
}
return p;
}


static int lock_file(struct lock_file *lk, const char *path)
{
int fd;
struct stat st;

if ((!lstat(path, &st)) && S_ISLNK(st.st_mode)) {
ssize_t sz;
static char target[PATH_MAX];
sz = readlink(path, target, sizeof(target));
if (sz < 0)
warning("Cannot readlink %s", path);
else if (target[0] != '/')
warning("Cannot lock target of relative symlink %s", path);
else
path = target;
}
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) {
Expand Down

0 comments on commit 5d5a7a6

Please sign in to comment.