archie/prospero/lib/pfs/rd_vdir.c
2024-05-27 16:13:40 +02:00

641 lines
23 KiB
C

/*
* Copyright (c) 1992, 1993 by the University of Southern California
*
* For copying and distribution information, please see the file
* <usc-license.h>
*/
#include <usc-license.h>
#include <stdio.h>
#include <string.h>
#include <pfs.h>
#include <perrno.h>
#include <pmachine.h>
static TOKEN p__rvdslashpath2tkl(char *nextcomp);
static void p__tkl_back_2rvdslashpath(TOKEN nextcomp_tkl, char *nextcomp);
/*
*/
int
rd_vdir(dirarg,comparg,dir,flags)
const char *dirarg; /* Virtual name for the directory */
const char *comparg; /* Component name (wildcards allowed) */
VDIR dir; /* Structure to be filled in */
long flags; /* Flags */
{
/* Note: We use a working copy of dirarg because we want to */
/* modify the working copy, and we would prefer not to */
/* modify the directory name passed to this procedure. */
/* dirnm is required because we need a character pointer */
/* later on (dirname), but the compiler only allows */
/* automatic allocation for a character array. */
/* components is a working copy of comparg which might be */
/* modified if a magic number has been specified. */
char dirnm[MAX_VPATH]; /* Working copy of dirarg */
char *dirname = dirnm; /* Pointer to dirnm */
char components[MAX_VPATH];/* Working copy of comparg */
char *dirhst; /* Host of current directory */
char *remdir; /* Dir on remote host */
char *homedir; /* Name of home directory */
char *workdir; /* Name of working directory */
int hdlen; /* Length of homedir name */
int wdlen; /* Length of workdir name */
char relativeto; /* 0 = name relative to root */
FILTER filters = NULL; /* Filters to be applied */
char *ndname; /* Next component of dirname */
char *magic_str; /* Magic number part of ndname */
int magic_no; /* Magic number from magic_str */
int poundsign; /* Flag, nextcomp contains # */
char wdname[MAX_VPATH]; /* Working directory name */
VLINK ltmp; /* To look for unexpanded links */
PATTRIB closure; /* Namespace from closed object */
int retval; /* Return value */
int depth; /* Depth for sym-link expansion */
char stmp[MAX_VPATH]; /* Temporary string */
char *colon; /* Pointer to colon in path */
char *nsname; /* Possible name of new ns */
int beyondcolon; /* Length of text after colon */
char *nextcomp; /* 4 steppng through components */
long intflags; /* Flags for intermediate query */
depth = SYMLINK_NESTING;
strcpy(dirname,dirarg);
strcpy(components,(comparg ? comparg : ""));
nsname = dirname;
colon = p_uln_index(dirname,':');
if(colon) {
/* Name can not start with a ":" */
if(colon == nsname) return(RVD_NS_NOT_FOUND);
/* Don't support name space aliases yet, so return an error */
/* if the colon is preceded by a "#". */
if(*(colon-1) == '#') return(RVD_NO_NS_ALIAS);
}
/* In most cases, we start from a specific directory */
/* and don't care about the full pathname from root */
/* We only want to deal with the pathname for the root */
/* directory if the name starts with ../'s or ~/../'s */
*wdname = '\0';
if(*dirname == '\0') {
relativeto = 2;
dirhst = pget_wdhost();
remdir = pget_wdfile();
}
else if(*dirname == '/') {
relativeto = 0;
dirhst = pget_rdhost();
remdir = pget_rdfile();
dirname++;
}
else if((strncmp(dirname,"~/",2) == 0)||(strcmp(dirname,"~") == 0)) {
/* Set wdname to homedir and later check for ../'s */
relativeto = 1;
strcpy(wdname,pget_hd());
dirhst = pget_hdhost();
remdir = pget_hdfile();
dirname++;
/* We only want to step over the next byte if it is not the */
/* end of the string. It will be if string was ~ */
if(*dirname) dirname++;
}
else {
/* Set wdname and later check for ../'s */
relativeto = 2;
workdir = pget_wd();
if(workdir) strcpy(wdname,workdir);
else return(PFS_ENV_NOT_INITIALIZED);
dirhst = pget_wdhost();
remdir = pget_wdfile();
}
/* If Prospero environment has not been initialized return error */
if((dirhst == NULL) || (*dirhst == '\0'))
return(PFS_ENV_NOT_INITIALIZED);
/* If we still allow ../'s and the dirname starts */
/* as such, determine the correct name for the new */
/* file relative to VSROOT, and use that instead */
if(*wdname && (strncmp(dirname,"..",2) == 0)) {
char *slash;
relativeto = 0;
dirhst = pget_rdhost();
remdir = pget_rdfile();
while((strncmp(dirname,"../",3) == 0) ||
(strcmp(dirname,"..") == 0)) {
dirname += 2;
if(*dirname == '/') dirname++;
slash = p_uln_rindex(wdname,'/');
colon = p_uln_rindex(wdname,':');
if(slash && (!colon || (slash > colon))) *slash = '\0';
else if(colon) *(colon+1) = '\0';
else *wdname = '\0';
if(!*wdname) strcpy(wdname,"/");
}
/* Remove a ./ if it caused termination of ../s */
if(strncmp(dirname,"./",2) == 0) {
dirname++;
dirname++;
}
if(*dirname && (*(wdname + strlen(wdname)-1) != '/'))
strcat(wdname,"/");
strcat(wdname,dirname);
dirname = wdname;
}
/* Special case "." if appears by itself */
if(strcmp(dirname,".") == 0) {
dirname++;
}
/* And finally, remove a ./ if it appears at start */
else if(strncmp(dirname,"./",2) == 0) {
dirname++;
dirname++;
}
/* Check to see if working or home directory is a */
/* prefix of the directory to be listed */
if(! (flags & RVD_NOCACHE) ) {
workdir = pget_wd();
homedir = pget_hd();
if((relativeto == 0) && (*dirname != '/')) {
workdir++;
homedir++;
}
wdlen = strlen(workdir);
hdlen = strlen(homedir);
/* Do not cache if remaining part of name has any */
/* single colons. Right now the check disables caching */
/* if it finds any colons. It should allow it if it is */
/* a double colon. */
if((strncmp(dirname,workdir,wdlen) == 0) &&
((hdlen <= wdlen) || (strncmp(dirname,homedir,hdlen) != 0)) &&
((*(dirname+wdlen) == '/') || (*(dirname+wdlen) == '\0'))&&
(p_uln_index(dirname+wdlen,':') == 0)) {
dirname = dirname + wdlen;
if(*dirname == '/') dirname++;
relativeto = 2;
dirhst = pget_wdhost();
remdir = pget_wdfile();
}
else if((strncmp(dirname,homedir,hdlen) == 0) &&
((*(dirname+hdlen) == '/') || (*(dirname+hdlen) == '\0'))&&
(p_uln_index(dirname+hdlen,':') == 0)) {
dirname = dirname + hdlen;
if(*dirname == '/') dirname++;
relativeto = 1;
dirhst = pget_hdhost();
remdir = pget_hdfile();
}
}
else flags &= (~ RVD_NOCACHE);
startover:
/* The target of a symbolic link must be an absolute path */
/* from the root of the specified virtual system. Thus */
/* if chasing a link, we skip the code above that checks */
/* for ../s, and ~/s. */
nsname = dirname;
/* If the directory includes :s, find correct namespace */
while(colon = p_uln_index(dirname,':')) {
/* If the vs name is null, return an error */
if(colon == nsname) return(RVD_NS_NOT_FOUND);
/* If an alias, return error. Aliases are only allowed for */
/* the first : and if an alias, it was resolved previously */
if(*(colon-1) == '#') return(RVD_NO_NS_ALIAS);
/* object closure */
if(*(colon+1) == ':') {
*(colon++) = '\0';
colon++;
ltmp = rd_vlink(dirname);
if(!ltmp) return(RVD_NO_CLOSED_NS);
closure = pget_at(ltmp,"CLOSURE");
vllfree(ltmp);
if(!closure) return(RVD_NO_CLOSED_NS);
/* XXX This must change! */
if (closure->avtype != ATR_SEQUENCE) {
atlfree(closure);
return(RVD_NO_CLOSED_NS);
}
if (qsprintf(stmp,sizeof stmp,
"%s:%s",closure->value.sequence->token,colon) >
sizeof stmp)
internal_error("stmp too small!");
atlfree(closure);
strcpy(dirnm,stmp);
dirname = dirnm;
nsname = dirname;
continue;
}
/* text between nsname and colon is name space */
*(colon++) = '\0';
if (sizeof stmp <
qsprintf(stmp, sizeof stmp,
"/VIRTUAL-SYSTEMS/%s/ROOT%s%s",nsname,
((!*colon || (*colon == '/')) ? "" : "/"),colon))
internal_error("stmp too small!");
/* Remember how much text after colon before we overwite dirname */
beyondcolon = strlen(colon);
/* Keep part of dirname that defines root of old namespace */
if((nsname > dirname) && (*(nsname-1) == '/')) *(nsname-1) = '\0';
else *nsname = '\0';
/* And append path to root of new one */
strcat(dirname,stmp);
/* Keep track of the root of the new name space */
nsname = dirname + strlen(dirname) - beyondcolon;
/* Start search from root of active name space */
relativeto = 0;
dirhst = pget_rdhost();
remdir = pget_rdfile();
}
ndname = dirname;
while(*ndname == '/') ndname++; /* multiple slashes treated as one slash */
nextcomp = p_uln_index(ndname,'/');
if(nextcomp) {
*(nextcomp++) = '\0';
if(!*nextcomp) nextcomp = NULL; /* trailing slash ignored. */
}
if(!*ndname) ndname = NULL;
/* If we only want the directory file, and we have a null */
/* dirname (ndname is first component), then we must create */
/* a fictitious directory entry from dirhst and remdir */
if(!ndname && (flags & RVD_DFILE_ONLY)) {
VLINK vl;
char *tmp_wdname = pget_wd(); /* working directory. */
char *s; /* link name */
/* If links remain in dir, free them */
vdir_freelinks(dir);
/* Allocate the pseudo link */
dir->links = vl = vlalloc();
if (!vl) out_of_memory();
/* and fill it in */
vl->host = stcopy(dirhst);
vl->hsoname = stcopy(remdir);
vl->target = stcopyr("DIRECTORY", vl->target);
/* Set a NAME field of some sort. */
if (tmp_wdname && *tmp_wdname) {
char *sp = p_uln_rindex(tmp_wdname, '/'); /* slashptr */
char *cp = p_uln_rindex(tmp_wdname, ':'); /* colonptr */
char *lastwdcomp; /* last comp in tmp_wdname */
s = ((cp < sp) ? sp : cp) + 1;
if (s && *s) vl->name = stcopyr(s, vl->name);
else vl->name = stcopyr(tmp_wdname, vl->name);
} else if (s = strrchr(remdir, '/')) {
vl->name = stcopyr(s, vl->name);
} else {
vl->name = stcopyr(remdir, vl->name);
}
}
while(ndname) {
TOKEN nextcomp_tkl; /* Token list form of nextcomp variable;
a temporary var. */
TOKEN thiscomp_tkl; /* used to convert quoted component strings to
normal format. */
VLINK dlink = vlalloc();
dlink->host = stcopyr(dirhst, dlink->host);
dlink->hsoname = stcopyr(remdir, dlink->hsoname);
dlink->filters = flcopy(filters, 1);
/* Check for magic number */
magic_no = 0;
magic_str = p_uln_rindex(ndname,'#');
/* Make sure that the rest of magic str is digits */
if(magic_str && *(magic_str + 1) &&
(strspn(magic_str+1,"-0123456789") == strlen(magic_str+1))) {
*(magic_str++) = '\0';
sscanf(magic_str,"%d",&magic_no);
}
/* Find out if there is a # in any remaining components */
/* and if so, don't reolve multiple components */
if(nextcomp && p_uln_index(nextcomp,'#')) poundsign = 1;
else poundsign = 0;
intflags = (magic_no ? GVD_LREMEXP : GVD_FIND);
if((*ndname == '#')&&(*(ndname+1))) {
intflags = GVD_UNION;
poundsign = 1;
}
/* if((flags & RVD_DFILE_ONLY) && (flags & RVD_ATTRIB)) */
/* intflags |= GVD_ATTRIB; */
/* It might not be a directory in which case we want the */
/* attributes */
if(flags & RVD_ATTRIB) intflags |= GVD_ATTRIB;
if ((*ndname != '#') || (!*(ndname + 1))) {
thiscomp_tkl = p__rvdslashpath2tkl(ndname);
assert(thiscomp_tkl && !thiscomp_tkl->next);
} else
thiscomp_tkl = NULL;
if (!magic_no && !poundsign) {
nextcomp_tkl = p__rvdslashpath2tkl(nextcomp);
retval = p_get_dir(dlink,
thiscomp_tkl? thiscomp_tkl->token : (char *)NULL,
dir, intflags, &nextcomp_tkl);
p__tkl_back_2rvdslashpath(nextcomp_tkl, nextcomp);
tklfree(nextcomp_tkl);
} else {
retval = p_get_dir(dlink,
thiscomp_tkl? thiscomp_tkl->token : (char *)NULL,
dir, intflags, NULL);
}
if (thiscomp_tkl) tklfree(thiscomp_tkl);
if(retval) {
vlfree(dlink);
if (retval == DIRSRV_NOT_DIRECTORY) {
p_err_string = qsprintf_stcopyr(p_err_string,
"Directory %s not found.",ndname);
return(PFS_DIR_NOT_FOUND);
}
else return(retval);
}
if(*ndname == '#' && *(ndname+1)) {
vllfree(dir->links);
dir->links = dir->ulinks;
dir->ulinks = NULL;
while(dir->links && (strcmp(dir->links->name,ndname+1) != 0)) {
ltmp = dir->links;
dir->links = dir->links->next;
vlfree(ltmp);
}
}
/* Find the one with the correct magic number. */
/* We can get rid of those that don't match */
if(dir->links && magic_no && (magic_no != dir->links->f_magic_no)){
ltmp = dir->links->replicas;
while(ltmp && (ltmp->f_magic_no != magic_no)) {
dir->links->replicas = ltmp->next;
vlfree(ltmp);
ltmp = dir->links->replicas;
}
/* found it, replace primary link */
if(ltmp) {
ltmp->replicas = ltmp->next;
if(ltmp->replicas) ltmp->replicas->previous = NULL;
ltmp->next = dir->links->next;
ltmp->previous = dir->links->previous;
dir->links->replicas = NULL;
vlfree(dir->links);
dir->links = ltmp;
}
/* Otherwise get rid of primary link */
else {
ltmp = dir->links;
dir->links = ltmp->next;
vlfree(ltmp);
dir->links->previous = NULL;
}
}
/* Can't find next directory link */
if(dir->links == NULL) {
p_err_string = qsprintf_stcopyr(p_err_string,
"Directory %s not found.",ndname);
/* If unexpanded ulinks, return temporary error */
ltmp = dir->ulinks;
while(ltmp) {
if(ltmp->expanded != TRUE) {
vlfree(dlink);
return(RVD_DIR_NOT_THERE);
}
ltmp = ltmp->next;
}
vlfree(dlink);
return(PFS_DIR_NOT_FOUND);
}
/* If a symbolic link, expand it */
if((strncmp(dir->links->target,"SYMBOLIC",8) == 0) &&
(strncmp(dir->links->hosttype,"VIRTUAL-SYSTEM",14) == 0)) {
if(depth-- > 0) {
sprintf(stmp,"%s:%s",dir->links->host,
dir->links->hsoname);
if(nextcomp && !*nextcomp) nextcomp = NULL;
while(ndname = nextcomp) {
nextcomp = p_uln_index(ndname,'/');
if(nextcomp) {
*(nextcomp++) = '\0';
if(*nextcomp == '\0') nextcomp = NULL;
}
strcat(stmp,"/");
strcat(stmp,ndname);
}
strcpy(dirnm,stmp);
dirname = dirnm;
vlfree(dlink); /* Goto's considered harmfull! */
goto startover;
}
else {
vlfree(dlink);
return(PFS_SYMLINK_DEPTH);
}
/* NOTREACHED */
assert(0); /* should never get here. */
}
/* found next directory - update dirhost and remdir - continue */
dirhst = dir->links->host;
remdir = dir->links->hsoname;
filters = dir->links->filters;
/* remove the next line */
dir->links->filters = NULL;
/* Find the next component of the path name */
if(nextcomp && !*nextcomp) nextcomp = NULL;
ndname = nextcomp;
if(ndname) {
nextcomp = p_uln_index(ndname,'/');
if(nextcomp) {
*(nextcomp++) = '\0';
if(*nextcomp == '\0') nextcomp = NULL;
}
}
/* If more components, and not a real link, then */
/* return an error */
if (ndname && (strcmp(dir->links->target,"DIRECTORY") != 0) &&
(strcmp(dir->links->target,"FILE") != 0)) {
/* Ooops - Release does vlfree but not return, good
reason to bracket EVERY if - Mitra */
vlfree(dlink);
return (PFS_EXT_USED_AS_DIR);
}
vlfree(dlink); /* For drop-thru or loop */
} /*while */
/* if magic number specified, chop it off before making request */
magic_no = 0;
magic_str = p_uln_rindex(components,'#');
/* Make sure that the rest of magic str is digits */
if(magic_str && *(magic_str + 1) &&
(strspn(magic_str+1,"-0123456789") == strlen(magic_str+1))) {
*(magic_str++) = '\0';
sscanf(magic_str,"%d",&magic_no);
/* If alternate version specified, don't stop looking */
if(flags & GVD_FIND) flags = (flags & (~GVD_FIND)) | GVD_LREMEXP;
}
/* If a normal link, then we do a listing of the */
/* directory. If not a directory, then get_vidr */
/* will fail We will pass through the error code */
/* Dir will contain the single directory link */
/* corresponding to the file */
if(!dir->links || (strcmp(dir->links->target,"DIRECTORY") == 0) ||
(strcmp(dir->links->target,"FILE") == 0)) {
VLINK dlink = vlalloc();
dlink->host = stcopyr(dirhst, dlink->host);
dlink->hsoname = stcopyr(remdir, dlink->hsoname);
dlink->filters = flcopy(filters, 1);
retval = p_get_dir(dlink,components,dir,flags, NULL);
/* Find the one with the correct magic number. */
/* We can get rid of those that don't match */
/* This code is not applicable for all flags combos */
if(dir->links && magic_no && (magic_no != dir->links->f_magic_no)){
ltmp = dir->links->replicas;
while(ltmp && (ltmp->f_magic_no != magic_no)) {
dir->links->replicas = ltmp->next;
vlfree(ltmp);
ltmp = dir->links->replicas;
}
/* found it, replace primary link */
if(ltmp) {
ltmp->replicas = ltmp->next;
if(ltmp->replicas) ltmp->replicas->previous = NULL;
ltmp->next = dir->links->next;
ltmp->previous = dir->links->previous;
dir->links->replicas = NULL;
vlfree(dir->links);
dir->links = ltmp;
}
/* Otherwise get rid of primary link */
else {
ltmp = dir->links;
dir->links = ltmp->next;
vlfree(ltmp);
dir->links->previous = NULL;
}
}
vlfree(dlink);
return(retval);
}
else return(DIRSRV_NOT_DIRECTORY);
}
/* These are not thread safe */
static int oldpathlen;
#ifndef NDEBUG /* for assertions */
static char *oldnextcomp;
#endif
static
TOKEN
p__rvdslashpath2tkl(char *nextcomp)
{
TOKEN retval = NULL;
char *cp;
char thiscomp[MAX_VPATH]; /* buffer for current comp. strip backslashes.
*/
char *thisp = thiscomp;
assert(P_IS_THIS_THREAD_MASTER());
oldpathlen = 0;
#ifndef NDEBUG
oldnextcomp = nextcomp;
#endif
if (!nextcomp) return NULL;
for (cp = nextcomp; *cp; cp++) {
if (*cp == '/') {
*thisp = '\0';
/* Skip multiple slashes (as rd_vdir() does.) */
if (thisp > thiscomp) {
++oldpathlen;
retval = tkappend(thiscomp, retval);
thisp = thiscomp; /* set for next */
}
continue;
} else if (*cp == '\\') {
++cp;
if (*cp == '\0') --cp; /* special case trailing \; treat it as a
literal backslash. */
}
*thisp++ = *cp; /* process quoted and literal characters */
}
/* Handle the last component. Skip trailing slashes. (as rd_vdir does). */
*thisp = '\0';
if (thisp > thiscomp) {
++oldpathlen;
retval = tkappend(thiscomp, retval);
}
return retval;
}
/* Should only be called when p__rvdslashpath2tkl was called. */
static
void
p__tkl_back_2rvdslashpath(TOKEN nextcomp_tkl, char *nextcomp)
{
char buf[MAX_VPATH];
/* Make sure the functions were called in the proper order. This assertion
helps make sure we don't write too much back into the old nextcomp
buffer. */
assert(P_IS_THIS_THREAD_MASTER());
assert(oldnextcomp == nextcomp);
*buf = '\0';
/* Optimize common case (no change). */
if (oldpathlen == length(nextcomp_tkl))
return;
assert(oldpathlen > length(nextcomp_tkl)); /* sanity */
for(; nextcomp_tkl; nextcomp_tkl = nextcomp_tkl->next) {
strcat(buf, nextcomp_tkl->token);
strcat(buf, "/");
}
assert(strlen(buf) <= strlen(nextcomp));
strcpy(nextcomp, buf);
}