home *** CD-ROM | disk | FTP | other *** search
- /* Routines to manage a list of local files and their NFS representation
-
- ©1998, 1999 Joseph Walton
-
- This software is distributed under the terms of the GNU General Public
- License; either version 2 of the License, or (at your option) any
- later version.
- */
-
- /*
- 29-Sep-1999 - Hashtables now used - should go faster.
- 4-Oct-1999 - Now prunes unused entries from memory.
- 30-Nov-1999 - Changes to integrate with new RPC code.
- */
-
- #include "nfsd.h"
- #include "handle_list.h"
- #include "auth.h"
- #include "memory.h"
- #include "nfs_utils.h"
- #include "dirlist.h"
- #include "hashtable.h"
-
- #include <stdio.h>
-
- #include <exec/memory.h>
- #include <clib/alib_protos.h>
-
- #include <stdlib.h> /* rand() prototype */
-
- #include <ctype.h> /* tolower() */
-
- #include <proto/exec.h>
- #include <proto/dos.h>
- #include <proto/timer.h>
-
- /* The file to store persistent inode data in */
- #define INODE_FILE "nfsd.inode"
-
- extern struct InfoData infodata;
- extern struct FileInfoBlock fib;
-
- /*
- lf_list is a list of all LocalFiles in the system.
- handle_ht is a hash from handles to nodes in the list.
- name_ht is a case-insensitive hash from names to nodes.
- Iterate over lf_list - use the others just for lookup
- */
- static struct List lf_list;
- static struct HashTable *handle_ht;
- static struct HashTable *name_ht;
-
- /* This needs to be unique when used */
- static u_int next_id = 0;
-
- /* Internal prototypes */
- static BOOL fh_match(nfs_fh *fha, nfs_fh *fhb);
- static void fh_fillin(nfs_fh *fh);
- static u_int new_inode(void);
- static struct LocalFile *create_localfile(STRPTR name);
- static void free_localfile(struct LocalFile *lf);
- static void remove_localfile(struct LocalFile *lf);
-
- /* Prototypes for hashtable callbacks */
- static ULONG handleHash(APTR handle);
- static ULONG handleEquals(APTR a, APTR b);
- static ULONG stringHash(APTR string);
- static BOOL stringEquals(APTR a, APTR b);
-
- /* Initialise the internal data for handle_list */
- BOOL init_handle_list()
- {
- handle_ht = htMakeNew(&handleHash, &handleEquals);
- name_ht = htMakeNew(&stringHash, &stringEquals);
- if (handle_ht && name_ht) {
- NewList(&lf_list);
- return TRUE;
- }
- return FALSE;
- }
-
- /* next_id needs to be unique amongst subsequent runs - we
- need to keep a persistent copy somewhere */
-
- /* Okay, I'm writing it in binary rather than text. This is probably
- the Wrong Thing, but I'm in no mood right now. */
-
- #define SNI sizeof(next_id)
-
- /* Fetch the base inode from persistent store */
- void persistent_inode_fetch()
- {
- BPTR file;
- if (file = Open(INODE_FILE, MODE_OLDFILE)) {
- if (Read(file, &next_id, SNI) != SNI) {
- next_id = 0;
- PrintFault(IoErr(), "failed to read from " INODE_FILE);
- }
-
- Close(file);
- } else
- PrintFault(IoErr(), "unable to open " INODE_FILE);
-
- /* Was it set? */
- if (!next_id) {
- next_id = 1;
- puts("Unable to get persistent inode data. This will cause problems unless this is a first run.");
- }
- printf("First inode is %d.\n", next_id);
- }
-
- /* Place the current inode in persistent store */
- BOOL persistent_inode_store()
- {
- BPTR file;
- BOOL result = TRUE;
- if (file = Open(INODE_FILE, MODE_NEWFILE)) {
- if (SNI != Write(file, &next_id, SNI)) {
- result = FALSE;
- PrintFault(IoErr(), "failed to write inode data");
- }
- Close(file);
- } else {
- result = FALSE;
- PrintFault(IoErr(), "failed to open " INODE_FILE " for writing");
- }
-
- return result;
- }
-
- #undef SNI
-
- /* Create a new node for a given filename */
- static struct LocalFile *create_localfile(STRPTR name)
- {
- struct LocalFile *nlf;
- if (name == NULL)
- return NULL;
-
- nlf = AllocVecPooled(sizeof(struct LocalFile));
- if (nlf) {
- nlf->lf_Name = DupString(name);
-
- if (nlf->lf_Name) {
- fh_fillin(&nlf->lf_NFSHandle); /* Give it a new handle */
- nlf->lf_FileID = 0; /* This will be filled in when it's used */
- nlf->lf_IsMount = FALSE;
- nlf->lf_Type = NFNON; /* This as well */
- nlf->lf_Lock = NULL;
- nlf->lf_DEList = NULL;
-
- /* These values, if set, will override the real ones */
- nlf->lf_ForcedUID = -1;
- nlf->lf_ForcedGID = -1;
- nlf->lf_ForcedProtection = -1;
-
- impose_configuration(nlf);
-
- GetSysTime(&(nlf->lf_LastUsed));
-
- /* And add it to the list */
- AddTail(&lf_list, (struct Node *)nlf);
-
- /* Put it in the hashtables */
- htAdd(handle_ht, &nlf->lf_NFSHandle, nlf);
- htAdd(name_ht, nlf->lf_Name, nlf);
-
- DEBUG(printf("Created handle for '%s'\n", nlf->lf_Name));
-
- /* fh_print(&(nlf->lf_NFSHandle)); */
- } else {
- FreeVecPooled(nlf);
- nlf = NULL;
- }
- }
- return nlf;
- }
-
-
- /* Free a LocalFile and any resources directly attached to it.
- Make sure you're not using it for list navigation at this point!
- */
- static void free_localfile(struct LocalFile *lf)
- {
- if (lf) {
- FreeVecPooled(lf->lf_Name);
- if (lf->lf_DEList) {
- free_delist_nodes(lf->lf_DEList);
- FreeVecPooled(lf->lf_DEList);
- }
- FreeVecPooled(lf);
- }
- }
-
- /* Remove a LocalFile from internal structures it may belong to.
- Afterwards it is safe to free it.
- */
- static void remove_localfile(struct LocalFile *lf)
- {
- if (lf) {
- Remove((struct Node *)lf); /* Assumes all LocalFiles are in a list */
-
- /* Remove it from the hashtables as well */
- if (!htRemove(handle_ht, &lf->lf_NFSHandle)
- || !htRemove(name_ht, lf->lf_Name))
- {
- printf("Internal error - %lx was not found in the hashtables", lf);
- }
- }
- }
-
-
- /* Search the list for a given filename (case insensitive) */
- struct LocalFile *find_namedlocalfile(STRPTR name)
- {
- if (!name)
- return NULL;
-
- return (struct LocalFile *)htGet(name_ht, name);
- }
-
- /* Get a structure for the given filename, creating one
- if need be */
- struct LocalFile *get_namedlocalfile(STRPTR name)
- {
- struct LocalFile *lf = find_namedlocalfile(name);
-
- if (lf)
- return lf;
-
- /* Nothing found? Create a new one, then. */
- return create_localfile(name);
- }
-
- /* Search the list for a given NFS file handle */
- struct LocalFile *find_handle(nfs_fh *fh)
- {
- struct LocalFile *lf;
-
- if (!fh)
- return NULL;
-
- lf = (struct LocalFile *)htGet(handle_ht, fh);
-
- if (!lf) {
- /* We couldn't find this handle, so print it.
- If it ever prunes old handles, take this out, because it'll
- happen fairly often. */
- DEBUG(puts("Stale handle received "));
- DEBUG(fh_print(fh));
- }
-
- return lf;
- }
-
- /* "Lose" a LocalFile, removing it from the list and freeing the
- memory */
- void lose_localfile(STRPTR full_name)
- {
- struct LocalFile *lf;
-
- if (lf = find_namedlocalfile(full_name)) {
- release_lf(lf);
- remove_localfile(lf);
- free_localfile(lf);
- }
- }
-
- /* Gets access to a file we've given out a handle to */
- struct LocalFile *get_by_handle(nfs_fh *fh, nfsstat *stat, ULONG mode)
- {
- BPTR lock;
- struct LocalFile *lf = find_handle(fh);
- if (lf == NULL) { /* We don't know this handle */
- *stat = NFSERR_STALE;
- return NULL;
- }
-
- lock = Lock(lf->lf_Name, mode);
-
- /* Why did we fail to lock it? */
- if (lock == NULL) {
- LONG err = IoErr();
- PrintFault(IoErr(), "Unable to lock");
- if ((err == ERROR_OBJECT_NOT_FOUND) || (err == ERROR_DIR_NOT_FOUND)) {
- lf->lf_Type = NFNON; /* It's gone away... */
- } else {
- *stat = NFS_err(IoErr());
- }
- return NULL;
- }
-
- /* Try and get details of the drive and file */
- if (Info(lock, &infodata) && Examine(lock, &fib)) {
- correct_struct(lf);
- lf->lf_Lock = lock;
- return lf;
- } else {
- *stat = NFS_err(IoErr());
- UnLock(lock);
- return NULL;
- }
- }
-
- /* Give up any interest in this LocalFile */
- void release_lf(struct LocalFile *rlf)
- {
- if (rlf) {
- if (rlf->lf_Lock) {
- UnLock(rlf->lf_Lock);
- rlf->lf_Lock = NULL;
- }
- /* One day we'll use this to determine what can go */
- GetSysTime(&(rlf->lf_LastUsed));
- }
- }
-
- /* Flushes any handles that haven't been used for a while from
- the system. Returns 'TRUE' if there would be any purpose in calling
- it again in FLUSH_HANDLES_EVERY seconds time.
- */
- BOOL flush_handles()
- {
- struct timeval time_now;
- BOOL handles_pending = FALSE;
-
- /* Now - do we need to flush? */
- GetSysTime(&time_now);
-
- /* We can't use SubTime because we need time_now repeatedly.
- This means we run the risk of being a couple of seconds out -
- that's not really a problem.
- */
-
- struct LocalFile *lf;
-
- lf = lf_list.lh_Head;
- /* This is a fairly expensive operation */
- while (lf->lf_Node.mln_Succ) {
- struct LocalFile *next = (struct LocalFile *)lf->lf_Node.mln_Succ;
- /* We never flush mountpoints. It isn't, is it? */
- if (!lf->lf_IsMount) {
- if ((time_now.tv_secs - lf->lf_LastUsed.tv_secs) > FLUSH_HANDLES_AFTER) {
- /* It's not a mountpoint, and it hasn't been used for
- FLUSH_HANDLES_AFTER seconds */
- DEBUG(printf("Flushing handle for '%s'\n", lf->lf_Name));
- remove_localfile(lf);
- free_localfile(lf);
- } else {
- /* It should be flushed, but not yet */
- DEBUG(printf("'%s' is a pending flushee\n", lf->lf_Name));
- handles_pending = TRUE;
- }
- }
- lf = next;
- }
-
- return handles_pending;
- }
-
- /* Compare two NFS file handles */
- static BOOL fh_match(nfs_fh *fha, nfs_fh *fhb)
- {
- return (memcmp(fha, fhb, NFS_FHSIZE) == 0);
- }
-
- /* Copy an NFS handle for a response */
- int32 *fh_copy(struct LocalFile *lf, int32 *dest)
- {
- if (!lf || !dest) {
- puts("null arguments to fh_copy");
- return NULL;
- }
-
- CopyMem(&lf->lf_NFSHandle, dest, NFS_FHSIZE);
-
- return dest + INT32S(NFS_FHSIZE);
- }
-
- /* Print a handle, purely for debugging */
- void fh_print(nfs_fh *fh)
- {
- int i;
- printf("{");
- for (i = 0 ; i < NFS_FHSIZE ; i++) {
- if (i > 0)
- putchar(',');
- printf("%2x", fh[i]);
- }
- printf("}\n");
- }
-
- /* Fill in a unique NFS handle */
- static void fh_fillin(nfs_fh *fh)
- {
- /* As well as all the random data, ensure it actually is unique
- by placing a counter in there */
- static ULONG handle_counter = 1;
-
- int32 *ptr = fh;
-
- GetSysTime((struct timeval *)ptr); /* First 8 bytes */
- ptr += 2;
-
- *ptr++ = handle_counter++;
-
- /* We have 5 more int32s; pad with noise to make spoofing harder */
- *ptr++ = rand();
- *ptr++ = rand();
- *ptr++ = rand();
- *ptr++ = rand();
- *ptr = rand();
- }
-
- /* Give a unique inode */
- static u_int new_inode()
- {
- return next_id++;
- }
-
- /* Correct the type of a LocalFile structure,
- and possibly give it a new inode */
- void correct_struct(struct LocalFile *lf)
- {
- u_int type = NFS_type(fib.fib_DirEntryType);
-
- if (type != lf->lf_Type) { /* It's changed... */
- lf->lf_Type = type;
- lf->lf_FileID = new_inode();
- }
- }
-
- /* Callbacks for hash tables */
- static ULONG handleHash(APTR handle)
- {
- ULONG val = 0;
- BYTE *ba = (BYTE *)handle;
- int i;
-
- for (i = 0 ; i < NFS_FHSIZE ; i++) {
- val = (val << 5) ^ (val >> 27) ^ *ba++;
- }
- return val;
- }
-
- static ULONG handleEquals(APTR a, APTR b)
- {
- return fh_match((nfs_fh *)a, (nfs_fh *)b);
- }
-
- /* This is a case insensitive hash */
- static ULONG stringHash(APTR string)
- {
- ULONG val = 0;
- STRPTR ba = (STRPTR)string;
-
- while (*ba) {
- val = (val << 5) ^ (val >> 27) ^ tolower(*ba++);
- }
- return val;
- }
-
- static BOOL stringEquals(APTR a, APTR b)
- {
- return (0 == stricmp((STRPTR)a, (STRPTR)b));
- }
-
-