This is the mail archive of the libc-alpha@sourceware.org mailing list for the glibc project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Proposal for a lrename() function


Although they is a useful rename(P) function in libc - often useful when
wanting to atomically copy a temporary file to a destination file - there
is nothing that will attempt to follow the destination if it
is a symbolic link.

That is, rename(P) *always* destroys any symbolic link destination.

This is a opten a real nuisance, because the reason people create
symbolic links in the first place is not to have them periodically
destroyed but to have them preserved and followed.

The following is a function I have named lrename() (for want of
any other name .... but might not be particularly appropriate
given that only destination is treated differently).

Please give it your consideration.

=====================================================================
#include <stdio.h>
#include <unistd.h>
#include <limits.h>
#include <sys/stat.h>

/*
 * Simple rename(P) alternative that attempts to rename to symlink
 * target. If symlink target is on different device, or resolving target
 * fails for any reason, then falls back to POSIX rename().
 *
 * This function helps preserve symbolic link hierarchies when
 * attempting to do atomic file replacement.
 */

int
lrename(const char *old, const char *new)
{
    const char *new0 = new;
    char *target = NULL;
    size_t targetlen = 0;
    int res;
#if defined(S_ISLNK)
    struct stat sb = {0};
#if defined(_POSIX_SYMLOOP_MAX)
    int maxloops = _POSIX_SYMLOOP_MAX;
#elif defined(_SYMLOOP_MAX)
    int maxloops = _SYMLOOP_MAX;
#else
    int maxloops = sysconf(_SC_SYMLOOP_MAX);
    if (maxloops <= 0)
	maxloops = 8;
#endif
    while (maxloops-- > 0 && lstat(new, &sb) == 0 && S_ISLNK(sb.st_mode))
    {
	size_t len;
	size_t bufflen = sb.st_size + 1;
	char *buff = malloc(bufflen);
	if (!buff)
	{
	    new = new0;
	    break;
	}
	len = readlink(new, buff, bufflen - 1);
	if (target)	// aka new
	{
	    free(target);
	    target = NULL;
	}
	target = buff;
	targetlen = bufflen;
	if (len == -1)
	{
	    // could be a dangling link
	    new = new0;
	    break;
	}
	else
	{
	    target[len] = '\0';
	    new = target;
	}
    }
#endif
    res = rename(old, new);
    if (res != 0 && new == target)
	res = rename(old, new0);
    if (target)
    {
	free(target);
	target = NULL;
    }
    return res;
}
=====================================================================

Regards
JW


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]