From: Chaskiel Grundman Date: Fri, 2 Jul 2010 18:08:23 +0000 (-0400) Subject: libafscp: a library for "clientless" operations X-Git-Tag: openafs-devel-1_7_1~875 X-Git-Url: https://git.openafs.org/?p=openafs.git;a=commitdiff_plain;h=08b7ca67a781ec93ae71677dd165133d9679a9bc libafscp: a library for "clientless" operations libafscp provides the ability to accomplish many of the functions of an AFS client without a running afsd cache manager. It is being introduced into the OpenAFS source tree in order to improve the capabilities of several utilities but may have additional benefits for testing clients and servers and for use on platforms which do not have afsd support. Change-Id: I24ee9416662c15b2d267317cea6fe0b7439ee022 Change-Id: I61037736e6edd01106b5b5ed982b211c16e1e7d9 Reviewed-on: http://gerrit.openafs.org/2371 Reviewed-by: Chaz Chandler Tested-by: Chaz Chandler Tested-by: BuildBot Reviewed-by: Derrick Brashear Tested-by: Derrick Brashear --- diff --git a/src/libafscp/afscp.h b/src/libafscp/afscp.h new file mode 100644 index 0000000..ae926c7 --- /dev/null +++ b/src/libafscp/afscp.h @@ -0,0 +1,136 @@ +#ifndef _AFSCP_H_ +#define _AFSCP_H_ + +/* AUTORIGHTS */ +#include +#include +struct afs_server; +struct afs_cell; +struct afs_callback ; + +struct afs_volume +{ + struct afs_cell *cell; + afs_uint32 id; + int voltype; + int nservers; + int servers[16]; + char name[256]; + void *statcache; + void *dircache; +}; + +struct afs_venusfid +{ + struct afs_cell *cell; + struct AFSFid fid; +}; + +struct afs_dirent +{ + afs_uint32 vnode; + afs_uint32 unique; + char name[16+32*(64-2)]; /* 64 is EPP. */ +}; + +typedef struct afs_dirstream afs_DIR; +typedef struct afs_openfile afs_FILE; + +extern int afs_errno; +int afscp_init(const char *cellname); +void afscp_finalize(void); + +#ifdef API_COMPAT +#define GetCellByName afs_cellbyname +#define GetCellById afs_cellbyid +#define GetDefaultCell afs_defaultcell +#define SetDefaultCell afs_setdefaultcell +#define GetCellID afs_cellid +#endif +struct afs_cell *afs_defaultcell(void); +struct afs_cell *afs_cellbyname(const char *cellname) ; +int afs_setdefaultcell(const char *cellname); +struct afs_cell *afs_cellbyid(int id); +int afs_cellid(struct afs_cell *cell); + + +#ifdef API_COMPAT +#define GetServerById afs_serverbyid +#define GetServerByAddr afs_serverbyaddr +#define GetAnyServerById afs_anyserverbyaddr +#define GetAnyServerByIndex afs_serverbyindex +#define GetConnection afs_serverconnection +#endif + +struct afs_server *afs_serverbyid(struct afs_cell *thecell, afsUUID *u); +struct afs_server *afs_serverbyaddr(struct afs_cell *thecell, + afs_uint32 addr); +struct afs_server *afs_anyserverbyaddr(afs_uint32 addr) ; +struct afs_server *afs_serverbyindex(int idx) ; +struct rx_connection *afs_serverconnection(const struct afs_server *srv, + int i); + +int AddCallBack(const struct afs_server *server, const struct AFSFid *fid, + const struct AFSFetchStatus *st, const struct AFSCallBack *cb); +int RemoveCallBack(const struct afs_server *server, const struct afs_venusfid *fid); +int ReturnCallBacks(const struct afs_server *server); +int ReturnAllCallBacks(void); + + +/* file metastuff */ +/* frees with free() */ +struct afs_venusfid *makefid(struct afs_cell *cell, afs_uint32 volume, + afs_uint32 vnode, afs_uint32 unique); +struct afs_venusfid *dupfid(const struct afs_venusfid *in); + +struct stat; +int afs_stat(const struct afs_venusfid *fid, struct stat *s); + +ssize_t afs_pread(const struct afs_venusfid *fid, void *buffer, size_t count, off_t offset); +ssize_t afs_pwrite(const struct afs_venusfid *fid, const void *buffer, size_t count, off_t offset); +afs_FILE *afs_open(const char *path); +afs_FILE *afs_fidopen(const struct afs_venusfid *fid); +off_t afs_fseek (afs_FILE *f, off_t o, int whence); +ssize_t afs_fread(const afs_FILE *f, void *buffer, size_t count); +ssize_t afs_fwrite(const afs_FILE *f, const void *buffer, size_t count); + +/* rpc wrappers */ +int afs_GetStatus(const struct afs_venusfid *fid, struct AFSFetchStatus *s); +int afs_StoreStatus(const struct afs_venusfid *fid, struct AFSStoreStatus *s); +int afs_CreateFile(const struct afs_venusfid *fid, /* const */ char *name, + struct AFSStoreStatus *sst, + struct afs_venusfid **ret); +int afs_MakeDir(const struct afs_venusfid *fid, /* const */ char *name, + struct AFSStoreStatus *sst, + struct afs_venusfid **ret); +int afs_Symlink(const struct afs_venusfid *fid, /* const */ char *name, + /*const*/ char *target, struct AFSStoreStatus *sst); +int afs_RemoveFile(const struct afs_venusfid *dir, char *name); +int afs_RemoveDir(const struct afs_venusfid *dir, char *name); +int afs_FetchACL(const struct afs_venusfid *dir, + struct AFSOpaque *acl); +int afs_StoreACL(const struct afs_venusfid *dir, + struct AFSOpaque *acl); +/* directory parsing stuff*/ +struct afs_dirstream *afs_opendir(const struct afs_venusfid *fid); +struct afs_dirent *afs_readdir(struct afs_dirstream *d); +int afs_rewinddir(struct afs_dirstream *d); +int afs_closedir(struct afs_dirstream *d); +struct afs_venusfid *DirLookup(struct afs_dirstream *d, const char *name); +struct afs_venusfid *ResolveName(const struct afs_venusfid *dir, const char *name); +struct afs_venusfid *ResolvePath(const char *path); +struct afs_venusfid *ResolvePath2(const struct afs_volume *start, const char *path); + +/* vldb stuff */ +struct afs_volume *afs_volumebyname(struct afs_cell *cell, const char *vname, afs_int32 vtype); +struct afs_volume *afs_volumebyid(struct afs_cell *cell, afs_uint32 id); + +#define DIRMODE_CELL 0 +#define DIRMODE_DYNROOT 1 + +#define VOLTYPE_RW 0 +#define VOLTYPE_RO 1 +#define VOLTYPE_BK 2 +int SetDirMode(int mode); + +#endif diff --git a/src/libafscp/afscp_acl.c b/src/libafscp/afscp_acl.c new file mode 100644 index 0000000..393c6cd --- /dev/null +++ b/src/libafscp/afscp_acl.c @@ -0,0 +1,112 @@ +/* AUTORIGHTS +Copyright (C) 2003 - 2010 Chaskiel Grundman +All rights reserved + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include "afscp.h" +#include "afscp_internal.h" + + +int afs_FetchACL(const struct afs_venusfid *dir, + struct AFSOpaque *acl) { + int code, i, j; + struct AFSFid df = dir->fid; + struct afs_volume *vol; + struct AFSFetchStatus dfst; + struct AFSVolSync vs; + struct afs_volume *volume; + struct afs_server *server; + + vol=afs_volumebyid(dir->cell, dir->fid.Volume); + if (!vol) { + afs_errno=ENOENT; + return -1; + } + code=ENOENT; + for (i=0;inservers;i++) { + server=afs_serverbyindex(vol->servers[i]); + if (server && server->naddrs > 0) { + for (j=0;j < server->naddrs;j++) { + code=RXAFS_FetchACL(server->conns[j], &df, acl, &dfst, &vs); + if (code >= 0) + break; + } + } + if (code >= 0) + break; + } + if (code) { + _StatInvalidate(dir); + afs_errno=code; + return -1; + } + _StatStuff(dir, &dfst); + return 0; +} + + +int afs_StoreACL(const struct afs_venusfid *dir, + struct AFSOpaque *acl) { + int code, i, j; + struct AFSFid df = dir->fid; + struct afs_volume *vol; + struct AFSFetchStatus dfst; + struct AFSVolSync vs; + struct afs_volume *volume; + struct afs_server *server; + + vol=afs_volumebyid(dir->cell, dir->fid.Volume); + if (!vol) { + afs_errno=ENOENT; + return -1; + } + code=ENOENT; + for (i=0;inservers;i++) { + server=afs_serverbyindex(vol->servers[i]); + if (server && server->naddrs > 0) { + for (j=0;j < server->naddrs;j++) { + code=RXAFS_StoreACL(server->conns[j], &df, acl, &dfst, &vs); + if (code >= 0) + break; + } + } + if (code >= 0) + break; + } + if (code) { + _StatInvalidate(dir); + afs_errno=code; + return -1; + } + _StatStuff(dir, &dfst); + return 0; +} diff --git a/src/libafscp/afscp_callback.c b/src/libafscp/afscp_callback.c new file mode 100644 index 0000000..f001b3c --- /dev/null +++ b/src/libafscp/afscp_callback.c @@ -0,0 +1,456 @@ +/* AUTORIGHTS +Copyright (C) 2003 - 2010 Chaskiel Grundman +All rights reserved + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#include +#include /*Callback interface defs*/ +#include +#include +#include +#include "afscp.h" +#include "afscp_internal.h" + +int afs_cb_inited = 0; +struct interfaceAddr afs_cb_interface; +static int afs_maxcallbacks=0, afs_cballoced=0; +struct afs_callback *allcallbacks=NULL; + +static int init_afs_cb() { + int count; + + afs_uuid_create(&afs_cb_interface.uuid); + count = rx_getAllAddr(&afs_cb_interface.addr_in, AFS_MAX_INTERFACE_ADDR); + if ( count <= 0 ) + afs_cb_interface.numberOfInterfaces = 0; + else + afs_cb_interface.numberOfInterfaces = count; + afs_cb_inited = 1; + return 0; +} + +int AddCallBack(const struct afs_server *server, const struct AFSFid *fid, + const struct AFSFetchStatus *fst, const struct AFSCallBack *cb) +{ + int i; + struct afs_callback *use=NULL, *newlist; + struct afs_venusfid f; + time_t now; + + time(&now); + + for (i=0;icell); + memcpy(&f.fid, &allcallbacks[i].fid, sizeof(struct afs_venusfid)); + _StatInvalidate(&f); + } + allcallbacks[i].valid=0; + + } + + if (allcallbacks[i].valid == 0) + use=&allcallbacks[i]; + if (allcallbacks[i].server==server && + fid->Volume == allcallbacks[i].fid.Volume && + fid->Vnode == allcallbacks[i].fid.Vnode && + fid->Unique == allcallbacks[i].fid.Unique) { + use=&allcallbacks[i]; + break; + } + } + if (!use) { + if (afs_maxcallbacks >= afs_cballoced) { + if (afs_cballoced) + afs_cballoced = afs_cballoced *2; + else + afs_cballoced = 4; + newlist=realloc(allcallbacks, afs_cballoced * + sizeof(struct afs_callback)); + if (!newlist) + return -1; + allcallbacks=newlist; + } + use=&allcallbacks[afs_maxcallbacks++]; + } + use->valid=1; + use->server=server; + memmove(&use->fid, fid, sizeof(struct AFSFid)); + memmove(&use->cb, cb, sizeof(struct AFSCallBack)); + f.cell=afs_cellbyid(server->cell); + memcpy(&f.fid, fid, sizeof(struct AFSFid)); + _StatStuff(&f, fst); + return 0; +} +int RemoveCallBack(const struct afs_server *server, const struct afs_venusfid *f) +{ + struct afs_callback *cb; + int i; + + _StatInvalidate(f); + if (!server) + return 0; + for (i=0;iserver == server && + f->fid.Volume == cb->fid.Volume && + f->fid.Vnode == cb->fid.Vnode && + f->fid.Unique == cb->fid.Unique) { + cb->valid = 0; + break; + } + } + return 0; +} + +int ReturnCallBacks(const struct afs_server *server) +{ + struct AFSCBFids theFids; + struct AFSCBs theCBs; + struct afs_callback *cb; + struct afs_venusfid f; + int inited=0; + int ncallbacks=0; + int i,j; + time_t now; + + time(&now); + + for (i=0;iserver, cb->cb.ExpirationTime, + cb->valid);*/ + if (cb->server != server) + continue; + if (cb->cb.ExpirationTime < now) { + if (cb->valid) { + f.cell=afs_cellbyid(cb->server->cell); + memcpy(&f.fid, &cb->fid, sizeof(struct afs_venusfid)); + _StatInvalidate(&f); + } + + cb->valid=0; + continue; + } + if (!inited) { + theFids.AFSCBFids_val=malloc(sizeof(struct AFSCallBack) * AFSCBMAX); + if (!theFids.AFSCBFids_val) + return -1; + theCBs.AFSCBs_val=malloc(sizeof(struct AFSFid) * AFSCBMAX); + if (!theCBs.AFSCBs_val) { + free(theFids.AFSCBFids_val); + return -1; + } + } + + if (ncallbacks == AFSCBMAX) { + theFids.AFSCBFids_len=ncallbacks; + theCBs.AFSCBs_len=ncallbacks; + for (j=0;jnaddrs;j++) { + if (!RXAFS_GiveUpCallBacks(server->conns[j], &theFids, + &theCBs)) + break; + } + ncallbacks=0; + } + memmove(&theFids.AFSCBFids_val[ncallbacks], &cb->fid, + sizeof(struct AFSFid)); + memmove(&theCBs.AFSCBs_val[ncallbacks], &cb->cb, + sizeof(struct AFSCallBack)); + + theCBs.AFSCBs_val[ncallbacks].CallBackType = CB_DROPPED; + ncallbacks++; + if (cb->valid) { + f.cell=afs_cellbyid(cb->server->cell); + memcpy(&f.fid, &cb->fid, sizeof(struct afs_callback)); + _StatInvalidate(&f); + } + + cb->valid=0; + } + if (ncallbacks) { + theFids.AFSCBFids_len=ncallbacks; + theCBs.AFSCBs_len=ncallbacks; + for (j=0;jnaddrs;j++) { + if (!RXAFS_GiveUpCallBacks(server->conns[j], &theFids, + &theCBs)) + break; + } + free(theFids.AFSCBFids_val); + free(theCBs.AFSCBs_val); + } + return 0; +} + +int ReturnAllCallBacks(void) +{ + struct afs_server *s; + int i; + + for (i=0;(s=afs_serverbyindex(i));i++) + ReturnCallBacks(s); + return 0; +} + + + +afs_int32 SRXAFSCB_CallBack(rxcall, Fids_Array, CallBack_Array) + struct rx_call *rxcall; + AFSCBFids *Fids_Array; + AFSCBs *CallBack_Array; + +{ /*SRXAFSCB_CallBack*/ + struct rx_connection *rxconn=rx_ConnectionOf(rxcall); + struct rx_peer *rxpeer=rx_PeerOf(rxconn); + struct afs_server *server=afs_anyserverbyaddr(rxpeer->host); + struct afs_callback *cb; + struct afs_venusfid f; + struct AFSFid *fid; + int i,j; + + if (!server) + return 0; + for (i=0;iserver != server) + continue; + for (j=0;jAFSCBFids_len;j++) { + fid=&Fids_Array->AFSCBFids_val[j]; + if (fid->Volume == cb->fid.Volume && + fid->Vnode == cb->fid.Vnode && + fid->Unique == cb->fid.Unique) + cb->valid = 0; + f.cell=afs_cellbyid(cb->server->cell); + memcpy(&f.fid, &cb->fid, sizeof(struct afs_venusfid)); + _StatInvalidate(&f); + } + } + + return(0); + +} /*SRXAFSCB_CallBack*/ + + +afs_int32 SRXAFSCB_InitCallBackState(rxcall) + struct rx_call *rxcall; + +{ /*SRXAFSCB_InitCallBackState*/ + struct rx_connection *rxconn=rx_ConnectionOf(rxcall); + struct rx_peer *rxpeer=rx_PeerOf(rxconn); + struct afs_server *server=afs_anyserverbyaddr(rxpeer->host); + struct afs_callback *cb; + struct afs_venusfid f; + int i; + + if (!server) + return 0; + for (i=0;iserver != server) + continue; + if (cb->valid) { + f.cell=afs_cellbyid(cb->server->cell); + memcpy(&f.fid, &cb->fid, sizeof(struct afs_callback)); + _StatInvalidate(&f); + } + cb->valid = 0; + } + return(0); + +} /*SRXAFSCB_InitCallBackState*/ + +afs_int32 SRXAFSCB_Probe(rxcall) + struct rx_call *rxcall; + +{ /*SRXAFSCB_Probe*/ + return(0); + +} /*SRXAFSCB_Probe*/ + + +afs_int32 SRXAFSCB_GetCE(rxcall) + struct rx_call *rxcall; + +{ /*SRXAFSCB_GetCE*/ + return(0); +} /*SRXAFSCB_GetCE*/ + +afs_int32 SRXAFSCB_GetCE64(rxcall) + struct rx_call *rxcall; + +{ /*SRXAFSCB_GetCE*/ + return(0); +} /*SRXAFSCB_GetCE*/ + + +afs_int32 SRXAFSCB_GetLock(rxcall) + struct rx_call *rxcall; + +{ /*SRXAFSCB_GetLock*/ + return(0); + +} /*SRXAFSCB_GetLock*/ +afs_int32 SRXAFSCB_XStatsVersion(rxcall) + struct rx_call *rxcall; + +{ /*SRXAFSCB_XStatsVersion*/ + return(0); + +} /*SRXAFSCB_XStatsVersion*/ + +afs_int32 SRXAFSCB_GetXStats(rxcall) + struct rx_call *rxcall; + +{ /*SRXAFSCB_GetXStats*/ + return(0); +} /*SRXAFSCB_GetXStats*/ + +int SRXAFSCB_InitCallBackState2(rxcall, addr) +struct rx_call *rxcall; +struct interfaceAddr * addr; +{ + return RXGEN_OPCODE; +} + +int SRXAFSCB_WhoAreYou(rxcall, addr) +struct rx_call *rxcall; +struct interfaceAddr *addr; +{ + XDR x; + if ( rxcall && addr ) + { + if (!afs_cb_inited) init_afs_cb(); + *addr = afs_cb_interface; + } +#ifdef STRANGEDEBUG + xdrrx_create(&x, rxcall, XDR_ENCODE); + xdr_interfaceAddr(&x, addr); + rx_Write(rxcall,"",0); + rxi_FlushWrite(rxcall); + rx_EndCall(rxcall, 0); + IOMGR_Sleep(10); + IOMGR_Sleep(600); +#endif + return(0); +} + +int SRXAFSCB_InitCallBackState3(rxcall, uuidp) +struct rx_call *rxcall; +afsUUID *uuidp; +{ + struct rx_connection *rxconn=rx_ConnectionOf(rxcall); + struct rx_peer *rxpeer=rx_PeerOf(rxconn); + struct afs_server *server=afs_anyserverbyaddr(rxpeer->host); + struct afs_callback *cb; + struct afs_venusfid f; + int i; + + if (!server) + return 0; + for (i=0;iserver != server) + continue; + if (cb->valid) { + f.cell=afs_cellbyid(cb->server->cell); + memcpy(&f.fid, &cb->fid, sizeof(struct afs_callback)); + _StatInvalidate(&f); + } + cb->valid = 0; + } + return(0); +} +int SRXAFSCB_ProbeUuid(rxcall, uuidp) +struct rx_call *rxcall; +afsUUID *uuidp; +{ + int code = 0; + if (!afs_cb_inited) init_afs_cb(); + if (!afs_uuid_equal(uuidp, &afs_cb_interface.uuid)) + code = 1; /* failure */ +#ifdef STRANGEDEBUG + rx_EndCall(rxcall,code); + IOMGR_Sleep(600); +#endif + return code; +} +int SRXAFSCB_GetServerPrefs( + struct rx_call *a_call, + afs_int32 a_index, + afs_int32 *a_srvr_addr, + afs_int32 *a_srvr_rank) +{ + *a_srvr_addr = 0xffffffff; + *a_srvr_rank = 0xffffffff; + return 0; +} + +int SRXAFSCB_GetCellServDB( + struct rx_call *a_call, + afs_int32 a_index, + char **a_name, + afs_int32 *a_hosts) +{ + return RXGEN_OPCODE; +} + +int SRXAFSCB_GetLocalCell( + struct rx_call *a_call, + char **a_name) +{ + return RXGEN_OPCODE; +} + +int SRXAFSCB_GetCacheConfig( + struct rx_call *a_call, + afs_uint32 callerVersion, + afs_uint32 *serverVersion, + afs_uint32 *configCount, + cacheConfig *config) +{ + return RXGEN_OPCODE; +} +int SRXAFSCB_GetCellByNum( + struct rx_call *a_call, + afs_int32 a_index, + char **a_name, + afs_int32 *a_hosts) +{ + return RXGEN_OPCODE; +} +#ifdef AFS_64BIT_CLIENT +afs_int32 +SRXAFSCB_TellMeAboutYourself(struct rx_call * rxcall, + struct interfaceAddr * addr, + Capabilities * capabilities) +{ + if ( rxcall && addr ) + { + if (!afs_cb_inited) init_afs_cb(); + *addr = afs_cb_interface; + } + return(0); +} +#endif diff --git a/src/libafscp/afscp_dir.c b/src/libafscp/afscp_dir.c new file mode 100644 index 0000000..d761a0b --- /dev/null +++ b/src/libafscp/afscp_dir.c @@ -0,0 +1,670 @@ +/* AUTORIGHTS +Copyright (C) 2003 - 2010 Chaskiel Grundman +All rights reserved + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "afscp.h" +#include "afscp_internal.h" + +static int dirmode=DIRMODE_CELL; + +int SetDirMode(int mode) { + + if (mode != DIRMODE_CELL && mode != DIRMODE_DYNROOT) { + afs_errno=EINVAL; + return -1; + } + dirmode=mode; + return 0; +} + +/* comparison function for tsearch */ +static int dircompare(const void *a, const void *b) +{ + const struct afs_dircache *sa=a,*sb=b; + if (sa->me.fid.Vnode < sb->me.fid.Vnode) return -1; + if (sa->me.fid.Vnode > sb->me.fid.Vnode) return 1; + if (sa->me.fid.Unique < sb->me.fid.Unique) return -1; + if (sa->me.fid.Unique > sb->me.fid.Unique) return 1; + return 0; +} + +/* make sure the dirstream contains the most up to date directory contents */ +static int _DirUpdate (struct afs_dirstream *d) { + struct AFSFetchStatus s; + int code; + struct afs_volume *v; + struct afs_dircache key, *stored; + void **cached; + + + code=afs_GetStatus(&d->fid, &s); + if (code) + return code; + + if (d->dirbuffer && d->dv == s.DataVersion) + return 0; + v=afs_volumebyid(d->fid.cell, d->fid.fid.Volume); + if (!v) { + afs_errno=ENOENT; + return -1; + } + + memcpy(&key.me, &d->fid, sizeof(struct afs_venusfid)); + cached=tfind(&key, &v->dircache, dircompare); + if (cached) { + stored=*(struct afs_dircache **)cached; + if (d->dv == s.DataVersion) { + d->dirbuffer = stored->dirbuffer; + d->buflen = stored->buflen; + d->dv = stored->dv; + return 0; + } + tdelete(&key, &v->dircache, dircompare); + if (d->dirbuffer != stored->dirbuffer) + free(stored->dirbuffer); + free(stored); + } + if (s.Length > BIGMAXPAGES * AFS_PAGESIZE) { + afs_errno=EFBIG; + return -1; + } + if (d->buflen != s.Length) { + char *new; + if (d->dirbuffer) { + new=realloc(d->dirbuffer, s.Length); + } else { + new=malloc(s.Length); + } + if (new) { + d->dirbuffer=new; + } else { + afs_errno=ENOMEM; + return -1; + } + d->buflen=s.Length; + } + + code=afs_pread(&d->fid, d->dirbuffer, s.Length, 0); + if (code < 0) { + return -1; + } + d->dv=s.DataVersion; + cached=tsearch(&key, &v->dircache, dircompare); + if (cached) { + stored=malloc(sizeof(struct afs_dircache)); + if (stored) { + memcpy(&stored->me, &d->fid, sizeof(struct afs_venusfid)); + stored->buflen=d->buflen; + stored->dirbuffer=d->dirbuffer; + stored->dv=d->dv; + *(struct afs_dircache **)cached=stored; + } else { + tdelete(&key, &v->dircache, dircompare); + } + } + return 0; +} + + +static struct DirEntry *dir_get_entry(struct afs_dirstream *d, int entry) { + + struct DirHeader *h=(struct DirHeader *)d->dirbuffer; + struct PageHeader *p; + struct DirEntry *ret; + int fr; + int pg, off; + + + pg=entry >> LEPP; + off=entry & (EPP-1); + + if (pg * AFS_PAGESIZE >= d->buflen) { /* beyond end of file */ + return NULL; + } + if (!off || (!pg && off < DHE + 1)) { /* offset refers to metadata */ + return NULL; + } + if (pg < MAXPAGES && h->alloMap[pg] == EPP) { /* page is empty */ + return NULL; + } + p=(struct PageHeader *)&d->dirbuffer[pg * AFS_PAGESIZE]; + fr=p->freebitmap[off >> 8]; +#if 0 + if ((fr & (1<<(off & 7))) == 0 ) { /* entry isn't allocated */ + return NULL; + } +#endif + + ret=(struct DirEntry *)&d->dirbuffer[pg * AFS_PAGESIZE + 32 * off]; + + return ret; +} + +struct afs_dirstream *afs_opendir(const struct afs_venusfid *fid) { + struct afs_dirstream *ret; + struct AFSFetchStatus s; + int code; + + code=afs_GetStatus(fid, &s); + if (code) { + return NULL; + } + + if (s.FileType != Directory) { + afs_errno=ENOTDIR; + return NULL; + } + ret=malloc(sizeof(struct afs_dirstream)); + if (!ret) { + afs_errno=ENOMEM; + return NULL; + } + memset(ret,0,sizeof(struct afs_dirstream)); + memmove(&ret->fid, fid, sizeof(struct afs_venusfid)); + code=_DirUpdate(ret); + if (code < 0) { + afs_closedir(ret); + return NULL; + } + ret->hashent=-1; + ret->entry=0; + return ret; +} + +struct afs_dirent *afs_readdir(struct afs_dirstream *d) { + struct DirHeader *h=(struct DirHeader *)d->dirbuffer; + struct DirEntry *info; + int ent; + + + ent=d->entry; + while (ent == 0 && d->hashent < NHASHENT-1) { + d->hashent++; + ent=ntohs(h->hashTable[d->hashent]); + } + if (ent == 0) { + afs_errno=0; + return NULL; + } + info=dir_get_entry(d, ent); + if (!info) { + afs_errno=0; + return NULL; + } + d->ret.vnode=ntohl(info->fid.vnode); + d->ret.unique=ntohl(info->fid.vunique); + strcpy(d->ret.name, info->name); /* guaranteed to be NULL terminated? */ + d->entry=ntohs(info->next); + + return &d->ret; +} + +/* as it calls _DirUpdate, this may corrupt any previously returned dirent's */ +int afs_rewinddir(struct afs_dirstream *d) { + _DirUpdate(d); + d->hashent=-1; + d->entry=0; + return 0; +} +int afs_closedir(struct afs_dirstream *d) { + free(d); + return 0; +} + +static int namehash(const char *name) +{ + int hval, tval; + + hval=0; + while (*name) hval = (hval * 173) + *name++; + tval = hval & (NHASHENT - 1); + return tval ? + (hval < 0 ? NHASHENT - tval : tval) + : 0; +} + + +struct afs_venusfid *DirLookup(struct afs_dirstream *d, const char *name) { + int fid[3]; + int code; + int hval, entry; + struct DirHeader *h=(struct DirHeader *)d->dirbuffer; + struct DirEntry *info; + + code=_DirUpdate(d); + if (code) + return NULL; + hval=namehash(name); + entry=ntohs(h->hashTable[hval]); + + while (entry) { + info=dir_get_entry(d, entry); + if (!info) { + afs_errno=EIO; + return NULL; + } + if (!strcmp(info->name, name)) + break; + entry=ntohs(info->next); + } + if (entry) { + return makefid(d->fid.cell, d->fid.fid.Volume, + ntohl(info->fid.vnode), + ntohl(info->fid.vunique)); + } else { + afs_errno=ENOENT; + return NULL; + } +} + +struct afs_venusfid *ResolveName(const struct afs_venusfid *dir, const char *name) { + struct afs_venusfid *ret; + struct afs_dirstream *d; + + d=afs_opendir(dir); + if (!d) + return NULL; + ret=DirLookup(d, name); + afs_closedir(d); + return ret; +} + +static int gettoproot(struct afs_cell *cell, char *p, char **q, struct afs_venusfid **root) { + struct afs_volume *rootvol; + char *r; + + if (dirmode == DIRMODE_DYNROOT && !strcmp(p, "/afs")) { + afs_errno=EINVAL; + return 1; + } + if (!strncmp(p,"/afs",4)) { + afscp_dprintf(("gettoproot: path is absolute\n")); + p=&p[5]; + while (*p == '/') p++; + if (dirmode == DIRMODE_DYNROOT) { + int voltype; + retry_dot: + voltype = VOLTYPE_RO; + if (*p == '.') { + p++; + voltype=VOLTYPE_RW; + } + if (*p == '/') { + while (*p == '/') p++; + goto retry_dot; + } + if (*p == '.' || *p == 0) { + afs_errno=EINVAL; + return 1; + } + r=p; + while (*r && *r != '/') r++; + if (!*r) { + afs_errno=ENODEV; + return 1; + } + *r++=0; + *q=r; + afscp_dprintf(("gettoproot: dynroot looking up cell %s\n", p)); + cell=afs_cellbyname(p); + if (!cell) { + afscp_dprintf(("gettoproot: no such cell\n")); + afs_errno=ENODEV; + return 1; + } + rootvol=afs_volumebyname(cell, "root.cell", voltype); + if (!rootvol && voltype == VOLTYPE_RO) + rootvol=afs_volumebyname(cell, "root.cell", VOLTYPE_RW); + } else { + *q=p; + rootvol=afs_volumebyname(cell, "root.afs", VOLTYPE_RO); + if (!rootvol) + rootvol=afs_volumebyname(cell, "root.afs", VOLTYPE_RW); + } + if (!rootvol) + afscp_dprintf(("gettoproot: volume not found\n")); + } else { + afscp_dprintf(("gettoproot: path is relative\n")); + if (p[0] == '/') { + afs_errno=EXDEV; + return 1; + } + rootvol=afs_volumebyname(cell, "root.cell", VOLTYPE_RO); + if (!rootvol) + rootvol=afs_volumebyname(cell, "root.cell", VOLTYPE_RW); + *q=p; + } + if (!rootvol) { afs_errno=ENODEV; return 1;} + *root=makefid(cell, rootvol->id, 1, 1); + return 0; +} + +static int getvolumeroot(struct afs_cell *cell, int voltype, const char *vname, struct afs_venusfid **root) { + struct afs_volume *vol; + vol=afs_volumebyname(cell, vname, voltype); + if (!vol && voltype == VOLTYPE_RO) + vol=afs_volumebyname(cell, vname, VOLTYPE_RW); + if (!vol) { afs_errno=ENODEV; return 1;} + *root=makefid(cell, vol->id, 1, 1); + return 0; +} + +typedef struct fidstack_s +{ + int alloc; + int count; + struct afs_venusfid ** entries; +} *fidstack; + +static fidstack fidstack_alloc() { + fidstack ret; + + ret=malloc(sizeof(struct fidstack_s)); + if (!ret) { + afs_errno=ENOMEM; + return NULL; + } + ret->alloc=10; + ret->count=0; + ret->entries=malloc(ret->alloc * sizeof(struct afs_venusfid *)); + if (!ret->entries) { + free(ret); + afs_errno=ENOMEM; + return NULL; + } + return ret; +} + +static void fidstack_push(fidstack s, struct afs_venusfid *entry) { + struct afs_venusfid **new; + if (s->count >= s->alloc) { + new=realloc(s->entries, (s->alloc + 10) * + sizeof(struct afs_venusfid *)); + if (!new) + return; + s->entries=new; + s->alloc += 10; + } + s->entries[s->count++]=entry; + return; +} + +static struct afs_venusfid *fidstack_pop(fidstack s) { + if (s->count) + return s->entries[-- s->count]; + return NULL; +} + +static void fidstack_free(fidstack s) { + int i; + + for (i=0;icount;i++) + free(s->entries[i]); + free (s->entries); + free(s); +} + +static struct afs_venusfid *_ResolvePath(const struct afs_venusfid *, fidstack, char *, int); + +static struct afs_venusfid *HandleLink(struct afs_venusfid *in, const struct afs_venusfid *parent, fidstack fids, int follow, + const struct AFSFetchStatus *s, int terminal) { + char *linkbuf, *linkbufq; + struct afs_cell *cell; + struct afs_volume *v; + struct afs_venusfid *root,*ret; + int voltype; + int code; + if ((s->UnixModeBits & 0111) && + (follow == 0) && + terminal) { /* normal link */ + return in; + } + linkbuf=malloc(s->Length + 1); + code=afs_pread(in, linkbuf, s->Length, 0); + if (code < 0) { + free(linkbuf); + free(in); + return NULL; + } + if (code != s->Length) { + afs_errno=EIO; + free(linkbuf); + free(in); + return NULL; + } + linkbuf[s->Length]=0; + if (s->UnixModeBits & 0111) { /* normal link */ + afscp_dprintf(("Recursing on symlink %s...\n", linkbuf)); + if (linkbuf[0] == '/') { + if (gettoproot(in->cell, linkbuf, &linkbufq, &root)) { + free(linkbuf); + free(in); + return NULL; + } + free(in); + ret=_ResolvePath(root, 0, linkbufq, 0); + free(root); + } else { + free(in); + ret=_ResolvePath(parent, fids, linkbuf, 0); + } + + free(linkbuf); + + } else { /* mountpoint */ + afscp_dprintf(("EvalMountPoint %s...\n", linkbuf)); + linkbufq=strchr(linkbuf, ':'); + cell=in->cell; + v=afs_volumebyid(cell, in->fid.Volume); + free(in); + if (!v) { + free(linkbuf); + afs_errno=ENODEV; + return NULL; + } + voltype=v->voltype; + if (linkbuf[0] == '%') + voltype=VOLTYPE_RW; + if (!linkbufq) { + linkbufq=linkbuf+1; + } else { + *linkbufq++ = 0; + cell=afs_cellbyname(linkbuf+1); + if (linkbuf[0] != '%') + voltype=VOLTYPE_RO; + } + if (!cell) { + free(linkbuf); + afs_errno=ENODEV; + return NULL; + } + if (strlen(linkbufq) < 2) { + free(linkbuf); + afs_errno=ENODEV; + return NULL; + } + linkbufq[strlen(linkbufq)-1]=0; /* eliminate trailer */ + if (getvolumeroot(cell, voltype, linkbufq, &ret)) { + free(linkbuf); + return NULL; + } + free(linkbuf); + } + return ret; +} + +static struct afs_venusfid *_ResolvePath(const struct afs_venusfid *start, + fidstack infids, char *path, + int follow) { + struct afs_venusfid *ret,*cwd,*parent; + struct AFSFetchStatus s; + char *p,*q; + int code; + int linkcount; + fidstack fids; + + p=path; + ret=cwd=dupfid(start); + fids=infids; + if (!fids) + fids=fidstack_alloc(); + if (!fids) + return NULL; + + while (p && *p) { + q=strchr(p, '/'); + if (q) *q++=0; + if (!strcmp(p,".")) { + } else if (!strcmp(p,"..")) { + ret=fidstack_pop(fids); + if (!ret) + ret=cwd; + else + free(cwd); + } else { + ret=ResolveName(cwd, p); + if (!ret) { + afscp_dprintf(("Lookup %s in %lu.%lu.%lu failed\n", p, cwd->fid.Volume, cwd->fid.Vnode, cwd->fid.Unique)); + free(cwd); + if (!infids) + fidstack_free(fids); + return NULL; + + } + afscp_dprintf(("Lookup %s in %lu.%lu.%lu->%lu.%lu.%lu\n", p, cwd->fid.Volume, cwd->fid.Vnode, cwd->fid.Unique, ret->fid.Volume, ret->fid.Vnode, ret->fid.Unique)); + linkcount=0; + + retry: + if ((ret->fid.Vnode & 1) == 0) { /* not a directory; check for link */ + code=afs_GetStatus(ret, &s); + if (code) { + if (!infids) + fidstack_free(fids); + free(cwd); + free(ret); + return NULL; + } + if (s.FileType == SymbolicLink) { + if (linkcount++ > 5) { + afs_errno=ELOOP; + if (!infids) + fidstack_free(fids); + free(cwd); + free(ret); + return NULL; + } + ret=HandleLink(ret, cwd, fids, follow, &s, (q==NULL)); + if (!ret) { + free(cwd); + if (!infids) + fidstack_free(fids); + return NULL; + } + afscp_dprintf((" ....-> %lu.%lu.%lu\n", ret->fid.Volume, ret->fid.Vnode, ret->fid.Unique)); + goto retry; + } else { + if (q) { + afs_errno=ENOTDIR; + free(cwd); + free(ret); + if (!infids) + fidstack_free(fids); + return NULL; + } + } + } + fidstack_push(fids,cwd); + } + cwd=ret; + + if (q) + while (*q == '/') + q++; + p=q; + } + if (!infids) + fidstack_free(fids); + return ret; +} +/* 3 cases: + begins with /afs: start in root.afs of cell or home cell + else begins with /: error + else start in root.cell of cell or home cell +*/ +struct afs_venusfid *ResolvePath(const char *path) { + struct afs_venusfid *root,*ret; + struct afs_cell *cell; + + char *p,*q; + /* so we can modify the string */ + p=strdup(path); + if (!p) { + afs_errno=ENOMEM; + return NULL; + } + cell=afs_defaultcell(); + if (!cell) { afs_errno=EINVAL; return NULL;} + if (gettoproot(cell, p, &q, &root)) { + free(p); + return NULL; + } + if (q && *q) { + ret=_ResolvePath(root, 0, q, 1); + free(root); + } else + ret=root; + free(p); + return ret; +} + +struct afs_venusfid *ResolvePath2(const struct afs_volume *v, const char *path) { + struct afs_venusfid *root,*ret; + + char *p,*q; + /* so we can modify the string */ + p=strdup(path); + if (!p) { + afs_errno=ENOMEM; + return NULL; + } + root=makefid(v->cell, v->id, 1,1); + while (*p == '/') p++; + if (*p) { + ret=_ResolvePath(root, 0, p, 1); + free(root); + } else + ret=root; + free(p); + return ret; +} diff --git a/src/libafscp/afscp_dirops.c b/src/libafscp/afscp_dirops.c new file mode 100644 index 0000000..b2fa2e7 --- /dev/null +++ b/src/libafscp/afscp_dirops.c @@ -0,0 +1,243 @@ +/* AUTORIGHTS +Copyright (C) 2003 - 2010 Chaskiel Grundman +All rights reserved + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "afscp.h" +#include "afscp_internal.h" + + +int afs_CreateFile(const struct afs_venusfid *dir, char *name, + struct AFSStoreStatus *sst, + struct afs_venusfid **ret) { + int code, i, j; + struct AFSFid df = dir->fid; + struct afs_volume *vol; + struct AFSFetchStatus dfst, fst; + struct AFSVolSync vs; + struct AFSCallBack cb; + struct AFSFid ff; + struct afs_volume *volume; + struct afs_server *server; + struct rx_call *c; + + vol=afs_volumebyid(dir->cell, dir->fid.Volume); + if (!vol) { + afs_errno=ENOENT; + return -1; + } + code=ENOENT; + for (i=0;inservers;i++) { + server=afs_serverbyindex(vol->servers[i]); + if (server && server->naddrs > 0) { + for (j=0;j < server->naddrs;j++) { + code=RXAFS_CreateFile(server->conns[j], &df, name, sst, &ff, + &fst, &dfst, &cb, &vs); + if (code >= 0) + break; + } + } + if (code >= 0) + break; + } + if (code) { + _StatInvalidate(dir); + afs_errno=code; + return -1; + } + _StatStuff(dir, &dfst); + AddCallBack(server, &ff, &fst, &cb); + if (ret) + *ret=makefid(vol->cell, ff.Volume, ff.Vnode, ff.Unique); + return 0; +} + +int afs_MakeDir(const struct afs_venusfid *dir, char *name, + struct AFSStoreStatus *sst, + struct afs_venusfid **ret) { + int code, i, j; + struct AFSFid df = dir->fid; + struct afs_volume *vol; + struct AFSFetchStatus dfst, fst; + struct AFSVolSync vs; + struct AFSCallBack cb; + struct AFSFid ff; + struct afs_volume *volume; + struct afs_server *server; + + vol=afs_volumebyid(dir->cell, dir->fid.Volume); + if (!vol) { + afs_errno=ENOENT; + return -1; + } + code=ENOENT; + for (i=0;inservers;i++) { + server=afs_serverbyindex(vol->servers[i]); + if (server && server->naddrs > 0) { + for (j=0;j < server->naddrs;j++) { + code=RXAFS_MakeDir(server->conns[j], &df, name, sst, &ff, + &fst, &dfst, &cb, &vs); + if (code >= 0) + break; + } + } + if (code >= 0) + break; + } + if (code) { + _StatInvalidate(dir); + afs_errno=code; + return -1; + } + _StatStuff(dir, &dfst); + AddCallBack(server, &ff, &fst, &cb); + if (ret) + *ret=makefid(vol->cell, ff.Volume, ff.Vnode, ff.Unique); + return 0; +} + +int afs_Symlink(const struct afs_venusfid *dir, char *name, + char *target, + struct AFSStoreStatus *sst) { + int code, i, j; + struct AFSFid df = dir->fid; + struct afs_volume *vol; + struct AFSFetchStatus dfst, fst; + struct AFSVolSync vs; + struct AFSCallBack cb; + struct AFSFid ff; + struct afs_volume *volume; + struct afs_server *server; + + vol=afs_volumebyid(dir->cell, dir->fid.Volume); + if (!vol) { + afs_errno=ENOENT; + return -1; + } + code=ENOENT; + for (i=0;inservers;i++) { + server=afs_serverbyindex(vol->servers[i]); + if (server && server->naddrs > 0) { + for (j=0;j < server->naddrs;j++) { + code=RXAFS_Symlink(server->conns[j], &df, name, target, sst, &ff, + &fst, &dfst, &vs); + if (code >= 0) + break; + } + } + if (code >= 0) + break; + } + if (code) { + _StatInvalidate(dir); + afs_errno=code; + return -1; + } + _StatStuff(dir, &dfst); + return 0; +} + + +int afs_RemoveFile(const struct afs_venusfid *dir, char *name) { + int code, i, j; + struct AFSFid df = dir->fid; + struct afs_volume *vol; + struct AFSFetchStatus dfst; + struct AFSVolSync vs; + struct AFSCallBack cb; + struct afs_volume *volume; + struct afs_server *server; + + vol=afs_volumebyid(dir->cell, dir->fid.Volume); + if (!vol) { + afs_errno=ENOENT; + return -1; + } + code=ENOENT; + for (i=0;inservers;i++) { + server=afs_serverbyindex(vol->servers[i]); + if (server && server->naddrs > 0) { + for (j=0;j < server->naddrs;j++) { + code=RXAFS_RemoveFile(server->conns[j], &df, name, &dfst, &vs); + if (code >= 0) + break; + } + } + if (code >= 0) + break; + } + if (code) { + _StatInvalidate(dir); + afs_errno=code; + return -1; + } + _StatStuff(dir, &dfst); + return 0; +} + +int afs_RemoveDir(const struct afs_venusfid *dir, char *name) { + int code, i, j; + struct AFSFid df = dir->fid; + struct afs_volume *vol; + struct AFSFetchStatus dfst; + struct AFSVolSync vs; + struct AFSCallBack cb; + struct afs_volume *volume; + struct afs_server *server; + + vol=afs_volumebyid(dir->cell, dir->fid.Volume); + if (!vol) { + afs_errno=ENOENT; + return -1; + } + code=ENOENT; + for (i=0;inservers;i++) { + server=afs_serverbyindex(vol->servers[i]); + if (server && server->naddrs > 0) { + for (j=0;j < server->naddrs;j++) { + code=RXAFS_RemoveDir(server->conns[j], &df, name, &dfst, &vs); + if (code >= 0) + break; + } + } + if (code >= 0) + break; + } + if (code) { + _StatInvalidate(dir); + afs_errno=code; + return -1; + } + _StatStuff(dir, &dfst); + return 0; +} diff --git a/src/libafscp/afscp_fid.c b/src/libafscp/afscp_fid.c new file mode 100644 index 0000000..a85fde2 --- /dev/null +++ b/src/libafscp/afscp_fid.c @@ -0,0 +1,240 @@ +/* AUTORIGHTS +Copyright (C) 2003 - 2010 Chaskiel Grundman +All rights reserved + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "afscp.h" +#include "afscp_internal.h" + +struct afs_venusfid *makefid(struct afs_cell *cell, afs_uint32 volume, + afs_uint32 vnode, afs_uint32 unique) +{ + struct afs_venusfid *ret; + + if (!cell) + return NULL; + ret=malloc(sizeof(struct afs_venusfid)); + if (!ret) { + afs_errno=errno; + return NULL; + } + ret->cell=cell; + ret->fid.Volume=volume; + ret->fid.Vnode=vnode; + ret->fid.Unique=unique; + return ret; +} + +struct afs_venusfid *dupfid(const struct afs_venusfid *in) +{ + struct afs_venusfid *ret; + + ret=malloc(sizeof(struct afs_venusfid)); + if (!ret) { + afs_errno=errno; + return NULL; + } + ret->cell=in->cell; + ret->fid.Volume=in->fid.Volume; + ret->fid.Vnode=in->fid.Vnode; + ret->fid.Unique=in->fid.Unique; + return ret; +} + + +static int statcompare(const void *a, const void *b) +{ + const struct afs_statent *sa=a,*sb=b; + if (sa->me.fid.Vnode < sb->me.fid.Vnode) return -1; + if (sa->me.fid.Vnode > sb->me.fid.Vnode) return 1; + if (sa->me.fid.Unique < sb->me.fid.Unique) return -1; + if (sa->me.fid.Unique > sb->me.fid.Unique) return 1; + return 0; +} + + +int afs_GetStatus(const struct afs_venusfid *fid, struct AFSFetchStatus *s) +{ + struct afs_volume *v; + struct afs_server *server; + struct AFSCallBack cb; + struct AFSVolSync vs; + struct AFSFid tf = fid->fid; + struct afs_statent *stored,key; + void *cached; + + int code,i,j; + + v=afs_volumebyid(fid->cell, fid->fid.Volume); + if (!v) + return -1; + memset(&key, 0, sizeof(key)); + memcpy(&key.me,fid,sizeof(*fid)); + + cached=tfind(&key, &v->statcache, statcompare); + if (cached) { + stored=*(struct afs_statent **)cached; + memmove(s, &stored->status,sizeof(*s)); + afscp_dprintf(("Stat %u.%lu.%lu.%lu returning cached result\n", + fid->cell->id, fid->fid.Volume, fid->fid.Vnode, fid->fid.Unique)); + return 0; + } + + + code=ENOENT; + for (i=0;inservers;i++) { + server=afs_serverbyindex(v->servers[i]); + if (server && server->naddrs > 0) { + for (j=0;j < server->naddrs;j++) { + code=RXAFS_FetchStatus(server->conns[j], &tf, s, &cb, &vs); + if (!code) { + AddCallBack(server, &fid->fid, s, &cb); /* calls _StatStuff */ + afscp_dprintf(("Stat %u.%" PRIu32 ".%" PRIu32 ".%" PRIu32 " ok: type %" PRId32 " size %" PRId32 "\n", + fid->cell->id, fid->fid.Volume, fid->fid.Vnode, fid->fid.Unique, s->FileType, s->Length)); + return 0; + } + } + } + } + afs_errno=code; + return -1; +} + + + +int afs_stat(const struct afs_venusfid *fid, struct stat *s) +{ + + struct AFSFetchStatus status; + int code; + + code=afs_GetStatus(fid, &status); + if (code) + return code; + + if (status.FileType==File) + s->st_mode=S_IFREG; + else if (status.FileType==Directory) + s->st_mode=S_IFDIR; + else if (status.FileType==SymbolicLink) + s->st_mode=S_IFLNK; + else { + afs_errno=EINVAL; + return -1; + } + s->st_mode |= (status.UnixModeBits & (~S_IFMT)); + s->st_nlink = status.LinkCount; + s->st_size =status.Length; + s->st_uid =status.Owner; + /*s->st_blksize=status.SegSize;*/ + s->st_atime=s->st_mtime=status.ClientModTime; + s->st_ctime=status.ServerModTime; + s->st_gid = status.Group; + return 0; +} + + +int _StatInvalidate(const struct afs_venusfid *fid) { + struct afs_volume *v; + struct afs_statent key; + void **cached; + + v=afs_volumebyid(fid->cell, fid->fid.Volume); + if (!v) + return -1; + memmove(&key.me,fid,sizeof(*fid)); + + cached=tfind(&key, &v->statcache, statcompare); + if (cached) { + free(*cached); + tdelete(&key, &v->statcache, statcompare); + } + return 0; +} + +int _StatStuff(const struct afs_venusfid *fid, const struct AFSFetchStatus *s) { + struct afs_volume *v; + struct afs_statent key, *stored; + void **cached; + + v=afs_volumebyid(fid->cell, fid->fid.Volume); + if (!v) + return -1; + memmove(&key.me,fid,sizeof(*fid)); + + cached=tsearch(&key, &v->statcache, statcompare); + if (cached) { + stored=malloc(sizeof(struct afs_statent)); + if (stored) { + memmove(&stored->me, fid, sizeof(*fid)); + memmove(&stored->status,s,sizeof(*s)); + *(struct afs_statent **)cached=stored; + } else { + tdelete(&key, &v->statcache, statcompare); + } + } + return 0; +} + +int afs_StoreStatus(const struct afs_venusfid *fid, struct AFSStoreStatus *s) +{ + struct afs_volume *v; + struct afs_server *server; + struct AFSCallBack cb; + struct AFSVolSync vs; + struct AFSFetchStatus fst; + struct AFSFid tf = fid->fid; + int code,i,j; + + v=afs_volumebyid(fid->cell, fid->fid.Volume); + if (!v) + return -1; + + + code=ENOENT; + for (i=0;inservers;i++) { + server=afs_serverbyindex(v->servers[i]); + if (server && server->naddrs > 0) { + for (j=0;j < server->naddrs;j++) { + code=RXAFS_StoreStatus(server->conns[j], &tf, s, &fst, &vs); + if (!code) { + _StatStuff(fid, &fst); /* calls _StatStuff */ + return 0; + } + } + } + } + afs_errno=code; + return -1; +} diff --git a/src/libafscp/afscp_file.c b/src/libafscp/afscp_file.c new file mode 100644 index 0000000..78d4124 --- /dev/null +++ b/src/libafscp/afscp_file.c @@ -0,0 +1,182 @@ +/* AUTORIGHTS +Copyright (C) 2003 - 2010 Chaskiel Grundman +All rights reserved + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#include +#include +#include +#include +#include +#include +#include +#include "afscp.h" +#include "afscp_internal.h" + +ssize_t afs_pread(const struct afs_venusfid *fid, void *buffer, size_t count, off_t offset) { + struct AFSFetchStatus fst; + struct AFSVolSync vs; + struct AFSCallBack cb; + struct AFSFid tf = fid->fid; + struct afs_volume *vol; + struct afs_server *server; + + struct rx_call *c=NULL; + int code,code2; + int i,j,bytes,totalbytes; + int bytesremaining; + char *p; + + vol=afs_volumebyid(fid->cell, fid->fid.Volume); + if (!vol) { + afs_errno=ENOENT; + return -1; + } + code=ENOENT; + for (i=0;inservers;i++) { + server=afs_serverbyindex(vol->servers[i]); + if (server && server->naddrs > 0) { + for (j=0;j < server->naddrs;j++) { + c=rx_NewCall(server->conns[j]); + if (c) { + p=buffer; + code=StartRXAFS_FetchData(c, &tf, offset, count); + if (code) { + code=rx_EndCall(c,code); + continue; + } + bytes=rx_Read(c,(char *)&bytesremaining,sizeof(afs_int32)); + if (bytes != sizeof(afs_int32)) { + code=rx_EndCall(c,bytes); + continue; + } + bytesremaining=ntohl(bytesremaining); + totalbytes=0; + while (bytesremaining > 0) { + bytes=rx_Read(c, p, bytesremaining); + if (bytes <= 0) + break; + p+=bytes; + totalbytes+=bytes; + bytesremaining-=bytes; + } + if (bytesremaining == 0) { + code2=EndRXAFS_FetchData(c, &fst, &cb, &vs); + if (code2 == 0) + AddCallBack(server, &fid->fid, &fst, &cb); + } + code=rx_EndCall(c, code2); + } + if (code == 0) + return totalbytes; + } + } + } + afs_errno=code; + return -1; +} + + + +ssize_t afs_pwrite(const struct afs_venusfid *fid, const void *buffer, size_t count, off_t offset) { + + struct AFSFetchStatus fst; + struct AFSStoreStatus sst; + struct AFSVolSync vs; + struct AFSCallBack cb; + struct AFSFid tf=fid->fid; + struct afs_volume *vol; + struct afs_server *server; + + struct rx_call *c=NULL; + int code,code2; + int i,j,bytes,totalbytes; + int bytesremaining; + const char *p; + size_t filesize; + time_t now; + + + vol=afs_volumebyid(fid->cell, fid->fid.Volume); + if (!vol) { + afs_errno=ENOENT; + return -1; + } + if (vol->voltype != VOLTYPE_RW) { + afs_errno = EROFS; + return -1; + } + + code=ENOENT; + for (i=0;inservers;i++) { + server=afs_serverbyindex(vol->servers[i]); + if (server && server->naddrs > 0) { + for (j=0;j < server->naddrs;j++) { + code=RXAFS_FetchStatus(server->conns[j], &tf, &fst, &cb, &vs); + if (code) + continue; + sst.Mask=AFS_SETMODTIME; + time(&now); + sst.ClientModTime = now; + filesize=fst.Length; + if (offset + count > filesize) + filesize = offset + count; + c=rx_NewCall(server->conns[j]); + if (c) { + p=buffer; + code=StartRXAFS_StoreData(c, &tf, &sst, offset, count, filesize); + if (code) { + code=rx_EndCall(c,code); + continue; + } + bytesremaining=htonl(count); + bytes=rx_Write(c,(char *)&bytesremaining,sizeof(afs_int32)); + if (bytes != sizeof(afs_int32)) { + code=rx_EndCall(c,bytes); + continue; + } + bytesremaining=count; + totalbytes=0; + while (bytesremaining > 0) { + bytes=rx_Write(c, (char *)p, bytesremaining); + if (bytes <= 0) + break; + p+=bytes; + totalbytes+=bytes; + bytesremaining-=bytes; + } + if (bytesremaining == 0) { + code2=EndRXAFS_StoreData(c, &fst, &vs); + } + code=rx_EndCall(c, code2); + } + if (code == 0) + return totalbytes; + } + } + } + afs_errno=code; + return -1; +} + diff --git a/src/libafscp/afscp_init.c b/src/libafscp/afscp_init.c new file mode 100644 index 0000000..c533533 --- /dev/null +++ b/src/libafscp/afscp_init.c @@ -0,0 +1,106 @@ +/* AUTORIGHTS +Copyright (C) 2003 - 2010 Chaskiel Grundman +All rights reserved + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include "afscp.h" +#include "afscp_internal.h" + +#ifndef AFSCONF_CLIENTNAME +#include +#define AFSCONF_CLIENTNAME AFSDIR_CLIENT_ETC_DIRPATH +#endif + +static int init=0; +extern int RXAFSCB_ExecuteRequest(); +static struct rx_securityClass *sc; +static struct rx_service *serv; +extern PROCESS rx_listenerPid; +static int start_cb_server() +{ + + sc=rxnull_NewServerSecurityObject(); + serv=rx_NewService(0,1,"afs", &sc, 1, RXAFSCB_ExecuteRequest); + if (!serv) + return 1; + rx_StartServer(0); + return 0; +} + + +int afscp_init(const char *cell) +{ + if (init) + return 0; + if (_rx_InitRandomPort()) + return -1; + init=1; + if (start_cb_server()) { + printf("Cannot start callback service\n"); + return -1; + } + init=2; + if (cell) + return afs_setdefaultcell(cell); + return 0; +} + +void afscp_finalize(void) { + if (!init) + return; + + ReturnAllCallBacks(); + IOMGR_Sleep(1); + rx_Finalize(); +#if 0 + LWP_DestroyProcess(rx_listenerPid); + close(serv->socket); + rxi_FreeService(serv); +#endif + IOMGR_Finalize(); + LWP_TerminateProcessSupport(); +#if 1 + close(serv->socket); +#endif +} + + diff --git a/src/libafscp/afscp_internal.h b/src/libafscp/afscp_internal.h new file mode 100644 index 0000000..c2f887e --- /dev/null +++ b/src/libafscp/afscp_internal.h @@ -0,0 +1,79 @@ +#include +#include +#include +#define MAXADDRS 16 + +/* AUTORIGHTS */ +struct afs_server +{ + afsUUID id; + int index; + int cell; + int naddrs; + afs_uint32 addrs[MAXADDRS]; + struct rx_connection *conns[MAXADDRS]; +}; +struct afs_cell +{ + int id; + char name[MAXCELLCHARS]; + struct rx_securityClass *security; + int scindex; + struct ubik_client *vlservers; + int nservers; + int srvsalloced; + struct afs_server **fsservers; + void *volsbyname; + void *volsbyid; +}; + +struct afs_callback +{ + int valid; + const struct afs_server *server; + struct AFSFid fid; + struct AFSCallBack cb; +}; + +struct afs_dirstream +{ + struct afs_venusfid fid; + int buflen; + char *dirbuffer; + int hashent; + int entry; + int dv; + struct afs_dirent ret; +}; + +struct afs_dircache +{ + struct afs_venusfid me; + int buflen; + char *dirbuffer; + int dv; +}; + +struct afs_statent +{ + struct afs_venusfid me; + struct AFSFetchStatus status; +}; + +struct afs_openfile +{ + struct afs_venusfid fid; + off_t offset; +}; + +extern int _rx_InitRandomPort(void); +extern int _GetSecurityObject(struct afs_cell *); +extern int _GetVLservers(struct afs_cell *); +extern int _StatInvalidate(const struct afs_venusfid *fid); +extern int _StatStuff(const struct afs_venusfid *fid, const struct AFSFetchStatus *s); + +#ifdef AFSCP_DEBUG +#define afscp_dprintf(x) printf x +#else +#define afscp_dprintf(x) +#endif diff --git a/src/libafscp/afscp_server.c b/src/libafscp/afscp_server.c new file mode 100644 index 0000000..2fb9cd4 --- /dev/null +++ b/src/libafscp/afscp_server.c @@ -0,0 +1,350 @@ +/* AUTORIGHTS +Copyright (C) 2003 - 2010 Chaskiel Grundman +All rights reserved + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#include +#include +#include +#include +#include +#include +#ifndef AFSCONF_CLIENTNAME +#include +#define AFSCONF_CLIENTNAME AFSDIR_CLIENT_ETC_DIRPATH +#endif +#include +#include +#include +#include "afscp.h" +#include "afscp_internal.h" + +static int afs_ncells=0,afs_cellsalloced=0; +static struct afs_cell *allcells=NULL; +static int afs_nservers=0,afs_srvsalloced=0; +static struct afs_server **allservers=NULL; +static char *defcell; +int afs_errno=0; + +struct afs_cell *afs_cellbyid(int id) +{ + if (id >= afs_ncells) + return NULL; + return &allcells[id]; +} + +struct afs_cell *afs_cellbyname(const char *cellname) +{ + int i; + struct afs_cell *newlist, *thecell; + + for (i=0;i= afs_cellsalloced) { + if (afs_cellsalloced) + afs_cellsalloced = afs_cellsalloced *2; + else + afs_cellsalloced = 4; + newlist=realloc(allcells, afs_cellsalloced * sizeof(struct afs_cell)); + if (!newlist) + return NULL; + allcells=newlist; + } + thecell=&allcells[afs_ncells]; + memset(thecell, 0, sizeof(struct afs_cell)); + strcpy(thecell->name, cellname); + if (_GetSecurityObject(thecell)) + return NULL; + if (_GetVLservers(thecell)) { + RXS_Close(thecell->security); + return NULL; + } + thecell->id=afs_ncells++; + return thecell; +} +struct afs_cell *afs_defaultcell(void) +{ + struct afsconf_dir *dir; + char localcell[MAXCELLCHARS+1]; + int code; + + + if (defcell) { + return afs_cellbyname(defcell); + } + + dir=afsconf_Open(AFSCONF_CLIENTNAME); + if (!dir) { + afs_errno=AFSCONF_NODB; + return NULL; + } + code=afsconf_GetLocalCell(dir, localcell, MAXCELLCHARS); + if (code){ + afs_errno=code; + return NULL; + } + afsconf_Close(dir); + return afs_cellbyname(localcell); +} + +int afs_setdefaultcell(const char *cellname) { + struct afs_cell *this; + char *newdefcell; + if (!cellname) { + if (defcell) + free(defcell); + defcell=NULL; + return 0; + } + + this=afs_cellbyname(cellname); + if (!this) + return -1; + newdefcell=strdup(cellname); + if (!newdefcell) + return -1; + if (defcell) + free(defcell); + defcell = newdefcell; + return 0; +} + +int afs_cellid(struct afs_cell *cell) { + return cell->id; +} + +static void _xdr_free(bool_t (*fn)(XDR *xdrs, void *obj), void *obj) { + XDR xdrs; + xdrs.x_op=XDR_FREE; + fn(&xdrs, obj); +} +static bool_t _xdr_bulkaddrs(XDR *xdrs, void *objp) { + return xdr_bulkaddrs(xdrs, objp); +} + + +struct afs_server *afs_serverbyid(struct afs_cell *thecell, afsUUID *u) +{ + /* impliment uniquifiers? */ + int i; + struct afs_server **newlist; + struct afs_server **newall; + struct afs_server *ret; + afsUUID tmp; + bulkaddrs addrs; + struct ListAddrByAttributes attrs; + afs_int32 nentries, uniq; + + char s[512]; + + afsUUID_to_string(u, s, 511); + afscp_dprintf(("GetServerByID %s\n", s)); + + for (i=0;inservers;i++) { + if (afs_uuid_equal(&thecell->fsservers[i]->id, u)) + return thecell->fsservers[i]; + } + + if (thecell->nservers >= thecell->srvsalloced) { + if (thecell->srvsalloced) + thecell->srvsalloced = thecell->srvsalloced *2; + else + thecell->srvsalloced = 4; + newlist=realloc(thecell->fsservers, + thecell->srvsalloced * sizeof(struct afs_server *)); + if (!newlist) + return NULL; + thecell->fsservers=newlist; + } + ret=malloc(sizeof(struct afs_server)); + if (!ret) + return NULL; + thecell->fsservers[thecell->nservers]=ret; + memmove(&ret->id, u, sizeof(afsUUID)); + ret->cell=thecell->id; + memset(&tmp, 0, sizeof(tmp)); + memset(&addrs, 0, sizeof(addrs)); + memset(&attrs, 0, sizeof(attrs)); + attrs.Mask = VLADDR_UUID; + memmove(&attrs.uuid, u, sizeof(afsUUID)); + + if (ubik_VL_GetAddrsU(thecell->vlservers, 0, &attrs, &tmp, + &uniq, &nentries, &addrs)) + return NULL; + if (nentries > MAXADDRS) { + nentries=MAXADDRS; + /* XXX I don't want to do *that* much dynamic allocation */ + abort(); + } + + ret->naddrs=nentries; + for (i=0; i< nentries;i++) { + ret->addrs[i]=htonl(addrs.bulkaddrs_val[i]); + ret->conns[i]=rx_NewConnection(ret->addrs[i], + htons(AFSCONF_FILEPORT), + 1, thecell->security, + thecell->scindex); + } + _xdr_free(_xdr_bulkaddrs, &addrs); + thecell->nservers++; + + if (afs_nservers >= afs_srvsalloced) { + if (afs_srvsalloced) + afs_srvsalloced = afs_srvsalloced *2; + else + afs_srvsalloced = 4; + newall=realloc(allservers, + afs_srvsalloced * sizeof(struct afs_server *)); + if (!newall) + return ret; + allservers=newall; + } + ret->index=afs_nservers; + allservers[afs_nservers++]=ret; + return ret; +} + +struct afs_server *afs_serverbyaddr(struct afs_cell *thecell, afs_uint32 addr) +{ + /* impliment uniquifiers? */ + int i,j; + struct afs_server **newlist; + struct afs_server **newall; + struct afs_server *ret; + afsUUID uuid; + bulkaddrs addrs; + struct ListAddrByAttributes attrs; + afs_int32 nentries, code, uniq; + + + for (i=0;inservers;i++) { + ret=thecell->fsservers[i]; + for (j=0;jnaddrs;j++) + if (ret->addrs[j] == htonl(addr)) + return ret; + } + + if (thecell->nservers >= thecell->srvsalloced) { + if (thecell->srvsalloced) + thecell->srvsalloced = thecell->srvsalloced *2; + else + thecell->srvsalloced = 4; + newlist=realloc(thecell->fsservers, + thecell->srvsalloced * sizeof(struct afs_server)); + if (!newlist) + return NULL; + thecell->fsservers=newlist; + } + ret=malloc(sizeof(struct afs_server)); + if (!ret) + return NULL; + thecell->fsservers[thecell->nservers]=ret; + ret->cell=thecell->id; + memset(&uuid, 0, sizeof(uuid)); + memset(&addrs, 0, sizeof(addrs)); + memset(&attrs, 0, sizeof(attrs)); + attrs.Mask = VLADDR_IPADDR; + attrs.ipaddr=addr; + + + if ((code=ubik_VL_GetAddrsU(thecell->vlservers, 0, &attrs, &uuid, + &uniq, &nentries, &addrs))) { + memset(&ret->id, 0, sizeof(uuid)); + ret->naddrs=1; + ret->addrs[0]=htonl(addr); + ret->conns[0]=rx_NewConnection(ret->addrs[0], + htons(AFSCONF_FILEPORT), + 1, thecell->security, + thecell->scindex); + } else { + char s[512]; + + afsUUID_to_string(&uuid, s, 511); + afscp_dprintf(("GetServerByAddr 0x%x -> uuid %s\n", addr, s)); + + if (nentries > MAXADDRS) { + nentries=MAXADDRS; + /* XXX I don't want to do *that* much dynamic allocation */ + abort(); + } + memmove(&ret->id, &uuid, sizeof(afsUUID)); + + ret->naddrs=nentries; + for (i=0; i< nentries;i++) { + ret->addrs[i]=htonl(addrs.bulkaddrs_val[i]); + ret->conns[i]=rx_NewConnection(ret->addrs[i], + htons(AFSCONF_FILEPORT), + 1, thecell->security, + thecell->scindex); + } + _xdr_free(_xdr_bulkaddrs, &addrs); + } + + thecell->nservers++; + if (afs_nservers >= afs_srvsalloced) { + if (afs_srvsalloced) + afs_srvsalloced = afs_srvsalloced *2; + else + afs_srvsalloced = 4; + newall=realloc(allservers, + afs_srvsalloced * sizeof(struct afs_server *)); + if (!newall) + return ret; + allservers=newall; + } + ret->index=afs_nservers; + allservers[afs_nservers++]=ret; + return ret; +} + +struct afs_server *afs_anyserverbyaddr(afs_uint32 addr) +{ + /* no idea what this means: "impliment uniquifiers?" */ + int i,j; + struct afs_server *ret; + + for (i=0;inaddrs;j++) + if (ret->addrs[j] == htonl(addr)) + return ret; + } + return NULL; +} + +struct afs_server *afs_serverbyindex(int i) +{ + + if (i>= afs_nservers) + return NULL; + return allservers[i]; +} + +struct rx_connection *afs_serverconnection(const struct afs_server *srv, int i) { + if (i >= srv->naddrs) + return NULL; + return srv->conns[i]; +} diff --git a/src/libafscp/afscp_util.c b/src/libafscp/afscp_util.c new file mode 100644 index 0000000..9c58f14 --- /dev/null +++ b/src/libafscp/afscp_util.c @@ -0,0 +1,217 @@ +/* AUTORIGHTS +Copyright (C) 2003 - 2010 Chaskiel Grundman +All rights reserved + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#ifndef AFSCONF_CLIENTNAME +#include +#define AFSCONF_CLIENTNAME AFSDIR_CLIENT_ETC_DIRPATH +#endif + +#include +#include +#include + +#include + +#include "afscp.h" +#include "afscp_internal.h" + +#define HAVE_KRB5_CREDS_KEYBLOCK_ENCTYPE + +#ifdef HAVE_KRB5_CREDS_KEYBLOCK_ENCTYPE +#define Z_keydata(keyblock) ((keyblock)->contents) +#define Z_keylen(keyblock) ((keyblock)->length) +#define Z_credskey(creds) (&(creds)->keyblock) +#define Z_enctype(keyblock) ((keyblock)->enctype) +#else +#define Z_keydata(keyblock) ((keyblock)->keyvalue.data) +#define Z_keylen(keyblock) ((keyblock)->keyvalue.length) +#define Z_credskey(creds) (&(creds)->session) +#define Z_enctype(keyblock) ((keyblock)->keytype) +#endif + +int _rx_InitRandomPort(void) { + int sock; + unsigned int port; + struct sockaddr_in sin; + + sock=socket(PF_INET, SOCK_DGRAM, 0); + if (sock == -1) + return errno; + sin.sin_family=AF_INET; + sin.sin_addr.s_addr=INADDR_ANY; + sin.sin_port=0; + if (bind(sock, (struct sockaddr *)&sin, sizeof(struct sockaddr_in)) < 0) + return errno; + + port=sizeof(struct sockaddr_in); + if (getsockname(sock, (struct sockaddr *)&sin, &port) < 0) + return errno; + port=sin.sin_port; + close(sock); + return rx_Init(port); + +} + +static int insecure; + +int afscp_Insecure(void) { + insecure=1; + return 0; +} +static struct afsconf_dir *confdir; + +static int _GetCellInfo(char *cell, struct afsconf_cell *celldata) { + + if (!confdir) + confdir=afsconf_Open(AFSCONF_CLIENTNAME); + if (!confdir) + return AFSCONF_NODB; + return afsconf_GetCellInfo(confdir, cell, AFSCONF_VLDBSERVICE, celldata); +} + +int _GetSecurityObject(struct afs_cell *cell) { + int code; + krb5_context context; + krb5_creds match; + krb5_creds *cred; + krb5_ccache cc; + char **realms,*realm; + struct afsconf_cell celldata; + char localcell[MAXCELLCHARS]; + struct rx_securityClass *sc; + struct ktc_encryptionKey k; + int i; + rxkad_level l; + + code=_GetCellInfo(cell->name, &celldata); + if (code) + return code; + + code=krb5_init_context(&context); + if (code) + return code; + realm=NULL; + code=krb5_get_host_realm(context, celldata.hostName[0], &realms); + if (!code) { + strcpy(localcell,realms[0]); + krb5_free_host_realm(context,realms); + realm=localcell; + } + if (!realm) { + for (i=0; (i < MAXCELLCHARS && cell->name[i]); i++) { + if (isalpha(cell->name[i])) + localcell[i]=toupper(cell->name[i]); + else + localcell[i]=cell->name[i]; + } + localcell[i]='\0'; + realm=localcell; + } + cc=NULL; + code=krb5_cc_default(context, &cc); + + memset(&match, 0, sizeof(match)); + Z_enctype(Z_credskey(&match))=ENCTYPE_DES_CBC_CRC; + + if (!code) + code=krb5_cc_get_principal(context, cc, &match.client); + if (!code) + code=krb5_build_principal(context, &match.server, + strlen(realm), realm, + "afs", cell->name, NULL); + + if (code) { + krb5_free_cred_contents(context, &match); + if (cc) + krb5_cc_close(context, cc); + krb5_free_context(context); + return code; + } + + code = krb5_get_credentials(context, 0, cc, &match, &cred); + if (code) { + krb5_free_principal(context, match.server); + match.server=NULL; + code=krb5_build_principal(context, &match.server, + strlen(realm), realm, + "afs", NULL); /* afs@cell instead? */ + if (!code) + code = krb5_get_credentials(context, 0, cc, &match, &cred); + if (code) { + krb5_free_cred_contents(context, &match); + if (cc) + krb5_cc_close(context, cc); + krb5_free_context(context); + return code; + } + } + + if (insecure) + l=rxkad_clear; + else + l=rxkad_crypt; + memcpy(&k.data, Z_keydata(Z_credskey(cred)), 8); + sc = (struct rx_securityClass *)rxkad_NewClientSecurityObject + (l, &k, RXKAD_TKT_TYPE_KERBEROS_V5, + cred->ticket.length, cred->ticket.data); + krb5_free_creds(context, cred); + krb5_free_cred_contents(context, &match); + if (cc) + krb5_cc_close(context, cc); + krb5_free_context(context); + cell->security=sc; + cell->scindex=2; + return 0; +} +int _GetVLservers(struct afs_cell *cell) { + struct rx_connection *conns[MAXHOSTSPERCELL+1]; + int i; + int code; + struct afsconf_cell celldata; + + code=_GetCellInfo(cell->name, &celldata); + if (code) + return code; + + for (i=0; i < celldata.numServers; i++) { + conns[i] = rx_NewConnection(celldata.hostAddr[i].sin_addr.s_addr, + htons(AFSCONF_VLDBPORT), + USER_SERVICE_ID, cell->security, + cell->scindex); + } + conns[i]=0; + return ubik_ClientInit(conns, &cell->vlservers); +} diff --git a/src/libafscp/afscp_volume.c b/src/libafscp/afscp_volume.c new file mode 100644 index 0000000..933705e --- /dev/null +++ b/src/libafscp/afscp_volume.c @@ -0,0 +1,317 @@ +/* AUTORIGHTS +Copyright (C) 2003 - 2010 Chaskiel Grundman +All rights reserved + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "afscp.h" +#include "afscp_internal.h" + +#include +#include + +static int icompare(const void *pa, const void *pb) { + const struct afs_volume *va=pa,*vb=pb; + + if (va->id > vb->id) return 1; + if (va->id < vb->id) return -1; + return 0; +} + + +static int ncompare(const void *pa, const void *pb) { + const struct afs_volume *va=pa,*vb=pb; + + if (va->voltype > vb->voltype) return 1; + if (vb->voltype < va->voltype) return -1; + return strcmp(va->name, vb->name); +} + + +union allvldbentry +{ + struct uvldbentry u; + struct nvldbentry n; + struct vldbentry o; +}; + +struct afs_volume *afs_volumebyname(struct afs_cell *cell, const char *vname, afs_int32 intype) +{ + union allvldbentry u; + struct afs_volume *ret,key; + struct afs_server *server; + afs_int32 code,vtype,type,srv; + void *s; + struct in_addr i; + + if (intype == VOLTYPE_RW) + vtype=VLSF_RWVOL; + else if (intype == VOLTYPE_RO) + vtype=VLSF_ROVOL; + else if (intype == VOLTYPE_BK) + vtype=VLSF_BACKVOL; + else { + afs_errno=EINVAL; + return NULL; + } + + memset(&key,0, sizeof(key)); + strcpy(key.name,vname); + key.voltype=vtype; + s=tfind(&key, &cell->volsbyname, ncompare); + if (s) { + ret=*(struct afs_volume **)s; + return ret; + } + + type=0; + if ((code=ubik_VL_GetEntryByNameU(cell->vlservers, 0, (char *)vname, &u.u)) + == RXGEN_OPCODE) { + type=1; + if ((code=ubik_VL_GetEntryByNameN(cell->vlservers, 0, (char *)vname, &u.n)) + == RXGEN_OPCODE) { + type=2; + code=ubik_VL_GetEntryByNameO(cell->vlservers, 0, (char *)vname, &u.o); + } + } + if (code) { + afs_errno=code; + return NULL; + } + ret=malloc(sizeof(struct afs_volume)); + if (!ret) { + afs_errno=ENOMEM; + return NULL; + } + memset(ret,0,sizeof(struct afs_volume)); + strcpy(ret->name, u.u.name); + ret->nservers=0; + ret->cell=cell; + switch (type) { + case 0: + ret->id=u.u.volumeId[intype]; + for (srv=0;srv < u.u.nServers;srv++) { + if ((u.u.serverFlags[srv] & vtype) == 0) + continue; + //printf("uvldbentry server %d flags: %x\n",srv, u.u.serverFlags[srv]); + + if ((u.u.serverFlags[srv] & VLSERVER_FLAG_UUID) == 0) + server=afs_serverbyaddr(cell, u.u.serverNumber[srv].time_low); + else + server=afs_serverbyid(cell, &u.u.serverNumber[srv]); + if (!server) + continue; + ret->servers[ret->nservers++]=server->index; + } + break; + case 1: + ret->id=u.n.volumeId[intype]; + for (srv=0;srv < u.n.nServers;srv++) { + if ((u.n.serverFlags[srv] & vtype) == 0) + continue; + server=afs_serverbyaddr(cell, u.n.serverNumber[srv]); + if (!server) + continue; + ret->servers[ret->nservers++]=server->index; + } + break; + case 2: + ret->id=u.o.volumeId[intype]; + for (srv=0;srv < u.o.nServers;srv++) { + if ((u.o.serverFlags[srv] & vtype) == 0) + continue; + server=afs_serverbyaddr(cell, u.o.serverNumber[srv]); + if (!server) + continue; + ret->servers[ret->nservers++]=server->index; + } + break; + } + if (!ret->nservers || !ret->id) { + free(ret); + return NULL; + } + + ret->voltype=intype; + server=afs_serverbyindex(ret->servers[0]); + if (server) + i.s_addr=server->addrs[0]; + else + i.s_addr=0; + afscp_dprintf(("New volume BYNAME %s (%lu) on %s (%d)\n", ret->name, ret->id, inet_ntoa(i),ret->servers[0])); + s=tsearch(&key, &cell->volsbyname, ncompare); + if (s) + *(struct afs_volume **)s=ret; + key.id=ret->id; + s=tsearch(&key, &cell->volsbyid, icompare); + if (s) + *(struct afs_volume **)s=ret; + return ret; +} + +struct afs_volume *afs_volumebyid(struct afs_cell *cell, afs_uint32 id) +{ + union allvldbentry u; + struct afs_volume *ret,key; + struct afs_server *server; + afs_int32 code,vtype,type,srv; + int voltype; + char idbuffer[16]; + void *s; + struct in_addr i; + + memset(&key,0, sizeof(key)); + key.id=id; + s=tfind(&key, &cell->volsbyid, icompare); + if (s) { + ret=*(struct afs_volume **)s; + return ret; + } + + sprintf(idbuffer,"%" PRIu32, id); + type=0; + if ((code=ubik_VL_GetEntryByNameU(cell->vlservers, 0, idbuffer, &u.u)) + == RXGEN_OPCODE) { + type=1; + if ((code=ubik_VL_GetEntryByIDN(cell->vlservers, 0, id, -1, &u.n)) + == RXGEN_OPCODE) { + type=2; + code=ubik_VL_GetEntryByID(cell->vlservers, 0, id, -1, &u.o); + } + } + if (code) { + afs_errno=code; + return NULL; + } + ret=malloc(sizeof(struct afs_volume)); + if (!ret) { + afs_errno=ENOMEM; + return NULL; + } + + memset(ret,0,sizeof(struct afs_volume)); + strcpy(ret->name, u.u.name); + ret->nservers=0; + ret->cell=cell; + + switch (type) { + case 0: + if (id == u.u.volumeId[RWVOL]) { + vtype=VLSF_RWVOL; + voltype=VOLTYPE_RW; + } else if (id == u.u.volumeId[ROVOL]) { + vtype=VLSF_ROVOL; + voltype=VOLTYPE_RO; + } else if (id == u.u.volumeId[BACKVOL]) { + vtype=VLSF_BACKVOL; + voltype=VOLTYPE_BK; + } else { + vtype=0; + voltype=-1; + } + for (srv=0;srv < u.u.nServers;srv++) { + if ((u.u.serverFlags[srv] & vtype) == 0) + continue; + if ((u.u.serverFlags[srv] & VLSERVER_FLAG_UUID) == 0) + server=afs_serverbyaddr(cell, u.u.serverNumber[srv].time_low); + else + server=afs_serverbyid(cell, &u.u.serverNumber[srv]); + if (!server) + continue; + ret->servers[ret->nservers++]=server->index; + } + break; + case 1: + if (id == u.n.volumeId[RWVOL]) { + vtype=VLSF_RWVOL; + voltype=VOLTYPE_RW; + } else if (id == u.n.volumeId[ROVOL]) { + vtype=VLSF_ROVOL; + voltype=VOLTYPE_RO; + } else if (id == u.n.volumeId[BACKVOL]) { + vtype=VLSF_BACKVOL; + voltype=VOLTYPE_BK; + } else { + vtype=0; + voltype=-1; + } + for (srv=0;srv < u.n.nServers;srv++) { + if ((u.n.serverFlags[srv] & vtype) == 0) + continue; + server=afs_serverbyaddr(cell, u.n.serverNumber[srv]); + if (!server) + continue; + ret->servers[ret->nservers++]=server->index; + } + break; + case 2: + if (id == u.o.volumeId[RWVOL]) { + vtype=VLSF_RWVOL; + voltype=VOLTYPE_RW; + } else if (id == u.o.volumeId[ROVOL]) { + vtype=VLSF_ROVOL; + voltype=VOLTYPE_RO; + } else if (id == u.o.volumeId[BACKVOL]) { + vtype=VLSF_BACKVOL; + voltype=VOLTYPE_BK; + } else { + vtype=0; + voltype=-1; + } + for (srv=0;srv < u.o.nServers;srv++) { + if ((u.o.serverFlags[srv] & vtype) == 0) + continue; + server=afs_serverbyaddr(cell, u.o.serverNumber[srv]); + if (!server) + continue; + ret->servers[ret->nservers++]=server->index; + } + break; + } + ret->voltype=voltype; + server=afs_serverbyindex(ret->servers[0]); + if (server) + i.s_addr=server->addrs[0]; + else + i.s_addr=0; + afscp_dprintf(("New volume BYID %s (%lu) on %s (%d)\n", ret->name, ret->id, inet_ntoa(i), ret->servers[0])); + s=tsearch(&key, &cell->volsbyid, icompare); + if (s) + *(struct afs_volume **)s=ret; + strcpy(key.name, ret->name); + s=tsearch(&key, &cell->volsbyname, ncompare); + if (s) + *(struct afs_volume **)s=ret; + return ret; +} +