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

655 lines
24 KiB
C

/*
* Copyright (c) 1992, 1993 by the University of Southern California
*
* For copying and distribution information, please see the file
* <usc-license.h>
*
* Written by bcn 1989 modified 1989-1992
* Modified by swa 1992 to use qsscanf()
* Modified by bcn 1/93 to use ardp library
* bug fix by cheang 11/93 pre server filters.
*/
#include <usc-license.h>
#include <stdio.h>
#include <string.h>
#include <pprot.h>
#include <ardp.h>
#include <pfs.h>
#include <perrno.h>
#include <pparse.h>
#include <pmachine.h>
extern int pfs_debug;
extern TOKEN p__tokenize_midstyle_mcomp(char *nextname);
static void build_out(VDIR dir, OUTPUT out, char *options, FILTER *ptr_filters) {
/* Build dir->dqs->preq and out */
/* Note side effect, filters updated to point to first client filter */
struct dqstate *dqs = dir->dqs;
FILTER filters = *ptr_filters;
dir->status = DQ_ACTIVE;
dqs->preq = p__start_req(dqs->dl->host);
requesttoout(dqs->preq,out); /* two ways to output to this. */
if(dqs->flags & GVD_MOTD) p__add_req(dqs->preq, "PARAMETER GET MOTD\n");
if(dqs->flags & GVD_MOTD_O) return; /* Skip over query */
/* The Server FILTER option should be applied only once before */
/* union link expansion. */
if(filters && filters->execution_location == FIL_SERVER) {
static char * fil_option = NULL;
assert(P_IS_THIS_THREAD_MASTER()); /* Not thread safe yet */
fil_option = qsprintf_stcopyr(fil_option, "%s+FILTER", options);
p__add_req(dqs->preq, "DIRECTORY ASCII %'s\nLIST %s COMPONENTS",
dqs->dl->hsoname, fil_option + 1);
}
else
p__add_req(dqs->preq, "DIRECTORY ASCII %'s\nLIST %s COMPONENTS",
dqs->dl->hsoname,
(*options) ? options+1 : "''" );
if(dqs->comp) {
p__add_req(dqs->preq, " %'s", dqs->comp);
if (dqs->mcomp) {
#ifdef CLIENT_SEND_NEW_MCOMP
out_sequence(out, *dqs->acomp); /* terminates with a \n. */
#else
TOKEN acp;
for (acp = *dqs->acomp; acp; acp = acp->next) {
if(!(acp->token) || (!*(acp->token)))
p__add_req(dqs->preq, "/*", acp->token);
else p__add_req(dqs->preq, "/%'s", acp->token);
}
p__add_req(dqs->preq, "\n");
#endif
} else {
p__add_req(dqs->preq, "\n");
}
} else {
p__add_req(dqs->preq, "\n");
}
/* Append any appropriate filters to the request. */
while (filters && filters->execution_location == FIL_SERVER
&& (filters->type == FIL_DIRECTORY
|| filters->type == FIL_HIERARCHY)) {
qoprintf(out, "FILTER");
out_filter(out, filters, 0);
filters = filters->next;
}
}
static int
retrieve_and_parse( VDIR dir)
{
/* Retrieve and parse a ardp response, */
INPUT_ST in_st; /* Input pointer for response contents */
INPUT in = &in_st;
struct dqstate *dqs = dir->dqs;
CHECK_MEM();
rreqtoin(dqs->preq, in);
while(!in_eof(in)) {
int tmp;
char *line;
char *next_word;
CHECK_MEM();
if (tmp = in_line(in, &line, &next_word))
return(tmp);
switch (*line) {
char l_htype[MAX_DIR_LINESIZE];
char l_host[MAX_DIR_LINESIZE];
char l_ntype[MAX_DIR_LINESIZE];
char l_fname[MAX_DIR_LINESIZE];
int l_version;
int l_magic;
char t_unresolved[MAX_DIR_LINESIZE];
case 'A': /* ATTRIBUTE lines that apply to this
directory. */
if (!strnequal(line, "ATTRIBUTE", sizeof "ATTRIBUTE" - 1))
goto scanerr;
if (tmp = in_ge1_atrs(in, line, next_word, &dir->attributes)) {
if (pfs_debug)
perrmesg("p_get_dir()", 0, NULL);
return(tmp);
}
case 'L': /* LINK or LINK-INFO */
if(!strnequal(line,"LINK",4))
goto scanerr;
/* If only verifying, don't want to change dir */
if(dqs->flags & GVD_VERIFY) {
break;
}
/* If first link and some links in dir, free them */
if(!dqs->newlinks++) {
if(dir->links) vllfree(dir->links); dir->links=NULL;
if(dir->ulinks) vllfree(dir->ulinks); dir->ulinks=NULL;
}
CHECK_MEM();
if (tmp = in_link(in,line,next_word,0,&(dqs->clnk),
(TOKEN *) NULL)) {
if (pfs_debug)
perrmesg("p_get_dir()", 0, NULL);
return(DIRSRV_BAD_FORMAT);
}
/* Double check to make sure we don't get */
/* back unwanted components */
/* OK to keep if special (URP) links */
/* or if dqs->mcomp specified */
if(!dqs->mcomp &&
((dqs->clnk->linktype == 'L') ||
(dqs->clnk->linktype == 'I')) && dqs->comp
&& (!wcmatch(dqs->clnk->name,dqs->comp))) {
vlfree(dqs->clnk); dqs->clnk = NULL;
break;
}
/* If other optional info was sent back, it must */
/* also be parsed before inserting link *** */
if(dqs->clnk->linktype == 'L' || dqs->clnk->linktype == 'I')
vl_insert(dqs->clnk,dir,
(dqs->flags & GVD_NOSORT) ? VLI_NOSORT : VLI_ALLOW_CONF
);
else {
tmp = ul_insert(dqs->clnk,dir,dqs->pulnk);
/* If inserted after dqs->pulnk, next one after dqs->clnk*/
if(dqs->pulnk && (!tmp || (tmp == UL_INSERT_SUPERSEDING)))
dqs->pulnk = dqs->clnk;
}
break;
case 'F': /* FAILURE or FORWARDED */
/* FORWARDED */
if(strncmp(line,"FORWARDED",9) == 0) {
if((dqs->fwdcnt)-- <= 0) {
perrno = PFS_MAX_FWD_DEPTH;
if (pfs_debug)
perrmesg("p_get_dir()", 0, NULL);
return(perrno);
}
/* parse and start over */
tmp = qsscanf(line,"FORWARDED %s %'s %s %'s %d %d",
l_htype,l_host,l_ntype,l_fname,
&l_version, &l_magic);
dqs->dl->host = stcopyr(l_host, dqs->dl->host);
dqs->dl->hsoname = stcopyr(l_fname, dqs->dl->hsoname);
if(tmp < 4) {
if (pfs_debug)
fprintf(stderr,
"p_get_dir(): Malformed FORWARDED line: %s",
line);
return(DIRSRV_BAD_FORMAT);
}
ardp_rqfree(dqs->preq);
dqs->preq = NOREQ;
dir->status = DQ_INACTIVE;
return(DSRFINFO_FORWARDED);
}
goto scanerr;
break;
case 'N': /* NONE-FOUND */
/* NONE-FOUND, we just have no links to insert */
/* It is not an error, but we must clear any */
/* old links in the directory arg */
if(strncmp(line,"NONE-FOUND",10) == 0) {
/* If only verifying, don't want to change dir */
if(dqs->flags & GVD_VERIFY) {
break;
}
/* If first link and some links in dir, free them */
if(!dqs->newlinks++) {
if(dir->links) vllfree(dir->links);
if(dir->ulinks) vllfree(dir->ulinks);
dir->links = NULL;
dir->ulinks = NULL;
}
break;
}
/* If anything else, scan error */
goto scanerr;
case 'U': /* UNRESOLVED */ {
char *p_unresolved;
TOKEN new_acomp;
int newlen, oldlen;
tmp = qsscanf(line,"UNRESOLVED %r", &p_unresolved);
if(tmp < 1) goto scanerr;
#ifndef DONT_ACCEPT_OLD_MCOMP
new_acomp = p__tokenize_midstyle_mcomp(p_unresolved);
#else /* guaranteed a new reply from server */
new_acomp = qtokenize(p_unresolved);
#endif
newlen = length(new_acomp);
oldlen = length(*dqs->acomp);
/* If multiple components were resolved */
/* Must be a proper suffix. */
if (newlen != oldlen) {
if (oldlen <= newlen) goto badunresolved;
/* Comp gets the last component resolved. (NB: Indexing
for elt() starts at 0.) */
dqs->comp = stcopy(elt(*dqs->acomp, oldlen - newlen - 1));
dqs->stfree_comp = 1;
/* Let rd_vdir know what remains */
tklfree(*dqs->acomp);
*dqs->acomp = new_acomp;
}
dqs->unresp = 1;
break;
badunresolved:
if (pfs_debug)
fprintf(stderr, "p_get_dir(): Received an UNRESOLVED \
response not a proper suffix of the acomp sent. This is not legal: %s\n",
line);
#if 0 /* mitra commented these out; unclear why */
ardp_rqfree(dir->dqs->preq);
if (dir->dqs->stfree_comp) stfree(dir->dqs->comp);
stfree(dir->dqs); dir->dqs = NULL;
#endif
return perrno = dir->status = DIRSRV_BAD_FORMAT;
} /* case 'U' (unresolved) */
scanerr:
default:
if(tmp = scan_error(line, dqs->preq)) {
/* XXX memory leak? check this mitracode. */
/* change 1 follows */
return(tmp);
}
break;
} /* switch */
} /*while*/
return PSUCCESS;
} /* bad_p_get_dir( */
/*
* p_get_dir - Get contents of a directory given its location
*
* P_GET_DIR takes a directory location, a list of desired
* components, a pointer to a directory structure to be
* filled in, and flags. It then queries the appropriate
* directory server and retrieves the desired information.
*
* ARGS: dlink - Host and host specific object name for directory
* plus any filters to be applied (->filters)
* components - The first component of the name we want to
* resolve in that directory. Wildcards are
* allowed if there are no additional components.
* NULL or the empty string means 'all'.
* dir - Structure to be filled in (or possibly in the
* process of being filled in, if GVD_ASYNC)
* flags - Options. See FLAGS
* acomp - Pointer to a token list consisting of the
* remaining components of the name we
* want to resolve in that directory. This
* argument might get modified. If it is, acomp
* will be left pointing to a suffix (not
* necessarily a proper suffix) of the token list or
* NULL, and the extra tokens will be freed by
* p_get_dir().
*
* FLAGS: GVD_UNION - Do not expand union links
* GVD_EXPAND - Expand union links locally
* GVD_REMEXP - Request remote expansion (& local if refused)
* GVD_LREMEXP - Request remote expansion of local union links
* GVD_VERIFY - Only verify that args are for a directory
* GVD_FIND - Stop expanding when a match is found
* GVD_ATTRIB - Request attributes from directory server
* GVD_NOSORT - Do not sort links when adding to directory
* GVD_MOTD - Request MOTD string from server
* GVD_MOTD_O - Request MOTD and nothing else
* GVD_ASYNC - Process request asynchronously
*
* RETURNS: PSUCCESS (0) or error code on completion.
* On some codes addition information in p_err_string.
* DQ_ACTIVE if request if GVD_ASYNC specified and
* request is still active.
*
* NOTES: If acomp is non-null the string it points to might be modified
*
* If the directory passed as an argument already has
* links or union links, then those lists will be freed
* before the new contents are filled in.
*
* If a filter is passed to the procedure, and application of
* the filter results in additional union link, then those links
* will (or will not) be expanded as specified in the FLAGS field.
*
* If the list of components in NULL, or the null string, then
* p_get_dir will return all links in the requested directory.
*/
/* XXX
* See bug report in /nfs/pfs/bugs/p_get_dir_modifies_argument for
* an explanation of what needs to be done to make this code
* more aesthetic and maintainable. this is a quick fix to get it working.
*/
#define RETURN(rv) { retval = (rv); goto cleanup ; }
static int
bad_p_get_dir(VLINK dlink, /* Directory to examine */
const char *components,/* Component name (wildcards allowed). */
VDIR dir, /* Structure to be filled in */
long flags, /* Flags */
TOKEN *acomp) /* Components left to be resolved */
{
struct dqstate *dqs; /* State of current query */
OUTPUT_ST out_st; /* Output pointer for packet contents */
OUTPUT out = &out_st;
int ardptime; /* Time to wait for ardp_send */
int tmp; /* To check return values */
VLINK tl = NULL; /* Temp link used for apply_filter() */
/* Setting it to NULL shuts up GCC; never used
before set. */
VLINK l; /* Temp link pointer */
FILTER filters; /* Filters to apply this time through */
char options[40]; /* LIST option */
char *tchar; /* Temporary chatacter pointer */
int retval; /* Hold return value */
perrno = 0;
if((dir->status == DQ_ACTIVE) && dir->dqs) {
dqs = dir->dqs;
/* Turn off this query's GVD_ASYNC flag if it was set on a previous
call to P_GET_DIR. */
if(!(flags & GVD_ASYNC)) dqs->flags &= (~GVD_ASYNC);
}
else {
/* Initialize a new dqstate structure. */
dqs = (struct dqstate *) stalloc(sizeof(struct dqstate));
if(!dqs) RETURN(PFAILURE);
dir->dqs = dqs;
dqs->dl = dlink;
if (!components || (*components == '\0')) dqs->comp = NULL;
else dqs->comp = (char *) components; /* strip off CONST. */
dqs->stfree_comp = 0;
dqs->acomp = acomp;
dqs->flags = flags;
dqs->fwdcnt = MAX_FWD_DEPTH;
dqs->newlinks = 0;
dqs->clnk = NULL;
dqs->cexp = NULL;
dqs->pulnk = NULL;
/* If filters, don't do remote expansion of union links! */
#ifdef NEVERDEFINED
/* I cant see why not! */
if(dqs->dl->filters) {
dqs->flags &= (~(GVD_REMEXP&(~GVD_EXPAND)));
dqs->comp = NULL;
}
#endif
if(acomp && *acomp && !dlink->filters) dqs->mcomp = 1;
else dqs->mcomp = 0;
}
*options = '\0';
if(dqs->flags & GVD_ATTRIB) strcat(options,"+ATTRIBUTES");
if((dqs->flags & GVD_EXPMASK) == GVD_REMEXP) strcat(options,"+EXPAND");
if((dqs->flags & GVD_EXPMASK) == GVD_LREMEXP) strcat(options,"+LEXPAND");
/* If all we are doing is verifying a directory */
/* we do not want a big response from the directory */
/* server. A NOT-FOUND is sufficient. */
if(dqs->flags & GVD_VERIFY) strcat(options,"+VERIFY");
if((dir->status == DQ_ACTIVE) && dir->dqs) goto cont_async;
startover:
filters = dqs->dl->filters;
if(filters) {
if(tl) vlfree(tl); /* Loop with filter leaked old copy */
tl = vlalloc(); /* Pased to apply_filters */
tl->name = stcopy("UNNEEDED");
tl->host = stcopy(dqs->dl->host);
tl->hsoname = stcopy(dqs->dl->hsoname);
tl->filters = flcopy(filters,1); /* otherwise gets freed twice*/
}
get_contents:
/* Build dir->dqs->preq and out */
/* Side effect, filters now points to first client-side filter */
build_out(dir, out, options, &filters);
cont_async:
if(dqs->flags & GVD_ASYNC) ardptime = 0;
else ardptime = ARDP_WAIT_TILL_TO;
if(dqs->preq->status == ARDP_STATUS_NOSTART)
tmp = ardp_send(dqs->preq,dqs->dl->host,0,ardptime);
else tmp = ardp_retrieve(dqs->preq,ardptime);
if((dqs->flags & GVD_ASYNC) && (tmp == ARDP_PENDING)) RETURN(DQ_ACTIVE);
/* If we don't get a response, then if the requested */
/* directory, return error, if a ulink, mark it unexpanded */
if(tmp != PSUCCESS) {
if (pfs_debug)
fprintf(stderr,"ardp_send failed: %d\n",tmp);
if(dqs->cexp) dqs->cexp->expanded = FAILED;
else {
ardp_rqfree(dir->dqs->preq);
/* change 1 done*/
if (dir->dqs->stfree_comp) stfree(dir->dqs->comp);
stfree(dir->dqs); dir->dqs = NULL;
RETURN(perrno = dir->status = tmp);
}
}
/* Claim that we don't have any more unresolved components to process. If
we get an UNRESOLVED response, we'll set this back on. */
dqs->unresp = 0;
if (tmp == PSUCCESS) {
/* Here we must parse reponse and put in directory */
/* While looking at each packet */
switch (tmp = retrieve_and_parse(dir)) {
case PSUCCESS: break;
case DSRFINFO_FORWARDED: goto startover;
default:
if (dqs->cexp) dqs->cexp->expanded = FAILED;
else {
ardp_rqfree(dqs->preq);
if (dqs->stfree_comp) stfree(dqs->comp);
stfree(dqs); dqs = NULL;
RETURN(perrno = dir->status = tmp);
}
} /*switch*/
}
/* We sent multiple components and weren't told any */
/* were unresolved */
if(dqs->mcomp && !dqs->unresp) {
TOKEN acp;
for (acp = *dqs->acomp; acp->next; acp = acp->next)
;
/* acp now points to the last additional component */
dqs->comp = acp->token;
acp->token = NULL;
/* Free the entries. */
tklfree(*dqs->acomp);
/* Let rd_vdir know what remains */
*(dqs->acomp) = NULL;
/* If we have union links to resolve, only one component remains */
dqs->mcomp = 0;
}
/* If only verifying, we already know it is a directory */
if(dqs->flags & GVD_VERIFY) {
ardp_rqfree(dqs->preq);
if (dir->dqs->stfree_comp) stfree(dir->dqs->comp);
stfree(dir->dqs); dir->dqs = NULL;
dir->status = DQ_COMPLETE;
RETURN(PSUCCESS);
}
/* Don't return if matching was delayed by the need to filter */
/* if FIND specified, and dir->links is non null, then we have */
/* found a match, and should return. */
if((dqs->flags & GVD_FIND) && dir->links && (!filters)) {
ardp_rqfree(dqs->preq);
if (dir->dqs->stfree_comp) stfree(dir->dqs->comp);
stfree(dir->dqs); dir->dqs = NULL;
dir->status = DQ_COMPLETE;
RETURN(PSUCCESS);
}
/* If expand specified, and ulinks must be expanded, making sure */
/* that the order of the links is maintained properly */
expand_ulinks:
if ((dqs->flags & GVD_FIND) ||
(((dqs->flags & GVD_EXPMASK) != GVD_UNION) &&
!(dqs->flags & GVD_VERIFY))) {
l = dir->ulinks;
/* Find first unexpanded ulink */
while(l && l->expanded && (l->linktype == 'U')) l = l->next;
/* Only expand if a FILE or DIRECTORY - Mark as */
/* failed otherwise */
/* We must still add support for symbolic ulinks */
if(l) {
if ((strcmp(l->target,"DIRECTORY") == 0) ||
(strcmp(l->target,"FILE") == 0)) {
l->expanded = TRUE;
dqs->cexp = l;
dqs->pulnk = l;
dqs->dl->host = stcopyr(l->host, dqs->dl->host);
dqs->dl->hsoname = stcopyr(l->hsoname, dqs->dl->hsoname);
/* When we're expanding union links, we need to turn off
support for multiple components. Otherwise, p_get_dir() is
confused by a NONE-FOUND response during the expansion
process and zeroes out the dqs->acomp. */
dqs->mcomp = 0;
goto get_contents;
}
else l->expanded = FAILED;
}
}
/* Right now, give off no error messages if inappropriate filters are
encountered (e.g., a SERVER filter following a CLIENT filter).
We just don't worry about it right now. Maybe we should do error
reporting. Presumably, only client filters are left right now. XXX */
if(filters && filters->execution_location == FIL_CLIENT &&
(filters->type == FIL_DIRECTORY || filters->type == FIL_HIERARCHY)) {
apply_filters(dir,filters,tl,0); /* Apply a single filter */
filters = filters->next;
goto expand_ulinks;
}
/* Double check to make sure we don't get */
/* back unwanted components */
/* OK to keep if special (URP) links */
if(dqs->comp && *(dqs->comp)) {
l = dir->links;
while(l) {
VLINK ol;
if((l->linktype == 'L' || l->linktype == 'I')
&& (!wcmatch(l->name,dqs->comp))) {
if(l == dir->links)
dir->links = l->next;
else l->previous->next = l->next;
if(l->next) l->next->previous = l->previous;
ol = l;
l = l->next;
vlfree(ol);
}
else l = l->next;
}
}
ardp_rqfree(dqs->preq);
if (dir->dqs->stfree_comp) stfree(dir->dqs->comp);
stfree(dir->dqs); dir->dqs = NULL;
dir->status = DQ_COMPLETE;
RETURN(PSUCCESS);
cleanup:
if (tl) vlfree(tl);
return(retval);
}
int
p_get_dir(VLINK dlink, const char *components, VDIR dir,
long flags, TOKEN *acomp)
{
VLINK vl;
int retval;
/* the distributed p_get_dir hacks dlink if the resulting directory contains
any union links, or is forwarded, fix this behaviour */
vl = vlcopy(dlink,FALSE);
retval = bad_p_get_dir(vl, components, dir, flags, acomp);
vlfree(vl);
return retval;
}
/* Older interfaces (backwards compatability) */
int
p_get_vdir(VLINK dlink, /* Directory to examine */
const char *components,/* Component name (wildcards allowed). */
VDIR dir, /* Structure to be filled in */
long flags, /* Flags */
TOKEN *acomp) /* Components left to be resolved */
{
return p_get_dir(dlink, components, dir, flags, acomp);
}
int
get_vdir(VLINK dlink, /* Directory to examine */
const char *components,/* Component name (wildcards allowed). */
VDIR dir, /* Structure to be filled in */
long flags, /* Flags */
TOKEN *acomp) /* Components left to be resolved */
{
return p_get_dir(dlink, components, dir, flags, acomp);
}