home *** CD-ROM | disk | FTP | other *** search
/ Magazyn Amiga 13 / MA_Cover_13.bin / source / c / nfsd / src / dirlist.c < prev    next >
Encoding:
C/C++ Source or Header  |  1999-11-30  |  8.0 KB  |  284 lines

  1. /*  Functions to deal with giving out director listings
  2.  
  3.     ©1998, 1999 Joseph Walton
  4.  
  5.     This software is distributed under the terms of the GNU General Public
  6.     License; either version 2 of the License, or (at your option) any
  7.     later version.
  8. */
  9.  
  10. /*
  11.     We build and cache directory listings before giving them out.
  12.     The process may not be hugely efficient, but NFS places several
  13.     constraints on us that are not natural to the Amiga.
  14. */
  15.  
  16. /*
  17.     30-Nov-1999 - Comprehensively rewritten to directly create RPC responses.
  18. */
  19.  
  20. #include "nfsd.h"
  21. #include "dirlist.h"
  22. #include "memory.h"
  23. #include "handle_list.h"
  24. #include "nfs_utils.h"
  25.  
  26. #include <stdio.h>
  27. #include <clib/alib_protos.h>
  28.  
  29. #include <proto/dos.h>
  30. #include <proto/exec.h>
  31.  
  32. struct DirEntry *create_direntry(STRPTR name, u_int inode);
  33. void add_in_order(struct DEList *del, struct DirEntry *nde);
  34.  
  35. extern struct FileInfoBlock fib; /* memory.c */
  36.  
  37. static struct DirEntry *create_direntry(STRPTR name, u_int inode)
  38. {
  39.     struct DirEntry *nde;
  40.  
  41.     if (!name)
  42.         return NULL;
  43.  
  44.     if (nde = AllocVecPooled(sizeof(struct DirEntry))) {
  45.         int l = strlen(name);
  46.         int n = INT32S(l);
  47.  
  48.         int32 *mem = (int32 *)AllocVecPooled(n * SINT32);
  49.  
  50.         if (mem) {
  51.             /* Touch the last int32 with zero, to pad */
  52.             if (n > 0) {
  53.                 *(mem + n - 1) = 0;
  54.             }
  55.             memcpy(mem, name, l);
  56.  
  57.             nde->de_NameChars = l;
  58.             nde->de_NameInt32s = n;
  59.             nde->de_Name = mem;
  60.             nde->de_Inode = inode;
  61.         } else {
  62.             FreeVecPooled(nde);
  63.             return NULL;
  64.         }
  65.     }
  66.  
  67.     return nde;
  68. }
  69.  
  70.  
  71. void free_delist_nodes(struct DEList *del)
  72. {
  73.     if (del) {
  74.         struct DirEntry *de;
  75.         while (de = (struct DirEntry *)RemTail((struct List *)del)) {
  76.             FreeVecPooled(de->de_Name);
  77.             FreeVecPooled(de);
  78.         }
  79.     }
  80. }
  81.  
  82. /* Get an up-to-date listing of the directory */
  83. static struct DEList *get_delist(struct LocalFile *dir_lf, nfsstat *status)
  84. {
  85.     struct DEList *del = dir_lf->lf_DEList;
  86.     int buf_length;
  87.     STRPTR path_buf;
  88.  
  89.     /* First up, is it actually a directory? Well,
  90.         we used to check that first of all, but hardlinks and softlinks
  91.         muddy the water a little more.
  92.        We now try it anyway, giving the appropriate error if something
  93.         goes wrong.
  94.     */
  95.  
  96.     /* Does this contain a still-valid list? */
  97.     if (del) {
  98.         /* Is it still up-to-date? */
  99.         if (CompareDates(&(fib.fib_Date), &(del->del_DateStamp)) >= 0) {
  100.             return del;
  101.         }
  102.  
  103.         /* No, so let's get rid of the current listing */
  104.         free_delist_nodes(del);
  105.     } else {
  106.         if (del = (struct DEList *)AllocVecPooled(sizeof(struct DEList))) {
  107.             NewList((struct List *)del); /* Hah! Imagine what would happen if I forgot that... */
  108.         }
  109.     }
  110.  
  111.     /* This is long enough for anything up to 256 char filenames
  112.         in this directory */
  113.     buf_length = strlen(dir_lf->lf_Name) + 256 + 2;
  114.     path_buf = AllocVecPooled(buf_length);
  115.  
  116.     /* In fact, if we're being strict, that's about 148 characters more
  117.         than it could be, but we'll plan for the future I think */
  118.  
  119.     if (!del || !path_buf) {
  120.         FreeVecPooled(path_buf);
  121.         FreeVecPooled(del);
  122.         *status = NFSERR_IO; // No memory?
  123.         return (dir_lf->lf_DEList = NULL);
  124.     }
  125.  
  126.     /* Okay, let's date this list */
  127.     CopyMem(&(fib.fib_Date), &(del->del_DateStamp),
  128.         sizeof(struct DateStamp));
  129.  
  130.     DEBUG(printf("Reading the directory '%s'\n", dir_lf->lf_Name));
  131.  
  132.     /* Now let's journey the directory... */
  133.     *status = NFS_OK;
  134.  
  135.     while (ExNext(dir_lf->lf_Lock, &fib)) {
  136.         strcpy(path_buf, dir_lf->lf_Name);
  137.         if (AddPart(path_buf, fib.fib_FileName, buf_length)) {
  138.             struct LocalFile *lf = get_namedlocalfile(path_buf);
  139.             if (lf) {
  140.                 correct_struct(lf); /* Give it a correct inode */
  141.                 struct DirEntry *nde
  142.                     = create_direntry(fib.fib_FileName, lf->lf_FileID);
  143.                 if (nde) {
  144.                     add_in_order(del, nde);
  145.                 } else {
  146.                     *status = NFSERR_IO; /* No memory? */
  147.                     break;
  148.                 }
  149.             } else {
  150.                 *status = NFSERR_IO; /* No memory? */
  151.                 break;
  152.             }
  153.         } else {
  154.             /* We alert the user and leave it out of the listing */
  155.             printf("AddPart() failed - the filename '%s' may be too long.\n", fib.fib_FileName);
  156.         }
  157.     }
  158.  
  159.     FreeVecPooled(path_buf);
  160.  
  161.     /* Did our scan finish unnaturally? */
  162.     if (IoErr() != ERROR_NO_MORE_ENTRIES)
  163.         *status = NFS_wt_err(NFSERR_NOTDIR);
  164.  
  165.     if (*status != NFS_OK) {
  166.         free_delist_nodes(del);
  167.         FreeVecPooled(del);
  168.         printf("Error reading directory '%s'- %s\n", dir_lf->lf_Name,
  169.             reason(*status));
  170.         del = NULL;
  171.     }
  172.  
  173.     return (dir_lf->lf_DEList = del);
  174. }
  175.  
  176. /* Add the DirEntry to the list such that the inodes are in ascending order */
  177. static void add_in_order(struct DEList *del, struct DirEntry *nde)
  178. {
  179.     struct DirEntry *de = (struct DirEntry *)del->del_List.mlh_TailPred;
  180.  
  181.     while (de->de_Node.mln_Pred) {
  182.         if (de->de_Inode < nde->de_Inode) {
  183.             Insert((struct List *)del,
  184.                 (struct Node *)nde, (struct Node *)de);
  185.             return;
  186.         }
  187.  
  188.         de = (struct DirEntry *)de->de_Node.mln_Pred;
  189.     }
  190.  
  191.     /* It's earlier than all others */
  192.     AddHead((struct List *)del, (struct Node *)nde);
  193. }
  194.  
  195.  
  196. /* Public function to satisfy readdir requests. Returns the number
  197.     of int32s it used, rather than the total number in the response.
  198. */
  199. int readdir(struct LocalFile *dir_lf, int32 *rptr, int32 *rlimit, u_int cookie,
  200.     u_int max_count)
  201. {
  202.     struct DEList *del;
  203.  
  204.     /* Use the cookie as the inode we served up to. This avoids
  205.         problems with serving files twice if we rescanned between
  206.         dirattrs */
  207.  
  208.     DEBUG(printf("Asked for listing of '%s' (%d,%d)\n",
  209.         dir_lf->lf_Name, max_count, cookie));
  210.  
  211.     int32 *base_ptr = rptr;
  212.  
  213.     /* We want the count in int32s */
  214.     max_count /= SINT32;
  215.  
  216.     /* Enough for any single entry */
  217.     #define SDE_MAX (4 + INT32S(NFS_MAXNAMELEN))
  218.  
  219.     if (max_count < SDE_MAX) { /* We can't necessarily fit anything in there */
  220.         *rptr = NFSERR_INVAL;
  221.         return 1;
  222.     }
  223.  
  224.     #undef SDE_MAX
  225.  
  226.     nfsstat status;
  227.  
  228.     del = get_delist(dir_lf, &status);
  229.  
  230.     if (del) {
  231.         struct DirEntry *de = del->del_List.mlh_Head;
  232.         int i = 0;
  233.  
  234.         *rptr++ = P_ENUM(NFS_OK);
  235.  
  236.         /* Move forwards up to where we left off */
  237.         while (de->de_Node.mln_Succ && (de->de_Inode < cookie))
  238.             de = de->de_Node.mln_Succ;
  239.  
  240.         /* Now actually copy the entries */
  241.         while (de->de_Node.mln_Succ) {
  242.  
  243.             max_count -= (4 + de->de_NameInt32s);
  244.  
  245.             if ((max_count < 0) ||
  246.                 ((rlimit - rptr) < (6 + de->de_NameInt32s)))
  247.             {
  248.                 *rptr++ = P_ENUM_FALSE; // No entry follows
  249.                 *rptr++ = P_ENUM_FALSE; // This is not the end of the directory
  250.  
  251.                 DEBUG(printf("Didn't finish the directory (sent %d)\n", i));
  252.                 return (rptr - base_ptr);
  253.             }
  254.  
  255.             *rptr++ = P_ENUM_TRUE; // An entry follows
  256.  
  257.             *rptr++ = P_UINT(de->de_Inode);
  258.             *rptr++ = P_UINT(de->de_NameChars);
  259.             CopyMem(de->de_Name, rptr, SINT32 * de->de_NameInt32s);
  260.             rptr += de->de_NameInt32s;
  261.  
  262.             /* Pop a cookie in there */
  263.             *rptr++ = P_UINT(de->de_Inode + 1);
  264.             i++;
  265.  
  266.             de = de->de_Node.mln_Succ;
  267.         }
  268.  
  269.         /* Hey - we finished the directory */
  270.         *rptr++ = P_ENUM_FALSE; // No entry follows
  271.         *rptr++ = P_ENUM_TRUE;  // Yes, it is the end of the directory
  272.  
  273.         DEBUG(printf("Finished the directory (sent %d)\n", i));
  274.  
  275.         return (rptr - base_ptr);
  276.     } else {
  277.         *rptr = P_ENUM(status);
  278.         printf("Failed to get directory listing of '%s' - %s\n",
  279.             dir_lf->lf_Name, reason(status));
  280.         return 1;
  281.     }
  282. }
  283.  
  284.