/* * Copyright (c) 1993 by the University of Southern California * * For copying and distribution information, please see the file * . */ #include #include #include #include #include #ifndef SOLARIS /* if not posix, need MAXPATHLEN still */ #include #endif #include /* For DIRECTORYCACHING and for PLOG overrides. */ #include #include #include #include #ifdef DSROBJECT_SPEEDUP_IS_EXPERIMENTAL int dsrobject_speedup = 1; /* speed up DSROBJECT. This can be set to 0 to use the old code. These changes made by SWA during the week of 5/9/94 */ #endif static PATTRIB newamat(void); #ifdef DIRECTORYCACHING /* One day */ #define SECONDSPERDAY (60*60*24) #define MAXDIRCACHEAGE (1*SECONDSPERDAY) int cache_attempt = 0; int cache_can = 0; int cache_yes = 0; /* Used to see how many times dsrobject() failed in a retrieval request. */ int dsrobject_fail = 0; int vdir_outofdate(VDIR dir, char *hsoname) { char native_dirname[MAXPATHLEN]; char vfs_dirname[MAXPATHLEN]; nativize_prefix(hsoname, native_dirname, sizeof native_dirname); strcpy(vfs_dirname,shadow); strcat(vfs_dirname,native_dirname); strcat(vfs_dirname,"/"); strcat(vfs_dirname,dircont); return (stat_age(vfs_dirname) > MAXDIRCACHEAGE); } #endif /*DIRECTORYCACHING */ /* Union links are expanded at a higher level. * The only message dsrobject() sends to the client is an ardp_rwait(). * dsrobject() logs NOT-AUTHORIZED failures via plog, but doesn't send messages to the client; the caller does that. * Checks to make sure hsoname is within the space we're allowed to discuss. * Does check to make sure you have r, G, or g rights on a database before it lets you scan that database for information. */ /* flags honored:: DRO_VERIFY_DIR */ /* Returns: DSRFINFO_FORWARDED DIRSRV_NOT_AUTHORIZED DIRSRV_NOT_FOUND PFAILURE PSUCCESS */ /* If called with the DRO_VERIFY flag, will set the P_OBJECT_FILE and P_OBJECT_DIRECTORY flags in the ob structure, but will not *necessarily* put any other information into the ob structure. Caller is still responsible for freeing any links pointed to by the ob structure. */ /* If called with the DRO_VERIFY_DIR flag, will return PSUCCESS only if object is a directory. Will not necessarily put any other information into the OB structure. Caller is still responsible for freeing any links pointed to by the ob structure. */ /* dsrobject(), if it returns anything other than DSRFINFO_FORWARDED or PSUCCESS, may leave the ob structure with arbitrary data in it. The caller should not expect the ob structure to remain untouched. */ /* At the moment, the only option we look at in dsrobject_list_options is the requested_attributes one, and that only to test for the CONTENTS attribute. */ /* The database ACL itself may be obtained by using an HSONAME that is just the root prefix of the database. */ int requested_contents(struct dsrobject_list_options *listopts); PATTRIB read_contents(char hsoname[]); int dsrobject(RREQ req, char hsoname_type[], char hsoname[], long version, long magic_no, int flags, struct dsrobject_list_options *listopts, P_OBJECT ob) { VDIR_ST dir_st; register VDIR dir = &dir_st; register PFILE fi = NULL; /* Only set if it needs to be. */ int retval; /* Integer return value from subfunctions */ int dirretval = PFAILURE; /* Save initial dsrdir result*/ #ifdef DIRECTORYCACHING int cancache = TRUE; #endif #ifdef DSROBJECT_SPEEDUP_IS_EXPERIMENTAL if (!dsrobject_speedup) flags &= ~DRO_VERIFY; /* if not speedup, turn off DRO_VERIFY flag. */ #endif VLDEBUGBEGIN; vdir_init(dir); /* empty directory. */ set_client_addr(req->peer_addr.s_addr); /* temporary hack. */ /* Check whether HSONAME-TYPE and HSONAME are valid. */ if (!strequal(hsoname_type, "ASCII")) { plog(L_DIR_ERR, req, "dsrobject(): got invalid hsoname-type: %s", hsoname_type, 0); vdir_freelinks(dir); RETURNPFAILURE; } if (check_handle(hsoname) == FALSE) { plog(L_AUTH_ERR, req, "Got an HSONAME outside the part of the \ filesystem that this server is authorized to publish information about: %s", hsoname); /* Free the directory links */ vdir_freelinks(dir); return DIRSRV_NOT_AUTHORIZED; } if ( *hsoname != '/') { /* Database or special prefix in use */ register int i; #ifdef DIRECTORYCACHING /* mitracode */ cache_attempt++; /* SWA bug fix: this assumes listopts is always set, which it need not be. */ /* mitra: Can not cache if specifying a component (other than"*") */ /* swa added: Can not cache if #ALL attributes not set. This prevents us from blowing it badly. */ if (!listopts || (listopts->thiscompp && strcmp(*(listopts->thiscompp),"*")) || !listopts->req_link_ats.all) cancache = FALSE; if (cancache) { cache_can++; VLDEBUGBEGIN; if (!(dirretval = dsrdir(hsoname, magic_no, dir, NULL, DSRD_ATTRIBUTES))) cache_yes++; VLDEBUGDIR(dir); VLDEBUGEND; } #endif if (dirretval != PSUCCESS #ifdef DIRECTORYCACHING || vdir_outofdate(dir,hsoname) #endif || !(dir->links) ) { for (i = 0; i < db_num_ents; i++) { if (strnequal(hsoname, db_prefixes[i].prefix, strlen(db_prefixes[i].prefix)) && strequal(db_prefixes[i].hsoname_type, hsoname_type)) { ACL dbacl; VLDEBUGBEGIN; get_named_acl(db_prefixes[i].named_acl, &dbacl); VLDEBUGEND; if (dbacl && !srv_check_acl(dbacl, NULL, req, "r", SCA_LINKDIR,db_prefixes[i].prefix,NULL) && !srv_check_acl(dbacl, NULL, req, "g", SCA_LINKDIR,db_prefixes[i].prefix,NULL) && !srv_check_acl(dbacl, NULL, req, "G", SCA_LINKDIR,db_prefixes[i].prefix,NULL)) { plog(L_AUTH_ERR, req, "Unauthorized database request: %s %s", hsoname, listopts? *(listopts->thiscompp) : ""); vdir_freelinks(dir); return DIRSRV_NOT_AUTHORIZED; } /* This could take a while, tell client not to retry. This should be customized on a per-database basis. */ ardp_rwait(req, 180, 0, 0); VLDEBUGBEGIN; retval= db_prefixes[i].read_function(req, hsoname, version, magic_no, flags, listopts, ob); VLDEBUGOB(ob); VLDEBUGEND; #ifdef DIRECTORYCACHING if (retval == PSUCCESS && ob->links) { if (cancache) { VLDEBUGBEGIN; dswobject(hsoname_type,hsoname,ob); VLDEBUGEND; } vdir_freelinks(dir); return(retval); } else if (dirretval == PSUCCESS) { /* Must just have been old*/ /*drop through and reread dir and file*/ break; } else { /* Neither old nor new version available */ /* Or directory is really empty */ vdir_freelinks(dir); return(retval); } #else vdir_freelinks(dir); return retval; #endif /*DIRECTORYCACHING*/ } } /*for*/ } /*PSUCCESS*/ /* Here we have a prefix that is not a database, */ /* but not a normal filename either */ /* If normal file names need not begin with / */ /* fall through and try a normal query */ } /* Local directories can have associated finfo too. */ if (dirretval == PSUCCESS) { /* No need to reread if did already */ retval = dirretval; } else { VLDEBUGBEGIN; retval = dsrdir(hsoname, magic_no, dir, NULL, /* Set DSRD_VERIFY_DIR iff DRO_VERIFY_DIR is set. */ ((flags & DRO_VERIFY_DIR) ? DSRD_VERIFY_DIR : 0) /* Note that dsrdir() curently always returns all object attributes. This costs us some potentially unnecessary MALLOC()s. */ | ((listopts && listopts->requested_attrs) ? DSRD_ATTRIBUTES : 0)); VLDEBUGDIR(dir); VLDEBUGEND; } if (retval == PSUCCESS) ob->flags |= P_OBJECT_DIRECTORY; if (retval == PSUCCESS || retval == DSRDIR_NOT_A_DIRECTORY || retval == DIRSRV_NOT_DIRECTORY || retval == DIRSRV_NOT_FOUND) { int dsrfinfo_retval; #ifdef SERVER_DO_NOT_SUPPORT_FORWARDING /* Don't bother with the dsrfinfo() unless a specific attribute or set of attributes was requested, or unless a directory was not found. */ if (listopts->req_obj_ats.all || listopts->req_obj_ats.interesting || listopts->req_obj_ats.specific) { fi = pfalloc(); dsrfinfo_retval = dsrfinfo_with_attribs(hsoname, magic_no, fi, &listopts->req_obj_ats); } else { dsrfinfo_retval = PFAILURE; } #else VLDEBUGBEGIN; dsrfinfo_retval = dsrfinfo(hsoname,magic_no,fi); VLDEBUGFI(fi); VLDEBUGEND; if (dsrfinfo_retval == DSRFINFO_FORWARDED) { ob->inc_native = VDIN_MUNGED; ob->forward = fi->forward; fi->forward = NULL; pffree(fi); vdir_freelinks(dir); return dsrfinfo_retval; } #endif if (dsrfinfo_retval < 0) { /* dsrfinfo returns <0 to mean directory */ ob->flags |= P_OBJECT_DIRECTORY; retval = PSUCCESS; } else if (dsrfinfo_retval == PSUCCESS) { ob->flags |= P_OBJECT_FILE; if (requested_contents(listopts)) { PATTRIB at = read_contents(hsoname); /* Append it to fi->attributes since ob->attributes will get set from that. */ if (at) APPEND_ITEM(at, fi->attributes); } retval = PSUCCESS; } } #ifndef SERVER_DO_NOT_SUPPORT_FORWARDING if (retval == DSRFINFO_FORWARDED) { /* Only get here if dsrdir() or dsdb() returned DSRFINFO_FORWARDED */ ob->inc_native = VDIN_MUNGED; ob->forward = dir->f_info->forward; dir->f_info->forward = NULL; vdir_freelinks(dir); return retval; } #endif if (retval) { vdir_freelinks(dir); if (fi) pffree(fi); if (retval == DIRSRV_NOT_DIRECTORY || retval == DSRDIR_NOT_A_DIRECTORY || retval == DSRFINFO_NOT_A_FILE) return DIRSRV_NOT_FOUND; return retval; } /* Now for some manly calisthenics! Merge f_info and directory info. */ /* Note this is the model for the VERSION5 version dswobject so, if you change this, probably need to change that - Mitra */ /* Only potential conflict here: ACL. Handle it by appending the two ACLs if both are set. (DIRECTORY first; why not). If one is unset, use the default (DEFAULT SYSTEM for DIRECTORY, CONTAINER for OBJECT). If directory and object both have magic numbers set use one from directory. */ if (ob->flags & P_OBJECT_DIRECTORY) { ob->version = dir->version; /* always 0 */ ob->inc_native = dir->inc_native; ob->magic_no = dir->magic_no; ob->acl = dir->dacl; dir->dacl = NULL; assert(!dir->f_info); /* should have already been handled */ ob->links = dir->links; dir->links = NULL; ob->ulinks = dir->ulinks; dir->ulinks = NULL; ob->native_mtime = dir->native_mtime; } else { ob->inc_native = VDIN_NOTDIR; } /* If FINFO present (will not always need to be present), merge it in. */ if (fi) { /* Magic # on directory, if present, supersedes magic # on finfo. */ if (!ob->magic_no) ob->magic_no = fi->f_magic_no; /* Append object ACLs. */ if (fi->oacl) { CONCATENATE_LISTS(ob->acl, fi->oacl); fi->oacl = NULL; } ob->exp = fi->exp; ob->ttl = fi->ttl; ob->last_ref = fi->last_ref; ob->forward = fi->forward; fi->forward = NULL; ob->backlinks = fi->backlinks; fi->backlinks = NULL; ob->attributes = fi->attributes; fi->attributes = NULL; /* Done merging in attributes from FINFO structure. */ } /* Clean up and return. */ vdir_freelinks(dir); if (fi) pffree(fi); fi = NULL; VLDEBUGOB(ob); VLDEBUGEND; return retval; } /* Return a list of OBJECT INTRINSIC ACCESS-METHOD attributes for the local real file FILENAME. These attributes should be usable by the client coming from the address client_addr. One day this function will no longer be necessary when we have the Prospero access method working. Allocates pattribs and puts them into RETVAL. Note that FILENAME has already been expanded from any HSONAME. */ /* Called by dsrfinfo at the moment. */ void get_access_method(char filename[], long client_addr, PATTRIB *retval) { PATTRIB at; /* temporary working attribute. */ TOKEN nfs_am; /* Access method for NFS (if any) */ *retval = NULL; /* return list starts empty. */ /* Check for PROSPERO-CONTENTS access method */ /* Always true for any local file. */ at = newamat(); at->value.sequence = tkappend("PROSPERO-CONTENTS", at->value.sequence); at->value.sequence = tkappend("", at->value.sequence); at->value.sequence = tkappend("", at->value.sequence); at->value.sequence = tkappend("", at->value.sequence); at->value.sequence = tkappend("", at->value.sequence); APPEND_ITEM(at, *retval); /* Check for LOCAL access method. */ if (myaddress() == client_addr || /* Check for loopback net. */ #if BYTE_ORDER == BIG_ENDIAN (client_addr & 0xff000000) == (127 << 24) #else (client_addr & 0x000000ff) == 127 #endif ) { /* This may not return information for multi-homed hosts. But it will never return incorrect information. */ at = newamat(); at->value.sequence = tkappend("LOCAL", at->value.sequence); at->value.sequence = tkappend("", at->value.sequence); at->value.sequence = tkappend("", at->value.sequence); at->value.sequence = tkappend("", at->value.sequence); at->value.sequence = tkappend("", at->value.sequence); APPEND_ITEM(at, *retval); } else { #ifdef SHARED_PREFIXES char *cp; /* this memory doesn't need to be freed. */ if (cp = check_localpath(filename, client_addr)){ at = newamat(); at->value.sequence = tkappend("LOCAL", at->value.sequence); at->value.sequence = tkappend("", at->value.sequence); at->value.sequence = tkappend("", at->value.sequence); at->value.sequence = tkappend("", at->value.sequence); at->value.sequence = tkappend(cp, at->value.sequence); APPEND_ITEM(at, *retval); } #endif } /* Check for NFS access method. */ #ifdef NFS_EXPORT if(nfs_am = check_nfs(filename,client_addr)) { at = newamat(); at->value.sequence = nfs_am; APPEND_ITEM(at, *retval); } #endif NFS_EXPORT /* Check for AFS access method. Note that the hostname is irrelevant. */ if(*afsdir && strnequal(filename, afsdir, strlen(afsdir))) { char *suffix = filename + strlen(afsdir); at = newamat(); at->value.sequence = tkappend("AFS", at->value.sequence); at->value.sequence = tkappend("", at->value.sequence); at->value.sequence = tkappend("", at->value.sequence); at->value.sequence = tkappend("ASCII", at->value.sequence); at->value.sequence = tkappend(suffix, at->value.sequence); APPEND_ITEM(at, *retval); #ifdef AFS_AFTP_GATEWAY /* provide additional access method for sites that don't run AFS. */ at = newamat(); at->value.sequence = tkappend("AFTP", at->value.sequence); at->value.sequence = tkappend("INTERNET-D", at->value.sequence); at->value.sequence = tkappend(hostname, at->value.sequence); at->value.sequence = tkappend("ASCII", at->value.sequence); at->value.sequence = tkappend(qsprintf_stcopyr((char *) NULL, "%s%s", AFS_AFTP_GATEWAY, suffix), at->value.sequence); at->value.sequence = tkappend("BINARY", at->value.sequence); APPEND_ITEM(at, *retval); #endif } /* Check for AFTP access method. */ if(*aftpdir && strnequal(filename,aftpdir, strlen(aftpdir))) { char *suffix = filename + strlen(aftpdir); at = newamat(); at->value.sequence = tkappend("AFTP", at->value.sequence); at->value.sequence = tkappend("INTERNET-D", at->value.sequence); at->value.sequence = tkappend(hostname, at->value.sequence); at->value.sequence = tkappend("ASCII", at->value.sequence); at->value.sequence = tkappend(suffix, at->value.sequence); at->value.sequence = tkappend("BINARY", at->value.sequence); APPEND_ITEM(at, *retval); } } /* Allocate a new ACCESS-METHOD attribute. */ static PATTRIB newamat(void) { PATTRIB retval = atalloc(); retval->aname = stcopyr("ACCESS-METHOD", retval->aname); retval->precedence = ATR_PREC_OBJECT; retval->nature = ATR_NATURE_INTRINSIC; retval->avtype = ATR_SEQUENCE; return retval; } /* Was the CONTENTS attribute requested on the object itself? CONTENTS is an INTRINSIC- attribute. Return non-zero (C language "true") if true, zero (C language "false") if false. */ int requested_contents(struct dsrobject_list_options *listopts) { return listopts && was_attribute_requested("CONTENTS", &listopts->req_obj_ats); } /* Return the CONTENTS attribute. */ PATTRIB read_contents(char hsoname[]) { char *filename; PATTRIB retval; int fd; /* file descriptor to read from. */ struct stat file_stat_st; struct stat * file_stat = &file_stat_st; TOKEN tk; /* working token */ /* Code modified from dsrfinfo() */ /* Expand special file names */ if((*hsoname != '/') && *aftpdir && strnequal(hsoname,"AFTP",4)) filename = qsprintf_stcopyr("%s%s", aftpdir, hsoname + 4); else filename = stcopy(hsoname); fd = open(filename, 0); /* open for reading */ if (fd < 0) { stfree(filename); return NULL; } if (stat(filename,file_stat) != 0) { close(fd); stfree(filename); return NULL; } retval = atalloc(); retval->aname = stcopyr("CONTENTS", retval->aname); retval->precedence = ATR_PREC_OBJECT; retval->nature = ATR_NATURE_INTRINSIC; retval->avtype = ATR_SEQUENCE; /* Build a two-element sequence, DATA and a byte stream of the data. */ retval->value.sequence = tkappend("DATA", retval->value.sequence); tk = tkalloc(NULL); /* allocate one extra space for the trailing null. */ tk->token = stalloc(file_stat->st_size + 1); /* tk->token is the buffer we fill in. It remains a valid reference even after the APPEND_ITEM. We call APPEND_ITEM() first so that it's easy to abort in case of an error (one less freeing operation to call). */ APPEND_ITEM(tk, retval->value.sequence); /* Read whole contents and close the file. If either fails, don't set the CONTENTS attribute. */ /* This might fail IF the file size changes between the stat() and the read. Oh well. */ if(read(fd, tk->token, file_stat->st_size) != file_stat->st_size || close(fd)) { atfree(retval); close(fd); return NULL; } p_bst_set_buffer_length_nullterm(tk->token, file_stat->st_size); /* Automatically null terminates it for us too. */ return retval; }