482 lines
18 KiB
C
482 lines
18 KiB
C
/*
|
|
* Copyright (c) 1989, 1990, 1991 by the University of Washington
|
|
* Copyright (c) 1992, 1993 by the University of Southern California
|
|
*
|
|
* For copying and distribution information, please see the files
|
|
* <uw-copyright.h> and <usc-copyr.h>
|
|
*/
|
|
|
|
#include <uw-copyright.h>
|
|
#include <usc-copyr.h>
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#ifdef USE_SYS_DIR_H
|
|
#include <sys/dir.h>
|
|
#else
|
|
#include <dirent.h>
|
|
#endif
|
|
#include <stdio.h>
|
|
#include <sgtty.h>
|
|
#include <string.h>
|
|
#include <pwd.h>
|
|
#include <grp.h>
|
|
|
|
#define FILE_VNO 5 /* version # of the file format. */
|
|
|
|
#include <ardp.h>
|
|
#include <pfs.h>
|
|
#include <plog.h>
|
|
#include <pprot.h>
|
|
#include <perrno.h>
|
|
#include <pmachine.h>
|
|
#include <pparse.h>
|
|
#include <psrv.h> /* For nativize_prefix etc */
|
|
|
|
extern char shadow[];
|
|
extern char dirshadow[];
|
|
extern char dircont[];
|
|
extern char aftpdir[];
|
|
|
|
PATTRIB atalloc();
|
|
VLINK check_fwd();
|
|
|
|
/* Temporary hack. This will go away when the interface to dsrfinfo() and
|
|
dsrobject() changes. Note that the attributes returned depend upon the
|
|
client address; this will have to be resolved when we try caching this kind
|
|
of directory service information. */
|
|
PERFILE_STATIC_LONG_DEF(client_addr); /* set client address. Initially
|
|
0. */
|
|
#define client_addr p_th_arclient_addr[p__th_self_num()]
|
|
|
|
void
|
|
set_client_addr(long client_addr_arg)
|
|
{
|
|
client_addr = client_addr_arg;
|
|
}
|
|
|
|
/*
|
|
* dsrfinfo - Read file info from disk
|
|
*
|
|
* DSRFINFO is used by the directory server to read file information from
|
|
* disk. It takes the host specific part of the system level name of the
|
|
* file about which information is to be read, and a pointer to the file
|
|
* structure which it fills in. It returns 0 or -1 on success, 0 if a file
|
|
* and -1 if a directory. On errors with some corrupt lines in the shadow
|
|
* file, it logs the bad data format to the Prospero logfile, and attempts to
|
|
* continue. On other errors, it returns PFAILURE. If unable to find the
|
|
* object, it returns DSRFINFO_NOT_A_FILE. If it finds useful forwarding
|
|
* information, it will return DSRFINFO_FORWARDED.
|
|
*/
|
|
|
|
int
|
|
dsrfinfo(char *nm,long magic,PFILE fi)
|
|
{
|
|
struct requested_attributes req_obj_ats_st;
|
|
|
|
ZERO(&req_obj_ats_st);
|
|
req_obj_ats_st.all = 1;
|
|
return dsrfinfo_with_attribs(nm, magic, fi, &req_obj_ats_st);
|
|
}
|
|
|
|
/*
|
|
* For now, we have turned off the magic # matching features. Forwarding is
|
|
* no longer magic number based, pending a determination on the role of magic
|
|
* numbers in object creation. See /nfs/pfs/notes/forwarding.
|
|
*/
|
|
int
|
|
dsrfinfo_with_attribs(char *nm,long magic,PFILE fi,
|
|
struct requested_attributes *req_obj_ats)
|
|
{
|
|
FILE *pfs_fi = NULL;
|
|
char *pfs_fi_name = NULL;
|
|
char name[MAXPATHLEN];
|
|
char shadow_fname[MAXPATHLEN];
|
|
char dirshadow_fname[MAXPATHLEN];
|
|
|
|
VLINK cur_fp; /* current forwarding poiner */
|
|
int tmp;
|
|
char *ls; /* Last slash */
|
|
|
|
struct passwd *ownpw;
|
|
struct group *owngr;
|
|
struct tm *mt;
|
|
char mtime_str[20];
|
|
char mode_str[15];
|
|
|
|
PATTRIB at;
|
|
PATTRIB ap; /* Temp attribute pointer */
|
|
|
|
struct stat file_stat_dat;
|
|
struct stat *file_stat = &file_stat_dat;
|
|
|
|
/* This may be reset to -1 once we determine it's a directory. If we go up
|
|
the directory tree looking for forwarding information, it will be reset
|
|
to DSRFINFO_FORWARDED. */
|
|
int retval = PSUCCESS;
|
|
INPUT_ST in_st;
|
|
INPUT in = &in_st;
|
|
|
|
/* Expand special file names */
|
|
if((*nm != '/') && *aftpdir && (strncmp(nm,"AFTP",4) == 0)) {
|
|
strcpy(name,aftpdir);
|
|
strcat(name,nm+4);
|
|
}
|
|
else strcpy(name,nm);
|
|
|
|
/* Need to canonicalize the file or directory name (strip out any
|
|
symbolic links. */
|
|
/* XXX Find the real name of the file and use it */
|
|
/* rnl = readlink(filename,rname,MAXPATHLEN); */
|
|
/* if(rnl >= 0) *(rname+rnl) = '\0'; */
|
|
|
|
fi->version = -1; /* sentinel value in case not found. */
|
|
fi->f_magic_no = 0;
|
|
/* No need to initialize rest of fi, its already initialized */
|
|
startover:
|
|
|
|
/* Determine name of shadow file */
|
|
strcpy(shadow_fname,shadow);
|
|
nativize_prefix(name, shadow_fname + strlen(shadow),
|
|
sizeof shadow_fname - strlen(shadow));
|
|
qsprintf(dirshadow_fname,sizeof dirshadow_fname,"%s/%s",shadow_fname,dirshadow);
|
|
|
|
/* NOTE: A temporary inefficient shadow file format is*/
|
|
/* in use. For this reason, the code supporting it */
|
|
/* is also interim code, and does not do any checking */
|
|
/* to make sure that the file actually follows */
|
|
/* the format. Thus, a munged file will result */
|
|
/* in unpredictable results. */
|
|
|
|
/* Read the contents of the shadow file */
|
|
/* First find out if it is a directory */
|
|
if(stat(shadow_fname,file_stat) == 0) {
|
|
if(file_stat->st_mode & S_IFDIR)
|
|
pfs_fi = locked_fopen(pfs_fi_name = dirshadow_fname,"r");
|
|
else pfs_fi = locked_fopen(pfs_fi_name = shadow_fname,"r");
|
|
}
|
|
|
|
if(pfs_fi != NULL) {
|
|
#define RETURN(rv) { retval = (rv); goto cleanup ; }
|
|
char *line, *next_word;
|
|
tmp = wholefiletoin(pfs_fi, in);
|
|
locked_fclose_A(pfs_fi, pfs_fi_name, TRUE);
|
|
if(tmp) {
|
|
plog(L_DIR_ERR,NOREQ,"%s",p_err_string);
|
|
return tmp;
|
|
}
|
|
while(in_nextline(in)) {
|
|
if (in_line(in, &line, &next_word)) {
|
|
/* If we cannot parse it, do not use it. */
|
|
plog(L_DATA_FRM_ERR,NOREQ,
|
|
"Unreadable file info format in %s.", shadow_fname);
|
|
RETURN(PFAILURE);
|
|
}
|
|
|
|
CHECK_PTRinBUFF(line,next_word);
|
|
switch(*line) {
|
|
case 'V': /* Version Number */
|
|
tmp = sscanf(line,"VERSION %d",&(fi->version));
|
|
if(tmp != 1) {
|
|
plog(L_DATA_FRM_ERR,NOREQ,"Bad file info format %s: %s",
|
|
shadow_fname,line,0);
|
|
}
|
|
if (fi->version != FILE_VNO) {
|
|
plog(L_DATA_FRM_ERR, NOREQ, "Bad file info format %s: \
|
|
unknown version %d, expected %d", shadow_fname, fi->version, FILE_VNO);
|
|
RETURN(PFAILURE);
|
|
}
|
|
break;
|
|
case 'M': /* Magic Number */
|
|
tmp = sscanf(line,"MAGIC-NUMBER %ld",&(fi->f_magic_no));
|
|
if(tmp != 1) {
|
|
fi->f_magic_no = 0;
|
|
plog(L_DATA_FRM_ERR,NOREQ,"Bad file info format %s: %s",
|
|
shadow_fname,line);
|
|
}
|
|
break;
|
|
case 'E': /* Expiration Date */
|
|
break;
|
|
case 'T': /* Time to live */
|
|
break;
|
|
case 'L': /* Time of last reference */
|
|
break;
|
|
case 'F': /* Forwarding Pointer */
|
|
/* A forwarding pointer has its NAME member set to the old
|
|
hosname of the object (now being forwarded). the NAME
|
|
member may terminate in a *, indicating a wildcarded match.
|
|
The HOST is the new host of the object and the HSONAME is
|
|
its new HSONAME. A trailing * in the HSONAME is replaced
|
|
by whatever matched the trailing * in the NAME member
|
|
of the object being forwarded. Yes, this means that it is
|
|
difficult to forward an object whose real HSONAME ends in
|
|
'*'. We do not currently create such objects, although we
|
|
might. */
|
|
if (strnequal(line, "FORWARD", 7)
|
|
&& in_link(in, line, next_word, 0, &cur_fp,
|
|
(TOKEN *) NULL) == PSUCCESS) {
|
|
APPEND_ITEM(cur_fp, fi->forward);
|
|
} else {
|
|
plog(L_DATA_FRM_ERR,NOREQ,"Bad file info format %s: %s",
|
|
shadow_fname,line);
|
|
}
|
|
break;
|
|
|
|
case 'B': /* Back Link */
|
|
break;
|
|
case 'A': /* Attribute or ACL*/
|
|
if (strnequal(line, "ACL", 3)) {
|
|
if(in_ge1_acl(in, line, next_word, &fi->oacl)) {
|
|
plog(L_DATA_FRM_ERR,NOREQ,"Bad file info format %s: %s",
|
|
shadow_fname,line);
|
|
RETURN(PFAILURE);
|
|
}
|
|
}
|
|
else if (!strnequal(line, "ATTRIBUTE", 9)
|
|
|| in_ge1_atrs(in, line, next_word, &fi->attributes))
|
|
plog(L_DATA_FRM_ERR,NOREQ,"Bad file info format %s: %s",
|
|
shadow_fname,line);
|
|
break;
|
|
/*default will skip on to next line - prob reasonable*/
|
|
}
|
|
}
|
|
|
|
|
|
#ifdef SERVER_DO_NOT_SUPPORT_FORWARDING
|
|
#if 0 /* This code is inconsistent; was murdered from
|
|
older working code */
|
|
/* Explicitly setting the magic # of an object to a negative number
|
|
indicates that 'this object is not really here; it's just
|
|
a placeholder for forwarding pointers'. */
|
|
/* Comparing the magic #s is a fast operation; faster than scanning
|
|
though a list of forwarding pointers. Therefore, if the magic #s
|
|
match, all is well. */
|
|
/* Check_above sets the return value to DSRFINFO_FORWARDED to indicate
|
|
that we're searching up the UNIX directory hierarchy for a matching
|
|
FORWARD message */
|
|
/* Determine if the file has been forwarded, and if so return, */
|
|
/* indicating that fact */
|
|
/* A file is assumed to have been forwarded if (a) an explicit magic #
|
|
was requested and it doesn't match the current magic # and matching
|
|
forwarding data was found here for it
|
|
if (((fi->f_magic_no && magic && (fi->f_magic_no != magic)) ||
|
|
(fi->f_magic_no < 0))
|
|
&& check_fwd(fi->forward,nm,magic))
|
|
return(DSRFINFO_FORWARDED);
|
|
/* Otherwise, this isn't the object that we thought it was (or it
|
|
has 'I am not really here' set on it, but no forwarding
|
|
informatin found. */
|
|
else
|
|
return DSRFINFO_NOT_A_FILE;
|
|
#else
|
|
|
|
/* Test: is this object marked indicating that it's the ghost of a
|
|
forwarded object? */
|
|
if (fi->f_magic_no < 0) retval = DSRFINFO_FORWARDED;
|
|
if (retval == DSRFINFO_FORWARDED) {
|
|
if (check_fwd(fi->forward, nm, magic)) /* if not locally forwarded.
|
|
*/
|
|
return DSRFINFO_FORWARDED;
|
|
else
|
|
return DSRFINFO_NOT_A_FILE;
|
|
}
|
|
#endif
|
|
#endif /* SERVER_DO_NOT_SUPPORT_FORWARDING */
|
|
|
|
}
|
|
|
|
/* Fill in attributes from the real file, if it exists and if we are not
|
|
just running up the hierarchy for forwarding information, and if we
|
|
actually want any of these attributes. */
|
|
|
|
if((retval == PSUCCESS) && was_attribute_requested("CONTENTS", req_obj_ats))
|
|
return(retval);
|
|
|
|
if ((retval == PSUCCESS)
|
|
&& ( was_attribute_requested("SIZE", req_obj_ats)
|
|
|| was_attribute_requested("NATIVE-OWNER", req_obj_ats)
|
|
|| was_attribute_requested("NATIVE-GROUP", req_obj_ats)
|
|
|| was_attribute_requested("LAST-MODIFIED", req_obj_ats)
|
|
|| was_attribute_requested("UNIX-MODES", req_obj_ats))
|
|
&& (stat(name,file_stat) == 0)) {
|
|
if (was_attribute_requested("SIZE", req_obj_ats)) {
|
|
/* off_t st_size; /* total size of file */
|
|
at = atalloc();
|
|
at->aname = stcopy("SIZE");
|
|
at->avtype = ATR_SEQUENCE;
|
|
at->nature = ATR_NATURE_INTRINSIC;
|
|
at->value.sequence = tkalloc(NULL);
|
|
at->value.sequence->token =
|
|
qsprintf_stcopyr((char *) NULL, "%d bytes", file_stat->st_size);
|
|
APPEND_ITEM(at, fi->attributes);
|
|
}
|
|
|
|
#ifndef PFS_THREADS
|
|
if (was_attribute_requested("NATIVE-OWNER", req_obj_ats)) {
|
|
/* short st_uid; /* user-id of owner */
|
|
ownpw = getpwuid(file_stat->st_uid); /* not MT safe */
|
|
if(ownpw) {
|
|
at = atalloc();
|
|
at->aname = stcopy("NATIVE-OWNER");
|
|
at->avtype = ATR_SEQUENCE;
|
|
at->nature = ATR_NATURE_INTRINSIC;
|
|
at->value.sequence = tkalloc(ownpw->pw_name);
|
|
APPEND_ITEM(at, fi->attributes);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
#ifndef PFS_THREADS
|
|
if (was_attribute_requested("NATIVE-GROUP", req_obj_ats)) {
|
|
/* short st_gid; /* group-id of owner */
|
|
owngr = getgrgid(file_stat->st_gid); /* not MT safe */
|
|
if(owngr) {
|
|
at = atalloc();
|
|
at->aname = stcopy("NATIVE-GROUP");
|
|
at->avtype = ATR_SEQUENCE;
|
|
at->nature = ATR_NATURE_INTRINSIC;
|
|
at->value.sequence = tkalloc(owngr->gr_name);
|
|
APPEND_ITEM(at, fi->attributes);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (was_attribute_requested("LAST-MODIFIED", req_obj_ats)) {
|
|
/* time_t st_atime; /* file last access time */
|
|
|
|
/* time_t st_mtime; /* file last modify time */
|
|
if(file_stat->st_mtime) {
|
|
at = atalloc();
|
|
at->aname = stcopy("LAST-MODIFIED");
|
|
at->avtype = ATR_SEQUENCE;
|
|
at->nature = ATR_NATURE_INTRINSIC;
|
|
at->value.sequence = tkalloc(NULL);
|
|
at->value.sequence->token =
|
|
p_timetoasn_stcopyr(file_stat->st_mtime,
|
|
at->value.sequence->token);
|
|
APPEND_ITEM(at, fi->attributes);
|
|
}
|
|
}
|
|
|
|
if (was_attribute_requested("UNIX-MODES", req_obj_ats)) {
|
|
/* u_short st_mode; /* protection */
|
|
at = atalloc();
|
|
at->aname = stcopy("UNIX-MODES");
|
|
at->avtype = ATR_SEQUENCE;
|
|
at->nature = ATR_NATURE_INTRINSIC;
|
|
|
|
strcpy(mode_str,"----------");
|
|
if((file_stat->st_mode & S_IFLNK) == S_IFLNK) mode_str[0] = 'l';
|
|
if(file_stat->st_mode & S_IREAD) mode_str[1] = 'r';
|
|
if(file_stat->st_mode & S_IWRITE) mode_str[2] = 'w';
|
|
if(file_stat->st_mode & S_IEXEC) mode_str[3] = 'x';
|
|
if(file_stat->st_mode & S_ISUID) mode_str[3] = 's';
|
|
if(file_stat->st_mode & (S_IREAD>>3)) mode_str[4] = 'r';
|
|
if(file_stat->st_mode & (S_IWRITE>>3)) mode_str[5] = 'w';
|
|
if(file_stat->st_mode & (S_IEXEC>>3)) mode_str[6] = 'x';
|
|
if(file_stat->st_mode & S_ISGID) mode_str[6] = 's';
|
|
if(file_stat->st_mode & (S_IREAD>>6)) mode_str[7] = 'r';
|
|
if(file_stat->st_mode & (S_IWRITE>>6)) mode_str[8] = 'w';
|
|
if(file_stat->st_mode & (S_IEXEC>>6)) mode_str[9] = 'x';
|
|
if(file_stat->st_mode & S_IFDIR) {
|
|
mode_str[0] = 'd';
|
|
if(retval == PSUCCESS) retval = -1;
|
|
}
|
|
at->value.sequence = tkalloc(mode_str);
|
|
APPEND_ITEM(at, fi->attributes);
|
|
}
|
|
}
|
|
/* If no native file AND no PFS file info, then look for forwarding */
|
|
else if(fi->version < 0) goto check_above;
|
|
|
|
/* Stick on the INTRINSIC ACCESS-METHOD attributes. */
|
|
if (retval == 0 /* if it's a file */
|
|
&& was_attribute_requested("ACCESS-METHOD", req_obj_ats)) {
|
|
PATTRIB at1 = NULL;
|
|
PATTRIB nextat;
|
|
get_access_method(name, client_addr, &at1);
|
|
for ( ; at1; at1 = nextat) {
|
|
nextat = at1->next;
|
|
EXTRACT_HEAD_ITEM(at1);
|
|
APPEND_ITEM(at1, fi->attributes);
|
|
}
|
|
}
|
|
return(retval);
|
|
|
|
/* Check above looks for forwarding pointers in directories */
|
|
/* above the named file. It is reached when we have been passed
|
|
an hsoname for a nonexistent object.
|
|
We might be passed an hsoname for a nonexistent object either
|
|
(a) if the object has been forwarded or (b) the object has not
|
|
yet been created. So we look for forwarding information or
|
|
for a real superior object. Once we make a hit (or hit the root of the
|
|
tree), we return either DSRFINFO_FORWARDED or DSRFINFO_NOT_A_FILE.
|
|
*/
|
|
/* Look up, in case the object has moved, but a forwarding address does not
|
|
exist in the corresponding file info, but might exist further up. */
|
|
|
|
check_above:
|
|
|
|
retval = DSRFINFO_NOT_A_FILE; /* If the forwarded test succeeds, will
|
|
return DSRFINFO_FORWARDED instead. */
|
|
|
|
ls = strrchr(name,'/');
|
|
/* If we have used up all components, return failure */
|
|
if((ls == 0) || (ls == name)) return(DSRFINFO_NOT_A_FILE);
|
|
|
|
*ls = '\0';
|
|
|
|
goto startover;
|
|
|
|
cleanup: /* used for abnormal exit */
|
|
/* input file is closed in code above. */
|
|
return retval;
|
|
}
|
|
|
|
|
|
|
|
/* check_fwd takes a list of forwarding pointers, checks to see */
|
|
/* if any apply to the object with the specified magic number */
|
|
/* and returns the appropiate link, or none if not forwarded */
|
|
/* If the match is a widarded match, the returned link will be */
|
|
/* modified so that the matched wildcard is replaced by the */
|
|
/* text that matched it */
|
|
/* */
|
|
/* BUGS only a trailing * wildcard is allowed */
|
|
VLINK
|
|
check_fwd(fl,name,magic)
|
|
VLINK fl;
|
|
char *name;
|
|
int magic;
|
|
{
|
|
char *sp; /* Star pointer */
|
|
|
|
while(fl) {
|
|
if(((magic == 0) || (fl->f_magic_no == 0) ||
|
|
(fl->f_magic_no == magic)) && (wcmatch(name,fl->name))) {
|
|
|
|
sp = strchr(fl->hsoname,'*');
|
|
if(sp) *sp = '\0';
|
|
|
|
sp = strchr(fl->name,'*');
|
|
if(sp) {
|
|
int n;
|
|
|
|
sp = name + (sp - fl->name);
|
|
fl->name = stcopyr(name,fl->name);
|
|
fl->hsoname = qsprintf_stcopyr(fl->hsoname,
|
|
"%s%s",fl->hsoname,sp);
|
|
}
|
|
|
|
return(fl);
|
|
}
|
|
fl = fl->next;
|
|
}
|
|
return(NULL);
|
|
}
|
|
|
|
|
|
|