home *** CD-ROM | disk | FTP | other *** search
- /* Functions to deal with giving out director listings
-
- ©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.
- */
-
- /*
- We build and cache directory listings before giving them out.
- The process may not be hugely efficient, but NFS places several
- constraints on us that are not natural to the Amiga.
- */
-
- /*
- 30-Nov-1999 - Comprehensively rewritten to directly create RPC responses.
- */
-
- #include "nfsd.h"
- #include "dirlist.h"
- #include "memory.h"
- #include "handle_list.h"
- #include "nfs_utils.h"
-
- #include <stdio.h>
- #include <clib/alib_protos.h>
-
- #include <proto/dos.h>
- #include <proto/exec.h>
-
- struct DirEntry *create_direntry(STRPTR name, u_int inode);
- void add_in_order(struct DEList *del, struct DirEntry *nde);
-
- extern struct FileInfoBlock fib; /* memory.c */
-
- static struct DirEntry *create_direntry(STRPTR name, u_int inode)
- {
- struct DirEntry *nde;
-
- if (!name)
- return NULL;
-
- if (nde = AllocVecPooled(sizeof(struct DirEntry))) {
- int l = strlen(name);
- int n = INT32S(l);
-
- int32 *mem = (int32 *)AllocVecPooled(n * SINT32);
-
- if (mem) {
- /* Touch the last int32 with zero, to pad */
- if (n > 0) {
- *(mem + n - 1) = 0;
- }
- memcpy(mem, name, l);
-
- nde->de_NameChars = l;
- nde->de_NameInt32s = n;
- nde->de_Name = mem;
- nde->de_Inode = inode;
- } else {
- FreeVecPooled(nde);
- return NULL;
- }
- }
-
- return nde;
- }
-
-
- void free_delist_nodes(struct DEList *del)
- {
- if (del) {
- struct DirEntry *de;
- while (de = (struct DirEntry *)RemTail((struct List *)del)) {
- FreeVecPooled(de->de_Name);
- FreeVecPooled(de);
- }
- }
- }
-
- /* Get an up-to-date listing of the directory */
- static struct DEList *get_delist(struct LocalFile *dir_lf, nfsstat *status)
- {
- struct DEList *del = dir_lf->lf_DEList;
- int buf_length;
- STRPTR path_buf;
-
- /* First up, is it actually a directory? Well,
- we used to check that first of all, but hardlinks and softlinks
- muddy the water a little more.
- We now try it anyway, giving the appropriate error if something
- goes wrong.
- */
-
- /* Does this contain a still-valid list? */
- if (del) {
- /* Is it still up-to-date? */
- if (CompareDates(&(fib.fib_Date), &(del->del_DateStamp)) >= 0) {
- return del;
- }
-
- /* No, so let's get rid of the current listing */
- free_delist_nodes(del);
- } else {
- if (del = (struct DEList *)AllocVecPooled(sizeof(struct DEList))) {
- NewList((struct List *)del); /* Hah! Imagine what would happen if I forgot that... */
- }
- }
-
- /* This is long enough for anything up to 256 char filenames
- in this directory */
- buf_length = strlen(dir_lf->lf_Name) + 256 + 2;
- path_buf = AllocVecPooled(buf_length);
-
- /* In fact, if we're being strict, that's about 148 characters more
- than it could be, but we'll plan for the future I think */
-
- if (!del || !path_buf) {
- FreeVecPooled(path_buf);
- FreeVecPooled(del);
- *status = NFSERR_IO; // No memory?
- return (dir_lf->lf_DEList = NULL);
- }
-
- /* Okay, let's date this list */
- CopyMem(&(fib.fib_Date), &(del->del_DateStamp),
- sizeof(struct DateStamp));
-
- DEBUG(printf("Reading the directory '%s'\n", dir_lf->lf_Name));
-
- /* Now let's journey the directory... */
- *status = NFS_OK;
-
- while (ExNext(dir_lf->lf_Lock, &fib)) {
- strcpy(path_buf, dir_lf->lf_Name);
- if (AddPart(path_buf, fib.fib_FileName, buf_length)) {
- struct LocalFile *lf = get_namedlocalfile(path_buf);
- if (lf) {
- correct_struct(lf); /* Give it a correct inode */
- struct DirEntry *nde
- = create_direntry(fib.fib_FileName, lf->lf_FileID);
- if (nde) {
- add_in_order(del, nde);
- } else {
- *status = NFSERR_IO; /* No memory? */
- break;
- }
- } else {
- *status = NFSERR_IO; /* No memory? */
- break;
- }
- } else {
- /* We alert the user and leave it out of the listing */
- printf("AddPart() failed - the filename '%s' may be too long.\n", fib.fib_FileName);
- }
- }
-
- FreeVecPooled(path_buf);
-
- /* Did our scan finish unnaturally? */
- if (IoErr() != ERROR_NO_MORE_ENTRIES)
- *status = NFS_wt_err(NFSERR_NOTDIR);
-
- if (*status != NFS_OK) {
- free_delist_nodes(del);
- FreeVecPooled(del);
- printf("Error reading directory '%s'- %s\n", dir_lf->lf_Name,
- reason(*status));
- del = NULL;
- }
-
- return (dir_lf->lf_DEList = del);
- }
-
- /* Add the DirEntry to the list such that the inodes are in ascending order */
- static void add_in_order(struct DEList *del, struct DirEntry *nde)
- {
- struct DirEntry *de = (struct DirEntry *)del->del_List.mlh_TailPred;
-
- while (de->de_Node.mln_Pred) {
- if (de->de_Inode < nde->de_Inode) {
- Insert((struct List *)del,
- (struct Node *)nde, (struct Node *)de);
- return;
- }
-
- de = (struct DirEntry *)de->de_Node.mln_Pred;
- }
-
- /* It's earlier than all others */
- AddHead((struct List *)del, (struct Node *)nde);
- }
-
-
- /* Public function to satisfy readdir requests. Returns the number
- of int32s it used, rather than the total number in the response.
- */
- int readdir(struct LocalFile *dir_lf, int32 *rptr, int32 *rlimit, u_int cookie,
- u_int max_count)
- {
- struct DEList *del;
-
- /* Use the cookie as the inode we served up to. This avoids
- problems with serving files twice if we rescanned between
- dirattrs */
-
- DEBUG(printf("Asked for listing of '%s' (%d,%d)\n",
- dir_lf->lf_Name, max_count, cookie));
-
- int32 *base_ptr = rptr;
-
- /* We want the count in int32s */
- max_count /= SINT32;
-
- /* Enough for any single entry */
- #define SDE_MAX (4 + INT32S(NFS_MAXNAMELEN))
-
- if (max_count < SDE_MAX) { /* We can't necessarily fit anything in there */
- *rptr = NFSERR_INVAL;
- return 1;
- }
-
- #undef SDE_MAX
-
- nfsstat status;
-
- del = get_delist(dir_lf, &status);
-
- if (del) {
- struct DirEntry *de = del->del_List.mlh_Head;
- int i = 0;
-
- *rptr++ = P_ENUM(NFS_OK);
-
- /* Move forwards up to where we left off */
- while (de->de_Node.mln_Succ && (de->de_Inode < cookie))
- de = de->de_Node.mln_Succ;
-
- /* Now actually copy the entries */
- while (de->de_Node.mln_Succ) {
-
- max_count -= (4 + de->de_NameInt32s);
-
- if ((max_count < 0) ||
- ((rlimit - rptr) < (6 + de->de_NameInt32s)))
- {
- *rptr++ = P_ENUM_FALSE; // No entry follows
- *rptr++ = P_ENUM_FALSE; // This is not the end of the directory
-
- DEBUG(printf("Didn't finish the directory (sent %d)\n", i));
- return (rptr - base_ptr);
- }
-
- *rptr++ = P_ENUM_TRUE; // An entry follows
-
- *rptr++ = P_UINT(de->de_Inode);
- *rptr++ = P_UINT(de->de_NameChars);
- CopyMem(de->de_Name, rptr, SINT32 * de->de_NameInt32s);
- rptr += de->de_NameInt32s;
-
- /* Pop a cookie in there */
- *rptr++ = P_UINT(de->de_Inode + 1);
- i++;
-
- de = de->de_Node.mln_Succ;
- }
-
- /* Hey - we finished the directory */
- *rptr++ = P_ENUM_FALSE; // No entry follows
- *rptr++ = P_ENUM_TRUE; // Yes, it is the end of the directory
-
- DEBUG(printf("Finished the directory (sent %d)\n", i));
-
- return (rptr - base_ptr);
- } else {
- *rptr = P_ENUM(status);
- printf("Failed to get directory listing of '%s' - %s\n",
- dir_lf->lf_Name, reason(status));
- return 1;
- }
- }
-
-