getpreroot.c revision 7c2fbfb345896881c631598ee3852ce9ce33fb07
/***********************************************************************
* *
* This software is part of the ast package *
* Copyright (c) 1985-2008 AT&T Intellectual Property *
* and is licensed under the *
* Common Public License, Version 1.0 *
* by AT&T Intellectual Property *
* *
* A copy of the License is available at *
* http://www.opensource.org/licenses/cpl1.0.txt *
* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) *
* *
* Information and Software Systems Research *
* AT&T Research *
* Florham Park NJ *
* *
* Glenn Fowler <gsf@research.att.com> *
* David Korn <dgk@research.att.com> *
* Phong Vo <kpv@research.att.com> *
* *
***********************************************************************/
#pragma prototyped
/*
* AT&T Bell Laboratories
* return the real absolute pathname of the preroot dir for cmd
* if cmd==0 then current preroot path returned
*/
#include <ast.h>
#include <preroot.h>
#if FS_PREROOT
#include <ast_dir.h>
#include <ls.h>
#include <error.h>
#include <stdio.h>
#ifndef ERANGE
#define ERANGE E2BIG
#endif
#define ERROR(e) {errno=e;goto error;}
char*
getpreroot(char* path, const char* cmd)
{
register int c;
register FILE* fp;
register char* p;
char buf[PATH_MAX];
if (!path) path = buf;
if (cmd)
{
sfsprintf(buf, sizeof(buf), "set x `%s= %s - </dev/null 2>&1`\nwhile :\ndo\nshift\ncase $# in\n[012]) break ;;\nesac\ncase \"$1 $2\" in\n\"+ %s\") echo $3; exit ;;\nesac\ndone\necho\n", PR_SILENT, cmd, PR_COMMAND);
if (!(fp = popen(buf, "rug"))) return(0);
for (p = path; (c = getc(fp)) != EOF && c != '\n'; *p++ = c);
*p = 0;
pclose(fp);
if (path == p) return(0);
return(path == buf ? strdup(path) : path);
}
else
{
char* d;
DIR* dirp = 0;
int namlen;
int euid;
int ruid;
struct dirent* entry;
struct stat* cur;
struct stat* par;
struct stat* tmp;
struct stat curst;
struct stat parst;
struct stat tstst;
char dots[PATH_MAX];
cur = &curst;
par = &parst;
if ((ruid = getuid()) != (euid = geteuid())) setuid(ruid);
if (stat(PR_REAL, cur) || stat("/", par) || cur->st_dev == par->st_dev && cur->st_ino == par->st_ino) ERROR(ENOTDIR);
/*
* like getcwd() but starting at the preroot
*/
d = dots;
*d++ = '/';
p = path + PATH_MAX - 1;
*p = 0;
for (;;)
{
tmp = cur;
cur = par;
par = tmp;
if ((d - dots) > (PATH_MAX - 4)) ERROR(ERANGE);
*d++ = '.';
*d++ = '.';
*d = 0;
if (!(dirp = opendir(dots))) ERROR(errno);
#if !_dir_ok || _mem_dd_fd_DIR
if (fstat(dirp->dd_fd, par)) ERROR(errno);
#else
if (stat(dots, par)) ERROR(errno);
#endif
*d++ = '/';
if (par->st_dev == cur->st_dev)
{
if (par->st_ino == cur->st_ino)
{
closedir(dirp);
*--p = '/';
if (ruid != euid) setuid(euid);
if (path == buf) return(strdup(p));
if (path != p)
{
d = path;
while (*d++ = *p++);
}
return(path);
}
#ifdef D_FILENO
while (entry = readdir(dirp))
if (D_FILENO(entry) == cur->st_ino)
{
namlen = D_NAMLEN(entry);
goto found;
}
#endif
/*
* this fallthrough handles logical naming
*/
rewinddir(dirp);
}
do
{
if (!(entry = readdir(dirp))) ERROR(ENOENT);
namlen = D_NAMLEN(entry);
if ((d - dots) > (PATH_MAX - 1 - namlen)) ERROR(ERANGE);
memcpy(d, entry->d_name, namlen + 1);
if (stat(dots, &tstst)) ERROR(errno);
} while (tstst.st_ino != cur->st_ino || tstst.st_dev != cur->st_dev);
found:
if (*p) *--p = '/';
if ((p -= namlen) <= (path + 1)) ERROR(ERANGE);
memcpy(p, entry->d_name, namlen);
closedir(dirp);
dirp = 0;
}
error:
if (dirp) closedir(dirp);
if (ruid != euid) setuid(euid);
}
return(0);
}
#else
NoN(getpreroot)
#endif