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