Proposal for a lrename() function
fedora@zacglen.com.au
fedora@zacglen.com.au
Sun Mar 5 02:46:00 GMT 2006
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
More information about the Libc-alpha
mailing list