home *** CD-ROM | disk | FTP | other *** search
/ PC-Online 1996 May / PCOnline_05_1996.bin / linux / source / n / tcpip / nfs-serv.1 / nfs-serv / nfs-server-2.1 / fh.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-03-10  |  22.1 KB  |  949 lines

  1. /*
  2.  * fh        This module handles the file-handle cache.
  3.  *        FILE HANDLE PACKAGE FOR USER-LEVEL NFS SERVER
  4.  *
  5.  *        Interfaces:
  6.  *            pseudo_inode
  7.  *            mostly used internally, but also called from unfsd.c
  8.  *            when reporting directory contents.
  9.  *            fh_init
  10.  *            Initializes the queues and 'flush' timer
  11.  *            fh_pr
  12.  *            debugging primitive; converts file handle into a
  13.  *            printable text string
  14.  *            fh_create
  15.  *            establishes initial file handle; called from mount
  16.  *            daemon
  17.  *            fh_path
  18.  *            returns unix path corresponding to fh
  19.  *            fh_fd
  20.  *            returns open file descriptor for given file handle;
  21.  *            provides caching of open files
  22.  *            fd_idle
  23.  *            provides mututal exclusion of normal file descriptor
  24.  *            cache use, and alarm-driven cache flushing
  25.  *            fh_compose
  26.  *            construct new file handle from existing file handle
  27.  *            and directory entry
  28.  *            fh_psi
  29.  *            returns pseudo_inode corresponding to file handle
  30.  *            fh_remove (new, by Don Becker)
  31.  *            delete the file handle associated with PATH from the
  32.  *            cache
  33.  *
  34.  * Authors:    Mark A. Shand, May 1988
  35.  *        Donald J. Becker <becker@super.org>
  36.  *        Rick Sladkey <jrs@world.std.com>
  37.  *        Patrick    Sweeney <pjs@raster.Kodak.COM>
  38.  *        Orest Zborowski <obz@raster.Kodak.COM>
  39.  *        Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
  40.  *        Olaf Kirch <okir@monad.swb.de>
  41.  *
  42.  *        Copyright 1988 Mark A. Shand
  43.  *        This software maybe be used for any purpose provided
  44.  *        the above copyright notice is retained.  It is supplied
  45.  *        as is, with no warranty expressed or implied.
  46.  */
  47.  
  48. #include "nfsd.h"
  49.  
  50. #define hash_psi(psi) (((psi)^((psi)>>8)^((psi)>>16)^((psi)>>24)) & 0xff)
  51.  
  52. static mutex ex_state = inactive;
  53. static mutex io_state = inactive;
  54. static fhcache fh_head, fh_tail, *last_flushable;
  55. static fhcache *fh_hashed[HASH_TAB_SIZE];
  56. static int fh_list_size;
  57. static time_t curtime;
  58.  
  59. #ifndef FOPEN_MAX
  60. #define FOPEN_MAX 256
  61. #endif
  62.  
  63. static fhcache *fd_cache[FOPEN_MAX] = { NULL };
  64. static int fd_cache_size = 0;
  65.  
  66. #ifndef NFSERR_INVAL            /* that Sun forgot */
  67. #define NFSERR_INVAL    22
  68. #endif
  69.  
  70. struct {
  71.     enum nfsstat error;
  72.     int errno;
  73. } nfs_errtbl[]= {
  74.     { NFS_OK,        0        },
  75.     { NFSERR_PERM,        EPERM        },
  76.     { NFSERR_NOENT,        ENOENT        },
  77.     { NFSERR_IO,        EIO        },
  78.     { NFSERR_NXIO,        ENXIO        },
  79.     { NFSERR_ACCES,        EACCES        },
  80.     { NFSERR_EXIST,        EEXIST        },
  81.     { NFSERR_NODEV,        ENODEV        },
  82.     { NFSERR_NOTDIR,    ENOTDIR        },
  83.     { NFSERR_ISDIR,        EISDIR        },
  84.     { NFSERR_INVAL,        EINVAL        },
  85.     { NFSERR_FBIG,        EFBIG        },
  86.     { NFSERR_NOSPC,        ENOSPC        },
  87.     { NFSERR_ROFS,        EROFS        },
  88.     { NFSERR_NAMETOOLONG,    ENAMETOOLONG    },
  89.     { NFSERR_NOTEMPTY,    ENOTEMPTY    },
  90. #ifdef EDQUOT
  91.     { NFSERR_DQUOT,        EDQUOT        },
  92. #endif
  93.     { NFSERR_STALE,        ESTALE        },
  94.     { NFSERR_WFLUSH,    EIO        },
  95.     { -1,            EIO        }
  96. };
  97. int fh_initialized = 0;
  98.  
  99. /* Forward declared local functions */
  100. static _PRO(int path_psi, (char *, nfsstat *, struct stat *, struct stat *));
  101. static _PRO(void flush_cache, (int));
  102.  
  103. void mallocfailed()
  104. {
  105.     dprintf(0, "malloc failed -- exiting\n");
  106.     exit(1);
  107. }
  108.  
  109. static void fh_move_to_front(fhc)
  110. fhcache *fhc;
  111. {
  112.     /* Remove from current posn */
  113.     fhc->prev->next = fhc->next;
  114.     fhc->next->prev = fhc->prev;
  115.  
  116.     /* Insert at head */
  117.     fhc->prev = &fh_head;
  118.     fhc->next = fh_head.next;
  119.     fhc->prev->next = fhc;
  120.     fhc->next->prev = fhc;
  121. }
  122.  
  123. static void fh_inserthead(fhc)
  124. fhcache *fhc;
  125. {
  126.     register fhcache **hash_slot;
  127.  
  128.     /* Insert at head. */
  129.     fhc->prev = &fh_head;
  130.     fhc->next = fh_head.next;
  131.     fhc->prev->next = fhc;
  132.     fhc->next->prev = fhc;
  133.     fh_list_size++;
  134.  
  135.     /* Insert into hash tab. */
  136.     hash_slot = &(fh_hashed[fhc->h.psi % HASH_TAB_SIZE]);
  137.     fhc->hash_next = *hash_slot;
  138.     *hash_slot = fhc;
  139. }
  140.  
  141. static fhcache *fh_lookup(psi)
  142. u_long psi;
  143. {
  144.     register fhcache *fhc;
  145.  
  146.     fhc = fh_hashed[psi % HASH_TAB_SIZE];
  147.     while (fhc != NULL && fhc->h.psi != psi)
  148.         fhc = fhc->hash_next;
  149.     return (fhc);
  150. }
  151.  
  152. static void fh_close(fhc)
  153. fhcache *fhc;
  154. {
  155.     if (fhc->fd >= 0) {
  156.         dprintf(1, "fh_close: closing handle %x ('%s', fd=%d)\n",
  157.               fhc, fhc->path ? fhc->path : "<unnamed>", fhc->fd);
  158. #if 0
  159.         DBASSERT(fd_cache[fhc->fd] == fhc);
  160. #endif
  161.         fd_cache[fhc->fd] = NULL;
  162.         --fd_cache_size;
  163.         close(fhc->fd);
  164.         fhc->fd = -1;
  165.     }
  166. }
  167.  
  168. static void fh_delete(fhc)
  169. fhcache *fhc;
  170. {
  171.     register fhcache **hash_slot;
  172.  
  173.     dprintf(1, "fh_delete: deleting handle %x ('%s', fd=%d)\n", fhc,
  174.         fhc->path ? fhc->path : "<unnamed>", fhc->fd);
  175.  
  176.     /* Remove from current posn */
  177.     fhc->prev->next = fhc->next;
  178.     fhc->next->prev = fhc->prev;
  179.     fh_list_size--;
  180.  
  181.     /* Remove from hash tab */
  182.     hash_slot = &(fh_hashed[fhc->h.psi % HASH_TAB_SIZE]);
  183.     while (*hash_slot != NULL && *hash_slot != fhc)
  184.         hash_slot = &((*hash_slot)->hash_next);
  185.     if (*hash_slot == NULL)
  186.         dprintf(0, "internal inconsistency -- fhc(%x) not in hash table\n",
  187.             fhc);
  188.     else
  189.         *hash_slot = fhc->hash_next;
  190.  
  191.     fh_close(fhc);
  192.  
  193.     /* Free storage. */
  194.     if (fhc->path != NULL)
  195.         free(fhc->path);
  196.     free(fhc);
  197. }
  198.  
  199. /* Lookup an NFS error code and return UNIX equivalent. */
  200. enum nfsstat nfs_errno()
  201. {
  202.     int i;
  203.  
  204.     for (i = 0; nfs_errtbl[i].error != -1; i++) {
  205.         if (nfs_errtbl[i].errno == errno)
  206.             return (nfs_errtbl[i].error);
  207.     }
  208.     dprintf(0, "non-standard errno: %d (%s)\n", errno, strerror(errno));
  209.     return (NFSERR_IO);
  210. }
  211.  
  212. /*
  213.  * INODES and DEVICES.  NFS assumes that each file within an NFS mounted
  214.  * file-system has a unique inode number.  Thus to mount an entire file
  215.  * hierarchy, as this server sets out to do, requires mapping from inode/devs
  216.  * to pseudo-inode.  Furthermore mount points must be detected and so that
  217.  *    pseudo-inode("name") == pseudo-inode(direntry("name/../name"))
  218.  * One option is to force the directory entry inode to correspond to the
  219.  * result of the stat call, but this involves stat'ing every directory entry
  220.  * during a readdir.  Instead we force the stat call to corresopnd to the
  221.  * directory entry inode (see inner_getattr).  Of course this technique
  222.  * requires that the parent directory is readable.  If it is not the normal
  223.  * stat call result is used.  There is no chance of conflict because the
  224.  * directory can never be read.
  225.  *
  226.  * In theory unique pseudo-inodes cannot be guaranteed, since inode/dev
  227.  * contains 48 bits of information which must be crammed into an inode
  228.  * number constrained to 32 bits.  Fortunately inodes numbers tend to be
  229.  * small (often < 64k, almost always < 512k)
  230.  */
  231. int pseudo_inode(inode, dev)
  232. u_long inode;
  233. u_short dev;
  234. {
  235.     register dmajor, dminor;
  236.  
  237.     /*
  238.          * Assuming major and minor numbers are small integers,
  239.          * gravitate bits of dmajor & dminor device number to
  240.          * high-order bits of word, to avoid clash with real inode num.
  241.          */
  242.     /* reverse (byte-wise) */
  243.     dmajor = ((dev & 0xf0f) << 4) | ((dev & 0xf0f0) >> 4);
  244.     dmajor = ((dmajor & 0x3333) << 2) | ((dmajor & 0xcccc) >> 2);
  245.     dmajor = ((dmajor & 0x5555) << 1) | ((dmajor & 0xaaaa) >> 1);
  246.  
  247.     /* spread low-16 -> 32 with 0's in even posn */
  248.     dmajor = ((dmajor & 0xff00) << 8) | (dmajor & 0xff);
  249.     dmajor = ((dmajor & 0xf000f0) << 4) | (dmajor & 0xf000f);
  250.     dmajor = ((dmajor & 0xc0c0c0c) << 2) | (dmajor & 0x3030303);
  251.     dmajor = ((dmajor & 0x22222222) << 1) | (dmajor & 0x11111111);
  252.     dminor = (dmajor & 0x5555) << 15;
  253.     dmajor = dmajor & 0x55550000;
  254.  
  255.     /*
  256.     dprintf(1, "pseudo_inode: dev=%d, inode=%d, psi=%d\n", dev, inode,
  257.         (dmajor | dminor) ^ inode);
  258.     */
  259.     return ((dmajor | dminor) ^ inode);
  260. }
  261.  
  262. static char *fh_buildpath(h)
  263. svc_fh *h;
  264. {
  265.     int i;
  266.     int psi;
  267.     char *path;
  268.     struct stat sbuf;
  269.     char pathbuf[PATH_MAX + NAME_MAX + 1];
  270.     long cookie_stack[HP_LEN + 1];
  271.     char *slash_stack[HP_LEN];
  272.  
  273.     if (stat("/", &sbuf) < 0)
  274.         return (NULL);
  275.     psi = pseudo_inode(sbuf.st_ino, sbuf.st_dev);
  276.     if (h->hash_path[0] == 0) {
  277.         if (psi != h->psi)
  278.             return (NULL);
  279.         if ((path = malloc(sizeof ("/"))) == NULL)
  280.             mallocfailed();
  281.         strcpy(path, "/");
  282.         return (path);
  283.     }
  284.     /* else */
  285.     if (hash_psi(psi) != h->hash_path[1])
  286.         return (NULL);
  287.     strcpy(pathbuf, "/");
  288.     cookie_stack[2] = 0;
  289.     for (i = 2; i <= h->hash_path[0] + 1; i++) {
  290.         DIR *dir;
  291.         struct dirent *dp;
  292.  
  293.     backtrack:
  294.         if (stat(pathbuf, &sbuf) >= 0
  295.             && (dir = opendir(pathbuf)) != NULL) {
  296.             if (cookie_stack[i] != 0)
  297.                 seekdir(dir, cookie_stack[i]);
  298.             while ((dp = readdir(dir))) {
  299.                 if (strcmp(dp->d_name, ".") != 0
  300.                     && strcmp(dp->d_name, "..") != 0) {
  301.                     psi = pseudo_inode(dp->d_ino, sbuf.st_dev);
  302.                     if (i == h->hash_path[0] + 1) {
  303.                         if (psi == h->psi) {
  304.                             /*GOT IT*/
  305.                             strcat(pathbuf, dp->d_name);
  306.                             if ((path = malloc(strlen(pathbuf) + 1)) == NULL)
  307.                                 mallocfailed();
  308.                             strcpy(path, pathbuf);
  309.                             closedir(dir);
  310.                             return (path);
  311.                         }
  312.                     } else {
  313.                         if (hash_psi(psi) == h->hash_path[i]) {
  314.                             /*PERHAPS WE'VE GOT IT */
  315.                             cookie_stack[i] = telldir(dir);
  316.                             cookie_stack[i + 1] = 0;
  317.                             slash_stack[i] = pathbuf + strlen(pathbuf);
  318.                             strcpy(slash_stack[i], dp->d_name);
  319.                             strcat(pathbuf, "/");
  320.  
  321.                             closedir(dir);
  322.                             goto deeper;
  323.                         }
  324.                     }
  325.                 }
  326.             }
  327.             /* dp == NULL */
  328.             closedir(dir);
  329.         } else if (i <= h->hash_path[0] && access(pathbuf, R_OK) != 0
  330.                && access(pathbuf, X_OK) == 0) {
  331.  
  332.             /*
  333.              * Execute-only directory?  Maybe its in the cache.
  334.              * Note: cache is frozen for duration of fh_buildpath.
  335.              */
  336.             svc_fh xh;
  337.             fhcache *fhc;
  338.  
  339.             xh = *h;
  340.             xh.hash_path[0] = i - 1;
  341.             if (cookie_stack[i] == 0)
  342.                 fhc = fh_head.next;
  343.             else
  344.                 fhc = ((fhcache *) (cookie_stack[i]))->next;
  345.             while (fhc != &fh_tail) {
  346.                 if (memcmp(xh.hash_path, fhc->h.hash_path, i) == 0
  347.                     && xh.hash_path[i] == hash_psi(fhc->h.psi))
  348.                     break;
  349.                 else
  350.                     fhc = fhc->next;
  351.             }
  352.             if (fhc != NULL) {
  353.                 strcpy(pathbuf, fhc->path);
  354.                 cookie_stack[i] = (long) fhc;
  355.                 cookie_stack[i + 1] = 0;
  356.                 slash_stack[i] = strrchr(pathbuf, '/') + 1;
  357.                 strcat(pathbuf, "/");
  358.                 goto deeper;
  359.             }
  360.         }
  361.         /* shallower */
  362.         i--;
  363.         if (i < 2)
  364.             return (NULL);    /* SEARCH EXHAUSTED */
  365.  
  366.         /* Prune path */
  367.         *(slash_stack[i]) = '\0';
  368.         goto backtrack;
  369.     deeper:
  370.         ;
  371.     }
  372.     return (NULL);        /* actually not reached */
  373. }
  374.  
  375. static int path_psi(path, status, sbp, tsbp)
  376. char *path;
  377. nfsstat *status;
  378. struct stat *sbp;
  379. struct stat *tsbp;
  380. {
  381.     struct stat sbuf;
  382.  
  383.     if (sbp == NULL)
  384.         sbp = &sbuf;
  385.     if (lstat(path, sbp) < 0) {
  386.         *status = nfs_errno();
  387.         return (0);
  388.     }
  389.     if (tsbp)
  390.         *tsbp = *sbp;
  391.     if (S_ISDIR(sbp->st_mode) && strcmp(path, "/") != 0) {
  392.         /* Special case for directories--test for mount point. */
  393.         struct stat ddbuf;
  394.         char *sindx;
  395.         char *fname;
  396.         char squirrel;
  397.  
  398.         /* Find start of last component of path. */
  399.         if ((sindx = strrchr(path, '/')) == path) {
  400.             sindx++;
  401.             fname = sindx;
  402.         } else
  403.             fname = sindx + 1;
  404.  
  405.         /* Remove last element of path. */
  406.         squirrel = *sindx;
  407.         *sindx = '\0';
  408.         if (lstat(path, &ddbuf) < 0) {
  409.             *sindx = squirrel;
  410.             *status = nfs_errno();
  411.             return (0);
  412.         }
  413.         /* Sindx now points to directory entry name. */
  414.         if (ddbuf.st_dev != sbp->st_dev) {
  415.             /* Directory is a mount point. */
  416.             DIR *dirp;
  417.             struct dirent *dp;
  418.  
  419.             errno = 0;
  420.             if ((dirp = opendir(path)) == NULL) {
  421.                 *sindx = squirrel;    /* restore path */
  422.                 if (errno == EACCES)
  423.                     goto unreadable;
  424.                 if (errno != 0)
  425.                     *status = nfs_errno();
  426.                 else
  427.                     *status = NFSERR_NOENT;
  428.             } else {
  429.                 *sindx = squirrel;    /* restore path */
  430.                 *status = NFS_OK;
  431.                 do {
  432.                     if ((dp = readdir(dirp)) == NULL) {
  433.                         *status = NFSERR_NOENT;
  434.                         closedir(dirp);
  435.                         return (0);
  436.                     }
  437.                 } while (strcmp(fname, dp->d_name) != 0);
  438.                 sbp->st_dev = ddbuf.st_dev;
  439.                 sbp->st_ino = dp->d_ino;
  440.                 closedir(dirp);
  441.             }
  442.         } else
  443.             *sindx = squirrel;    /* restore path */
  444.     unreadable:
  445.         ;
  446.     }
  447.     return (pseudo_inode(sbp->st_ino, sbp->st_dev));
  448. }
  449.  
  450. fhcache *fh_find(h, create)
  451. svc_fh *h;
  452. int create;
  453. {
  454.     char buff[1024], *sp;
  455.     register fhcache *fhc;
  456.  
  457.     sprintf(buff, "fh_find: psi=%lu... ", (unsigned long) h->psi);
  458.     sp = buff + strlen(buff);
  459.     ex_state = active;
  460.     time(&curtime);
  461.     while ((fhc = fh_lookup(h->psi)) != NULL) {
  462.         dprintf(1, "%s found '%s', fd=%d\n", buff,
  463.             fhc->path ? fhc->path : "<unnamed>",
  464.             fhc->fd);
  465.  
  466.         /* But what if hash_paths are not the same? Something is stale. */
  467.         if (memcmp(h->hash_path, fhc->h.hash_path, HP_LEN) != 0) {
  468.             dprintf(1, "fh_find: path mismatch - stale!\n");
  469.             if (!create) {
  470.                 ex_state = inactive;
  471.                 return (NULL);
  472.             }
  473.             fh_delete(fhc);
  474.             dprintf(1, "fh_find: deleted old handle... sortof\n");
  475.             break;
  476.         }
  477.         if (client_authenticate && fhc->path) {
  478.             if (auth_clnt(svc_rqstp, fhc->path) == NULL)
  479.                 return NULL;
  480.         }
  481.         if (fhc != fh_head.next)
  482.             fh_move_to_front(fhc);
  483.         fhc->last_used = curtime;
  484.         ex_state = inactive;
  485.         return (fhc);
  486.     }
  487.  
  488.     dprintf(1, "%s not found.\n", buff);
  489.     if (fh_list_size > CACHE_SIZE_LIMIT) {
  490.         /* Don't flush current head. */
  491.         while (last_flushable != fh_head.next) {
  492.             if ((last_flushable->flags & FHC_XONLY_PATH) == 0) {
  493.                 fhc = last_flushable;
  494.                 last_flushable = last_flushable->prev;
  495.                 fh_delete(fhc);
  496.                 break;
  497.             }
  498.             last_flushable = last_flushable->prev;
  499.         }
  500.         last_flushable = last_flushable->next;
  501.     }
  502.     if ((fhc = (fhcache *) malloc(sizeof *fhc)) == NULL)
  503.         mallocfailed();
  504.     if (create)
  505.         fhc->path = NULL;
  506.     else {
  507.         /* attempt to contruct from hash_path */
  508.         char *path;
  509.  
  510.         if ((path = fh_buildpath(h)) == NULL) {
  511.             free(fhc);
  512.             ex_state = inactive;
  513.             return NULL;
  514.         }
  515.         fhc->path = path;
  516.         if (client_authenticate) {
  517.             if (auth_clnt(svc_rqstp, path) == NULL) {
  518.                 free(fhc);
  519.                 return NULL;
  520.             }
  521.         }
  522.     }
  523.     fhc->flags = 0;
  524.     fhc->fd = -1;
  525.     fhc->last_used = curtime;
  526.     fhc->h = *h;
  527.     fh_inserthead(fhc);
  528.     dprintf(1, "fh_find: created new handle %x ('%s')\n", fhc,
  529.         fhc->path ? fhc->path : "<unnamed>");
  530.     ex_state = inactive;
  531.     if (fh_list_size > HIWAT_CACHE_SIZE)
  532.         flush_cache(0);
  533.     return (fhc);
  534. }
  535.  
  536. static int fh_flush_fds()
  537. {
  538.     int i;
  539.     fhcache *discard;
  540.  
  541.     if (io_state == active) {
  542.         dprintf(1, "fh_flush_fds: not flushing... io active\n");
  543.         return (-1);
  544.     }
  545.     while (fd_cache_size > FD_CACHE_LIMIT) {
  546.         discard = NULL;
  547.         for (i = 0; i < FOPEN_MAX; ++i) {
  548.             if (fd_cache[i] && (discard == NULL ||
  549.                 fd_cache[i]->last_used < discard->last_used))
  550.                 discard = fd_cache[i];
  551.         }
  552.         if (discard == NULL) {
  553.             dprintf(1, "fh_flush_fds: can't find oldest??\n");
  554.             return (-1);
  555.         }
  556.         dprintf(1, "fh_flush_fds: discarding old handle %x\n", discard);
  557.         fh_close(discard);
  558.     }
  559.     return (0);
  560. }
  561.  
  562. /*
  563.  * flush_cache() is invoked periodically from SIGALRM, and on
  564.  * demand from fh_find.  A simple form of mutual exclusion
  565.  * protects this routine from multiple concurrent executions.
  566.  * Since the preemption that occurs when a signal is received
  567.  * is one-sided, we do need an atomic test and set.  If the
  568.  * signal arrives between the test and the set, the first
  569.  * invocation safely stalls until the signal-caused invocation
  570.  * completes.
  571.  */
  572. static void flush_cache(sig)
  573. int sig;
  574. {
  575.     register fhcache *h;
  576.  
  577. #ifdef DEBUG
  578.     time_t now;
  579.     time(&now);
  580.     dprintf(1, "flushing cache at %s: state = %s\n", ctime(&now),
  581.         (ex_state == inactive) ? "inactive" : "active");
  582. #endif
  583.  
  584.     if (ex_state == inactive) {
  585.         int cache_size = 0;
  586.  
  587.         ex_state = active;
  588.         time(&curtime);
  589.         /* Single execution thread */
  590.  
  591.         /* works in empty case because: fh_tail.next = &fh_tail */
  592.         h = fh_head.next;
  593.         while (h != &fh_tail) {
  594.             if (cache_size > LOWAT_CACHE_SIZE
  595.                 || (cache_size > CACHE_SIZE_LIMIT
  596.                 && (h->flags & FHC_XONLY_PATH) == 0)
  597.                 || curtime > h->last_used + DISCARD_INTERVAL
  598.                 || sig == SIGHUP) {
  599.                 h = h->next;
  600.                 fh_delete(h->prev);
  601.             } else {
  602.                 cache_size++;
  603.                 h = h->next;
  604.             }
  605.         }
  606.         if (fh_list_size != cache_size)
  607.             dprintf(0, "internal inconsistency (fh_list_size=%d) != (cache_size=%d)\n",
  608.                 fh_list_size, cache_size);
  609.         fh_list_size = cache_size;
  610.         ex_state = inactive;
  611.         if (_rpcpmstart) {
  612.             signal(SIGALRM, flush_cache);
  613.             alarm(FLUSH_INTERVAL);
  614.         }
  615.     } else {
  616.         if (!_rpcpmstart) {
  617.             signal(SIGALRM, flush_cache);
  618.             alarm(BUSY_RETRY_INTERVAL);
  619.         }
  620.     }
  621. }
  622.  
  623. void fh_init()
  624. {
  625.     if (fh_initialized)
  626.         return;
  627.     fh_initialized = 1;
  628.  
  629.     fh_head.next = fh_tail.next = &fh_tail;
  630.     fh_head.prev = fh_tail.prev = &fh_head;
  631.     last_flushable = &fh_tail;
  632.     fh_tail.flags = FHC_XONLY_PATH;
  633.     signal(SIGHUP, flush_cache);
  634.     if (!_rpcpmstart) {
  635.         signal(SIGALRM, flush_cache);
  636.         alarm(FLUSH_INTERVAL);
  637.     }
  638. }
  639.  
  640. char *fh_pr(fh)
  641. nfs_fh *fh;
  642. {
  643.     char *p;
  644.     nfsstat status;
  645.  
  646.     p = fh_path(fh, &status);
  647.     if (status != NFS_OK)
  648.         return ("///STALE///");
  649.     else
  650.         return (p);
  651. }
  652.  
  653. /*
  654.  * This routine is only used by the mount daemon.
  655.  * It creates the initial file handle.
  656.  */
  657. int fh_create(fh, path)
  658. nfs_fh *fh;
  659. char *path;
  660. {
  661.     svc_fh *key = (svc_fh *) fh;
  662.     fhcache *h;
  663.     int psi;
  664.     nfsstat status;
  665.     char *s;
  666.  
  667.     memset((char *) fh, 0, sizeof (nfs_fh));
  668.     key->hash_path[0] = 0;
  669.     status = NFS_OK;
  670.     if ((psi = path_psi("/", &status, NULL, NULL)) == 0)
  671.         return ((int) status);
  672.     s = path;
  673.     while ((s = strchr(s + 1, '/')) != NULL) {
  674.         if (++(key->hash_path[0]) >= HP_LEN)
  675.             return ((int) NFSERR_NAMETOOLONG);
  676.         key->hash_path[key->hash_path[0]] = hash_psi(psi);
  677.         *s = '\0';
  678.         if ((psi = path_psi(path, &status, NULL, NULL)) == 0)
  679.             return ((int) status);
  680.         *s = '/';
  681.     }
  682.     if (*(strrchr(path, '/') + 1) != '\0') {
  683.         if (++(key->hash_path[0]) >= HP_LEN)
  684.             return ((int) NFSERR_NAMETOOLONG);
  685.         key->hash_path[key->hash_path[0]] = hash_psi(psi);
  686.         if ((psi = path_psi(path, &status, NULL, NULL)) == 0)
  687.             return ((int) status);
  688.     }
  689.     key->psi = psi;
  690.     h = fh_find(key, 1);
  691.  
  692.     /* assert(h != NULL); */
  693.     if (h->path == NULL) {
  694.         h->fd = -1;
  695.         if ((h->path = malloc(strlen(path) + 1)) == NULL)
  696.             mallocfailed();
  697.         strcpy(h->path, path);
  698.         h->flags = 0;
  699.     }
  700.     return ((int) status);
  701. }
  702.  
  703. char *fh_path(fh, status)
  704. nfs_fh *fh;
  705. nfsstat *status;
  706. {
  707.     fhcache *h;
  708.  
  709.     if ((h = fh_find((svc_fh *) fh, 0)) == NULL) {
  710.         *status = NFSERR_STALE;
  711.         return (NULL);
  712.     }
  713.     *status = NFS_OK;
  714.     return (h->path);
  715. }
  716.  
  717. int path_open(path, omode, perm)
  718. char *path;
  719. int omode;
  720. int perm;
  721. {
  722.     int fd;
  723.     int oerrno, euid;
  724.     struct stat buf;
  725.  
  726.     fh_flush_fds();
  727.  
  728.     fd = open(path, omode, perm);
  729.  
  730.     oerrno = errno;
  731.  
  732.     /* The file must exist at this point. */
  733.     if (lstat(path, &buf) < 0) {
  734.         oerrno = oerrno;
  735.         return -1;
  736.     }
  737.  
  738.     /* Emulate SunOS NFS server for I/O requests on non-files. */
  739.     if (!S_ISREG(buf.st_mode)) {
  740.         errno = EISDIR;
  741.         return -1;
  742.     }
  743.  
  744.     /* Do some serious cheating for statelessness. */
  745.     if (fd < 0) {
  746.         if (oerrno == EACCES && (buf.st_uid == (euid = geteuid())
  747.             || (omode == O_RDONLY && (buf.st_mode & S_IXOTH)))) {
  748.             seteuid(0);
  749.             fd = open(path, omode, perm);
  750.             oerrno = errno;
  751.             seteuid(euid);
  752.         }
  753.     }
  754.  
  755.     errno = oerrno;
  756.     return (fd);
  757. }
  758.  
  759. int fh_fd(fh, status, omode)
  760. nfs_fh *fh;
  761. nfsstat *status;
  762. int omode;
  763. {
  764.     fhcache *h;
  765.  
  766.     if ((h = fh_find((svc_fh *) fh, 0)) == NULL) {
  767.         *status = NFSERR_STALE;
  768.         return (-1);
  769.     }
  770.     if (h->fd >= 0) {
  771.         if (h->omode == omode ||
  772.             ((omode == O_RDONLY || omode == O_WRONLY) && h->omode == O_RDWR)) {
  773.             dprintf(1, "fh_fd: reusing fd=%d\n", h->fd);
  774. #if 0
  775.             DBASSERT(fd_cache[h->fd] == h);
  776. #endif
  777.             return (h->fd);
  778.         }
  779.         dprintf(1, "fh_fd: mismatch in omode (%d wanted, %d cached)\n",
  780.             omode, h->omode);
  781.         fh_close(h);
  782.     }
  783.     errno = 0;
  784.     if (!h->path)
  785.         return (-1);    /* something is really hosed */
  786.     if ((h->fd = path_open(h->path, omode, 0)) >= 0) {
  787.         io_state = active;
  788.         h->omode = omode;
  789. #if 0
  790.         DBASSERT(fd_cache[h->fd] == NULL);
  791. #endif
  792.         fd_cache[h->fd] = h;
  793.         ++fd_cache_size;
  794.         dprintf(1, "fh_fd: new open as fd=%d\n", h->fd);
  795.     } else {
  796.         dprintf(1, "fh_fd: open failed.\n");
  797.         *status = nfs_errno();
  798.     }
  799.     return (h->fd);
  800. }
  801.  
  802. void fd_inactive(fd)
  803. int fd;
  804. {
  805.     io_state = inactive;
  806. }
  807.  
  808. nfsstat fh_compose(dopa, new_fh, sbpp, fd, omode)
  809. diropargs *dopa;
  810. nfs_fh *new_fh;
  811. struct stat **sbpp;
  812. int fd;
  813. int omode;
  814. {
  815.     svc_fh *key;
  816.     fhcache *dirh, *h;
  817.     char *sindx;
  818.     int is_dd;
  819.     nfsstat ret;
  820.     struct stat tsbuf;
  821.     char pathbuf[PATH_MAX + NAME_MAX + 1];
  822.  
  823.     if ((dirh = fh_find((svc_fh *) & (dopa->dir), 0)) == NULL)
  824.         return (NFSERR_STALE);
  825.  
  826.     /* This allows only single directories to be looked up, could be
  827.        a bit more sophisticated, but i don't know if that is neccesary */
  828.     if (strchr(dopa->name, '/') != NULL)
  829.         return(NFSERR_ACCES);
  830.  
  831.     /* Construct path */
  832.     if (strcmp(dopa->name, ".") == 0) {
  833.         *new_fh = dopa->dir;
  834.         *sbpp = NULL;
  835.         return (NFS_OK);
  836.     }
  837.     if (strcmp(dopa->name, "..") == 0) {
  838.         is_dd = 1;
  839.         sindx = strrchr(dirh->path, '/');
  840.         if (sindx == dirh->path)
  841.             strcpy(pathbuf, "/");
  842.         else {
  843.             int len = sindx - dirh->path;
  844.             strncpy(pathbuf, dirh->path, len);
  845.             pathbuf[len] = '\0';
  846.         }
  847.         if (client_authenticate) {
  848.             if (auth_clnt(svc_rqstp, pathbuf) == NULL)
  849.                 return (NFSERR_ACCES);
  850.         }
  851.     }
  852.     else if (!re_export && (dirh->flags & FHC_NFSMOUNTED))
  853.         return (NFSERR_NOENT);
  854.     else {
  855.         int len = strlen(dirh->path);
  856.  
  857.         is_dd = 0;
  858.         if (dirh->path[len - 1] == '/')
  859.             len--;
  860.         strncpy(pathbuf, dirh->path, len);
  861.         pathbuf[len] = '/';
  862.         strcpy(pathbuf + (len + 1), dopa->name);
  863.     }
  864.  
  865.     *new_fh = dopa->dir;
  866.     key = (svc_fh *) new_fh;
  867.     if ((key->psi = path_psi(pathbuf, &ret, *sbpp, &tsbuf)) == 0)
  868.         return (ret);
  869.  
  870.     if (is_dd)
  871.         key->hash_path[key->hash_path[0]--] = 0;
  872.     else {
  873.         if (++(key->hash_path[0]) >= HP_LEN)
  874.             return (NFSERR_NAMETOOLONG);
  875.         key->hash_path[key->hash_path[0]] = hash_psi(dirh->h.psi);
  876.     }
  877.     h = fh_find(key, 1);
  878.  
  879.     /* New code added by Don Becker */
  880.     if ((h->path != NULL) && (strcmp(h->path, pathbuf) != 0)) {
  881.         /* We must have cached an old file under the same inode number. */
  882.         fh_delete(h);
  883.         h = fh_find(key, 1);
  884.         if (h->path)
  885.             dprintf(0, "Internal inconsistency: double entry (path '%s', now '%s').\n",
  886.                 h->path, pathbuf);
  887.     }
  888.     dprintf(1, "fh_compose: using  handle %x ('%s', fd=%d)\n",
  889.         h, h->path ? h->path : "<unnamed>", h->fd);
  890.     /* End of new code */
  891.  
  892.     /* assert(h != NULL); */
  893.     if (h->path == 0) {
  894.         if ((h->path = malloc(strlen(pathbuf) + 1)) == NULL)
  895.             mallocfailed();
  896.         strcpy(h->path, pathbuf);
  897.         h->flags = 0;
  898.         if (!re_export && nfsmounted(pathbuf, &tsbuf))
  899.             h->flags |= FHC_NFSMOUNTED;
  900.         if (!is_dd && access(dirh->path, R_OK) != 0 &&
  901.             access(dirh->path, X_OK) == 0)
  902.             h->flags |= FHC_XONLY_PATH;
  903.         dprintf(1, "fh_compose: +using  handle %x ('%s', fd=%d)\n",
  904.             h, h->path ? h->path : "<unnamed>", h->fd);
  905.     }
  906.     if (fd >= 0) {
  907.         dprintf(1, "fh_compose: handle %x using passed fd %d\n", h, fd);
  908. #if 0
  909.         DBASSERT(fd_cache[fd] == NULL);
  910. #endif
  911.         if (h->fd >= 0)
  912.             fh_close(h);
  913.         h->fd = fd;
  914.         fd_cache[fd] = h;
  915.         ++fd_cache_size;
  916.         dprintf(1, "fh_compose: +using  handle %x ('%s', fd=%d)\n",
  917.             h, h->path ? h->path : "<unnamed>", h->fd);
  918.     }
  919.     if (omode >= 0)
  920.         h->omode = omode;
  921.     return (NFS_OK);
  922. }
  923.  
  924. int fh_psi(fh)
  925. nfs_fh *fh;
  926. {
  927.     svc_fh *h = (svc_fh *) fh;
  928.     return (h->psi);
  929. }
  930.  
  931. void fh_remove(path)
  932. char *path;
  933. {
  934.     int psi;
  935.     nfsstat status;
  936.     fhcache *fhc;
  937.  
  938.     psi = path_psi(path, &status, NULL, NULL);
  939.     if (psi == 0)
  940.         return;
  941.     ex_state = active;
  942.     fhc = fh_lookup(psi);
  943.     if (fhc != NULL)
  944.         fh_delete(fhc);
  945.  
  946.     ex_state = inactive;
  947.     return;
  948. }
  949.