Ultrix-3.1/src/libc/net/rename.c
/**********************************************************************
* Copyright (c) Digital Equipment Corporation 1984, 1985, 1986. *
* All Rights Reserved. *
* Reference "/usr/src/COPYRIGHT" for applicable restrictions. *
**********************************************************************/
/*
* SCCSID: @(#)rename.c 3.0 4/22/86
*/
#include <sys/types.h>
#include <signal.h>
#include <sys/stat.h>
#include <sys/errno.h>
#include <sys/dir.h>
#include <stdio.h>
extern errno;
/*
* rename - change the name of a file.
* What a beast...
*/
rename(from_p, to_p)
char *from_p, *to_p;
{
char from[1024], to[1024];
struct stat fromstat, tostat, pfromstat;
register char *cp;
register int i;
int (*savesig[3])();
savesig[0] = signal(SIGHUP, SIG_IGN);
savesig[1] = signal(SIGINT, SIG_IGN);
savesig[2] = signal(SIGQUIT, SIG_IGN);
if (abspath(from_p, from, 1024) == NULL
|| abspath(to_p, to, 1024) == NULL) {
errno = ENAMETOOLONG;
return(-1);
}
/* Can we access this file? */
if (stat(from, &fromstat) < 0)
goto bad;
/* If it's a directory, are we root? */
if ((fromstat.st_mode & S_IFMT) == S_IFDIR) {
if (geteuid()) {
errno = EPERM;
goto bad;
}
/* if the source is "." or "..", we bomb out right away */
cp = rindex(from_p, '/');
if (cp == NULL)
cp = from_p;
else
cp++;
if ((cp[0] == '.') &&
((cp[1] == '\0') ||
((cp[1] == '.') && (cp[2] == '\0')))) {
errno = EPERM;
goto bad;
}
}
if (is_parent_writeable(from, &pfromstat) < 0)
goto bad;
if (is_parent_writeable(to, &tostat) < 0)
goto bad;
/* Are the source and destination on the same device? */
if (pfromstat.st_dev != tostat.st_dev) {
errno = EXDEV;
goto bad;
}
/* just make sure the "to" is not a "." or ".." entry... */
cp = rindex(to_p, '/');
if (cp == NULL)
cp = to_p;
if ((cp[0] == '.') &&
((cp[1] == '\0') ||
((cp[1] == '.') && (cp[2] == '\0')))) {
errno = EPERM;
goto bad;
}
/* does the "to file exist? "*/
if (stat(to, &tostat) == 0) {
if ((tostat.st_mode & S_IFMT) == S_IFDIR) {
if ((fromstat.st_mode & S_IFMT) != S_IFDIR) {
errno = EPERM;
goto bad;
}
if (rmdir(to) < 0)
goto bad;
} else if ((fromstat.st_mode & S_IFMT) == S_IFDIR) {
errno = EPERM;
goto bad;
} else if (unlink(to) < 0)
goto bad;
}
if ((fromstat.st_mode & S_IFMT) != S_IFDIR) {
if ((link(from, to) < 0) || (unlink(from) < 0))
goto bad;
goto done;
}
/* gag, we are re-naming a directory... */
if (isparent(from, to)) {
errno = EINVAL;
goto bad;
}
if (mvdir(from, to) < 0)
goto bad;
done:
signal(SIGHUP, savesig[0]);
signal(SIGINT, savesig[1]);
signal(SIGQUIT, savesig[2]);
return(0);
bad:
signal(SIGHUP, savesig[0]);
signal(SIGINT, savesig[1]);
signal(SIGQUIT, savesig[2]);
return(-1);
}
static is_parent_writeable(file, statbuf)
char *file;
register struct stat *statbuf;
{
register char *cp;
register int i;
if ((cp = rindex(file, '/')) == NULL) {
if (stat(".", statbuf) < 0)
return(-1);
} else {
*cp = '\0';
i = stat(file, statbuf);
*cp = '/';
if (i < 0)
return(-1);
}
if (geteuid()) {
i = S_IWRITE;
if (statbuf->st_uid != geteuid()) {
i >>= 3;
if (statbuf->st_gid != getegid())
i >>=3;
}
if ((statbuf->st_mode & i) == 0) {
errno = EACCES;
return(-1);
}
}
return(0);
}
static rmdir(dir)
register char *dir;
{
char buf[1024];
register char *p1, *p2;
if (isempty(dir) < 0)
return(-1);
if (strlen(dir) > 1020) {
errno = EINVAL;
return(-1);
}
for (p1 = buf, p2 = dir; *p2; )
*p1++ = *p2++;
*p1++ = '/';
*p1++ = '.';
*p1 = '\0';
if (unlink(buf) < 0)
return(-1);
*p1++ = '.';
*p1 = '\0';
if (unlink(buf) || unlink(dir))
return(-1);
}
static isempty(dir)
char *dir;
{
register int fd;
struct direct dent;
register struct direct *dp = &dent;
if ((fd = open(dir, 0)) < 0)
return(-1);
while (read(fd, (char *)dp, sizeof(*dp)) > 0) {
if (dp->d_ino == 0)
continue;
if ((dp->d_name[0] == '.') &&
((dp->d_name[1] == '\0') ||
((dp->d_name[1] == '.') && (dp->d_name[2] == '\0'))))
continue;
errno = EPERM;
close(fd);
return(-1);
}
close(fd);
return(0);
}
static mvdir(from, to)
char *from, *to;
{
register char *p1, *p2;
register val;
char c;
char buf[1024];
/* make new link, the remove the old */
if ((link(from, to) < 0) || (unlink(from) < 0))
return(-1);
/* fix ".." so that it points to the new director */
for (p1 = buf, p2 = to; *p2; )
*p1++ = *p2++;
*p1++ = '/';
*p1++ = '.';
*p1++ = '.';
*p1 = '\0';
p1 = rindex(to, '/');
if (p1 == to)
p1++;
if (p1) {
c = *p1;
*p1 = '\0';
}
if ((unlink(buf) < 0) || (link(to, buf) < 0))
val = -1;
else
val = 0;
if (p1)
*p1 = c;
return(val);
}
static abspath(path, buf, size)
char *path;
register char *buf;
int size;
{
register char *s, *p;
s = buf;
if (*path != '/') {
if (getcwd(buf, size) == NULL)
return(NULL);
while (*s)
s++;
}
for (p = path; *p; ) {
/* skip multiple slashes */
while (*p == '/')
p++;
if (p[0] == '.') {
if (p[1] == '/' || p[1] == '\0') {
/* . entries are ignored */
p++;
continue;
} else if (p[1] == '.' &&
(p[2] == '/' || p[2] == '\0')) {
/* .. entry, skip back in path */
p += 2;
while (s > buf && *--s != '/')
;
continue;
}
}
/* add the next path unit */
if (*p) {
*s++ = '/';
while (*p && *p != '/') {
*s++ = *p++;
if (s > &buf[size])
return(NULL);
}
}
}
if (s == buf)
*s++ = '/';
*s = '\0';
return(buf);
}
static isparent(from, to)
register char *from, *to;
{
while (*from++ == *to++)
;
if (*--from == '\0')
return(1);
return(0);
}