home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / misc / volume39 / enh-du2 / part01 / fsinfo.c < prev    next >
C/C++ Source or Header  |  1993-08-21  |  17KB  |  689 lines

  1. #ifndef lint
  2. static char SCCSID[] = "@(#) fsinfo.c 1.25 93/08/18 00:09:06";
  3. #endif
  4.  
  5. /*
  6.  * "du" enhanced disk usage summary - version 2.
  7.  *
  8.  * Copyright 1990-1993, Unicom Systems Development.  All rights reserved.
  9.  * See accompanying README file for terms of distribution and use.
  10.  *
  11.  * Edit at tabstops=4.
  12.  */
  13.  
  14. /*
  15.  * All of the machine-specific filesystem support should be encapsulated
  16.  * within this one file.  We maintain the necessary filesystem information
  17.  * as a dynamically allocated list of (struct fsinfo) records, one record
  18.  * per mounted filesystem.
  19.  *
  20.  * There are four routines for working with filesystem information.
  21.  *
  22.  * fs_initinfo() - Initialize the "Fsinfo_list" filesystem information.
  23.  * fs_getinfo() - Gets filesystem information on a entry.
  24.  * fs_linkdone() - Determines whether a file has been visited already.
  25.  * fs_numblocks() - Calculates disk usage of an entry.
  26.  *
  27.  * One of the trickiest tasks is discovering what filesystems are mounted.
  28.  * This is *highly* system specific.  We also provide a set of three
  29.  * routines -- open_mnttab(), read_mnttab(), and close_mnttab() -- for
  30.  * fs_initinfo() to use to get this information.
  31.  */
  32.  
  33. #include "config.h"
  34. #include <stdio.h>
  35. #include <string.h>
  36. #ifdef USE_UNISTD
  37. # include <unistd.h>
  38. #endif
  39. #include <sys/types.h>
  40. #include <sys/stat.h>
  41. #include "du.h"
  42.  
  43. /*
  44.  * Within each filesystem information record, we maintain a dynamically
  45.  * allocated bit vector that indicates which inodes have been already
  46.  * visited on the filesystem.  If the system has a working statfs() call
  47.  * then we can preallocate the bit vector to precisely the right size.
  48.  * If statfs() is missing (e.g. old SysV) or broken (e.g. the Neurotic
  49.  * File System) we will instead make a guess and grow the bit vector as
  50.  * needed.   In this case, the allocation increment will be in chunks of
  51.  * ITABINCR/8 bytes (since one byte will hold the status of 8 inodes).
  52.  * Selection of this value is a simple speed vs memory tradeoff.
  53.  */
  54. #define ITABINCR 8192
  55.  
  56. /*
  57.  * Length of bit vector required for a particular number of inodes.
  58.  */
  59. #define NUMINO_TO_VECLEN(NUMINO) ((NUMINO)/8 + 1)
  60.  
  61. /*
  62.  * Can you believe this shit?
  63.  */
  64. #ifdef USE_MOUNT_MNTTAB
  65. #    include <mnttab.h>
  66. #    define mount_struct mnttab
  67. #    define mount_device mt_dev
  68. #    define mount_point  mt_filsys
  69. #endif
  70. #ifdef USE_MOUNT_R4MNTTAB
  71. #    include <sys/mnttab.h>
  72. #    define mount_struct mnttab
  73. #    define mount_device mnt_special
  74. #    define mount_point  mnt_mountp
  75. #endif
  76. #ifdef USE_MOUNT_MNTENT
  77. #    include <mntent.h>
  78. #    define mount_struct mntent
  79. #    define mount_device mnt_fsname
  80. #    define mount_point  mnt_dir
  81. #endif
  82. #ifdef USE_MOUNT_FSTAB
  83. #    include <fstab.h>
  84. #    define mount_struct fstab
  85. #    define mount_device fs_spec
  86. #    define mount_point  fs_file
  87. #endif
  88. #ifdef USE_MOUNT_MNTCTL
  89. #    include <sys/mntctl.h>
  90. #    include <sys/vmount.h>
  91.     struct mount_struct {
  92.         char *mount_device;
  93.         char *mount_point;
  94.     };
  95. #endif
  96.  
  97. /*
  98.  * sigh...
  99.  */
  100. #ifdef USE_STATFS_SYSV
  101. #    include <sys/statfs.h>
  102. #    define STATFS(MNT, PTR, PSIZE, FSTYPE) \
  103.         statfs(MNT, PTR, PSIZE, FSTYPE)
  104. #endif
  105. #ifdef USE_STATFS_BSD
  106. #    include <sys/mount.h>
  107. #    define STATFS(MNT, PTR, PSIZE, FSTYPE) \
  108.         statfs(MNT, PTR)
  109. #endif
  110. #ifdef USE_STATFS_SUN
  111. #    include <sys/vfs.h>
  112. #    define STATFS(MNT, PTR, PSIZE, FSTYPE) \
  113.         statfs(MNT, PTR, PSIZE, FSTYPE)
  114. #endif
  115. #ifdef USE_STATFS_HPUX
  116. #    include <sys/vfs.h>
  117. #    define STATFS(MNT, PTR, PSIZE, FSTYPE) \
  118.         statfs(MNT, PTR)
  119. #endif
  120. #ifdef USE_STATFS_NONE
  121. #    include <sys/param.h>
  122.     struct statfs {
  123.         long f_bsize, f_files;
  124.     };
  125. #    define STATFS(MNT, PTR, PSIZE, FSTYPE) \
  126.         ((PTR)->f_bsize = BSIZE, (PTR)->f_files = 0, 0)
  127. #endif
  128.  
  129.  
  130. /*
  131.  * Dynamically allocated list of filesystem information records.
  132.  */
  133. struct fsinfo **Fsinfo_list;
  134. int Fsinfo_size;
  135.  
  136. /*
  137.  * Mount table handling routines.
  138.  */
  139. int open_mnttab __ARGS((void));
  140. struct mount_struct *read_mnttab __ARGS((void));
  141. int close_mnttab __ARGS((void));
  142.  
  143.  
  144. /*
  145.  * fs_initinfo() - Initialize the "Fsinfo_list" filesystem information.
  146.  */
  147. void fs_initinfo()
  148. {
  149.     struct fsinfo *fsp;
  150.     struct mount_struct *mnt;
  151.     struct stat sbuf;
  152.     struct statfs fsbuf;
  153.     int n;
  154.  
  155.     Fsinfo_list = (struct fsinfo **) xmalloc(sizeof(struct fsinfo *));
  156.     Fsinfo_list[0] = NULL;
  157.     Fsinfo_size = 0;
  158.  
  159.     /*
  160.      * Open up the mount table.
  161.      */
  162.     if (open_mnttab() != 0)
  163.         errmssg(ERR_ABORT, errno, "could not open mount table");
  164.  
  165.     /*
  166.      * Initialize a filesystem information record for each mounted filesystem.
  167.      */
  168.     while ((mnt = read_mnttab()) != NULL) {
  169.  
  170.         /*
  171.          * Get the information on this filesystem.
  172.          */
  173.         if (stat(mnt->mount_point, &sbuf) != 0) {
  174.             errmssg(ERR_WARN, errno, "could not stat \"%s\"", mnt->mount_point);
  175.             continue;
  176.         }
  177.         if (STATFS(mnt->mount_point, &fsbuf, sizeof(struct statfs), 0) != 0) {
  178.             errmssg(ERR_WARN, errno,
  179.                 "could not statfs \"%s\"", mnt->mount_point);
  180.             continue;
  181.         }
  182.  
  183.         /*
  184.          * Allocate and initialize the filesystem information structure.
  185.          * If "f_files" is zero then we take an initial guess at the
  186.          * number of inodes.  This will happen if this system does not
  187.          * have a statfs() call or this is an NFS filesystem.
  188.          */
  189.         fsp = (struct fsinfo *) xmalloc(sizeof(struct fsinfo));
  190.         fsp->dev = sbuf.st_dev;
  191.         fsp->nino = (fsbuf.f_files > 0 ? fsbuf.f_files : ITABINCR);
  192.         fsp->bsize = fsbuf.f_bsize;
  193.         fsp->nindir = fsp->bsize / sizeof(daddr_t);
  194.         fsp->remote = (sbuf.st_dev < 0) ||
  195.             (strchr(mnt->mount_device, ':') != NULL);
  196.  
  197. #ifdef BROKE_STBLOCKS
  198.         /* very cool algorithm by Lars Henrik Mathiesen <thorinn@diku.dk> */
  199.         if (sbuf.st_blocks == 0) {
  200.             errmssg(ERR_WARN, 0,
  201.                 "cannot determine \"%s\" stat blksize - assuming 512",
  202.                 mnt->mount_device);
  203.             fsp->stbsize = 512;
  204.         } else {
  205.             int round, ratio;
  206.             round = (sbuf.st_size + fsbuf.f_bsize - 1) & ~(fsbuf.f_bsize - 1);
  207.             ratio = round / sbuf.st_blocks;
  208.             for (fsp->stbsize = 512 ; fsp->stbsize < ratio ; fsp->stbsize <<= 1)
  209.                 ;
  210.         }
  211. #endif
  212.  
  213.         /*
  214.          * Determine how long pathname buffers should be for
  215.          * entries on this filesystem.
  216.          */
  217.         fsp->path_max = max_path_len(mnt->mount_point);
  218.  
  219.         /*
  220.          * Create the bit vector that is used by fs_linkdone() to track
  221.          * which inodes have already been encountered.
  222.          */
  223.         n = NUMINO_TO_VECLEN(fsp->nino);
  224.         fsp->idone = (unsigned char *) xmalloc((unsigned)n);
  225.         (void) memset((PTRTYPE *)fsp->idone, 0, n);
  226.  
  227.         /*
  228.          * Attach the filesystem information to the end of the list.
  229.          */
  230.         Fsinfo_list = (struct fsinfo **) xrealloc((PTRTYPE *)Fsinfo_list,
  231.             (Fsinfo_size+2)*sizeof(struct fsinfo *));
  232.         Fsinfo_list[Fsinfo_size++] = fsp;
  233.         Fsinfo_list[Fsinfo_size] = NULL;
  234.  
  235. #ifdef DEBUG
  236.         Dprintf(stderr, "*** mount_device=\"%s\" mount_point=\"%s\"\n",
  237.             mnt->mount_device, mnt->mount_point);
  238.         Dprintf(stderr, "  remote=%s dev=%d nino=%d bsize=%d nindir=%d\n",
  239.             (fsp->remote ? "TRUE" : "FALSE"),
  240.             fsp->dev, fsp->nino, fsp->bsize, fsp->nindir);
  241. #ifdef BROKE_STBLOCKS
  242.         Dprintf(stderr, "   stbsize=%d", fsp->stbsize);
  243. #endif
  244.         Dprintf(stderr, "   path_max=%d\n", fsp->path_max);
  245. #endif
  246.  
  247.     }
  248.  
  249.     (void) close_mnttab();
  250.  
  251. }
  252.  
  253.  
  254. /*
  255.  * fs_getinfo() - Gets filesystem information on a entry.
  256.  *
  257.  * Given the inode info for an entry, locate the corresponding filesystem
  258.  * info record and return a pointer to it.  The most recent result is cached
  259.  * to reduce the number of list searches.
  260.  */
  261. struct fsinfo *fs_getinfo(sbufp)
  262. register struct stat *sbufp;
  263. {
  264.     static struct fsinfo *fsp_save = NULL;
  265.     register struct fsinfo **fsp;
  266.  
  267.     if (fsp_save != NULL && fsp_save->dev == sbufp->st_dev)
  268.         return fsp_save;
  269.     for (fsp = Fsinfo_list ; *fsp != NULL ; ++fsp) {
  270.         if ((*fsp)->dev == sbufp->st_dev) {
  271.             fsp_save = *fsp;
  272.             return fsp_save;
  273.         }
  274.     }
  275.     return (struct fsinfo *) NULL;
  276. }
  277.  
  278.  
  279. /*
  280.  * fs_linkdone() - Determines whether a file has been visited already.
  281.  *
  282.  * This procedure implements the logic to avoid recounting of multiply
  283.  * linked files.  Each (struct fsinfo) contains a bit vector to track
  284.  * which inodes have already been visited.  The first time this procedure
  285.  * is called for a particular inode, we return FALSE and mark it in the bit
  286.  * vector.  TRUE is returned subsequent times this procedure is called for
  287.  * the same inode.
  288.  */
  289. int fs_linkdone(fsp, sbufp)
  290. register struct fsinfo *fsp;
  291. register struct stat *sbufp;
  292. {
  293.     register unsigned char *rowp;
  294.     int mask;
  295.     unsigned old_bytes, new_bytes;
  296.  
  297.     /*
  298.      * Verify we haven't gone off the edge of the bit vector.  This
  299.      * could happen if we had to take an initial guess at the number
  300.      * of inodes.
  301.      */
  302.     if (sbufp->st_ino > fsp->nino) {
  303.  
  304.         old_bytes = NUMINO_TO_VECLEN(fsp->nino);
  305. #ifdef DEBUG
  306.         Dprintf(stderr, "*** growing bit vector for device %d at inode %d\n",
  307.             fsp->dev, sbufp->st_ino);
  308.         Dprintf(stderr, "  old vector size %d bytes (%d inodes)\n",
  309.             old_bytes, fsp->nino);
  310. #endif
  311.         while (sbufp->st_ino > fsp->nino)
  312.             fsp->nino += ITABINCR;
  313.         new_bytes = NUMINO_TO_VECLEN(fsp->nino);
  314. #ifdef DEBUG
  315.         Dprintf(stderr, "  new vector size %d bytes (%d inodes)\n",
  316.             new_bytes, fsp->nino);
  317. #endif
  318.  
  319.         fsp->idone =
  320.             (unsigned char *)xrealloc((PTRTYPE *)fsp->idone, new_bytes);
  321.         (void) memset((PTRTYPE *)(fsp->idone+old_bytes),
  322.             0, new_bytes-old_bytes);
  323.     }
  324.  
  325.     /*
  326.      * Locate the bit within the vector for this inode.
  327.      */
  328.     rowp = fsp->idone + (sbufp->st_ino >> 3);
  329.     mask = 1 << (sbufp->st_ino & 07);
  330.  
  331.     /*
  332.      * If the bit is set then this link was already done.
  333.      */
  334.     if (*rowp & mask)
  335.         return TRUE;
  336.  
  337.     /*
  338.      * Set the bit and indicate the link hasn't been done yet.
  339.      */
  340.     *rowp |= mask;
  341.     return FALSE;
  342. }
  343.  
  344.  
  345. /*
  346.  * A classic UNIX filesys contains ten data block addresses in the inode.
  347.  */
  348. #define DIRBLKS        10
  349.  
  350. /*
  351.  * Macro to calculate ceiling(A/B) pretty efficiently.
  352.  */
  353. #define CEIL_DIV(A, B)  (((A)+(B)-1) / (B))
  354.  
  355. /*
  356.  * fs_numblocks() - Calculates disk usage of an entry.
  357.  *
  358.  * This routine is a profiling "hot spot".  It is called for every entry,
  359.  * and about 15% of the time appears to be spent here.  Unfortunately, the
  360.  * calculation this routine has to implement is apparently easy to botch given
  361.  * the number of vendor's du's that get it wrong.  Therefore, I traded a
  362.  * bit of speed for readability and clarity.  Even still, my benchmarking
  363.  * tests show that it stands up well to other implementations.
  364.  */
  365. long fs_numblocks(fsp, sbufp)
  366. register struct fsinfo *fsp;
  367. struct stat *sbufp;
  368. {
  369.  
  370.     register long    n_used;        /* num blocks used, incl overhead    */
  371.  
  372. #ifdef USE_STBLOCKS /*{*/
  373.  
  374.     /*
  375.      * Ignore this entry if we are counting usage for a specific
  376.      * user and that user doesn't own this entry.
  377.      */
  378.     if (Selected_user >= 0 && sbufp->st_uid != Selected_user)
  379.         return 0L;
  380.  
  381. #ifdef BROKE_STBLOCKS
  382. # define STBSIZE(fsp)    ((fsp)->stbsize)
  383. #else
  384. # define STBSIZE(fsp)    (512)
  385. #endif
  386.  
  387.     /*
  388.      * This is a piece of cake on systems with "st_blocks".
  389.      */
  390.     if (Report_blksize == STBSIZE(fsp))
  391.         n_used = sbufp->st_blocks;
  392.     else if (Report_blksize == 0)
  393.         n_used = CEIL_DIV(sbufp->st_blocks*STBSIZE(fsp), fsp->bsize);
  394.     else
  395.         n_used = CEIL_DIV(sbufp->st_blocks*STBSIZE(fsp), Report_blksize);
  396.  
  397. #else /*}!USE_STBLOCKS{*/
  398.  
  399.     register long    n_to_place;    /* num data blocks to be placed        */
  400.     long    n_single_ind;    /* scratch single indirect block cntr    */
  401.     long    n_double_ind;    /* scratch double indirect block cntr    */
  402.  
  403.     /*
  404.      * Ignore this entry if we are counting usage for a specific
  405.      * user and that user doesn't own this entry.
  406.      */
  407.     if (Selected_user >= 0 && sbufp->st_uid != Selected_user)
  408.         return 0L;
  409.  
  410.     /*
  411.      * Determine the number of data blocks required to store this file.
  412.      */
  413.     n_used = CEIL_DIV(sbufp->st_size, fsp->bsize);
  414.  
  415.     /*
  416.      * The first DIRBLKS addresses are stored directly in the inode
  417.      * and thus require no additional overhead.  Figure out how many
  418.      * data blocks remain to be placed through indirect addresses.
  419.      */
  420.     n_to_place = n_used - DIRBLKS;
  421.  
  422.     /*
  423.      * If the file has DIRBLKS or less data blocks then the entire
  424.      * file can be stored in direct data blocks, there is no indirect
  425.      * block overhead, and thus we are done.
  426.      */
  427.     if (n_to_place <= 0)
  428.         goto done;
  429.  
  430.     /*
  431.      * With the single indirect block, we can get another "nindir" blocks.
  432.      */
  433.     ++n_used;
  434.     n_to_place -= fsp->nindir;
  435.     if (n_to_place <= 0)
  436.         goto done;
  437.  
  438.     /*
  439.      * With the double indirect block, we can get another "nindir" single
  440.      * indirect blocks, for a total of another "nindir**2" data blocks.
  441.      */
  442.     n_single_ind = CEIL_DIV(n_to_place, fsp->nindir);
  443.     if (n_single_ind > fsp->nindir)
  444.         n_single_ind = fsp->nindir;
  445.     n_used += 1 + n_single_ind;
  446.     n_to_place -= n_single_ind * fsp->nindir ;
  447.     if (n_to_place <= 0)
  448.         goto done;
  449.  
  450.     /*
  451.      * With the triple indirect block, we can get another "nindir" double
  452.      * indirect blocks, for another "nindir**2" single indirect blocks, for
  453.      * a total of another "nindir**3" data blocks.
  454.      */
  455.     n_single_ind = CEIL_DIV(n_to_place, fsp->nindir);
  456.     n_double_ind = CEIL_DIV(n_single_ind, fsp->nindir);
  457.     n_used += 1 + n_double_ind + n_single_ind;
  458.  
  459. done:
  460.  
  461.     /*
  462.      * If required, convert from native blocksize to reporting blocksize.
  463.      */
  464.     if (Report_blksize != 0 && Report_blksize != fsp->bsize)
  465.         n_used = CEIL_DIV(n_used*fsp->bsize, Report_blksize);
  466.  
  467. #endif /*}!USE_STBLOCKS*/
  468.  
  469.     return n_used;
  470.  
  471. }
  472.  
  473.  
  474. /*****************************************************************************
  475.  *
  476.  * Mount Table Access Routines
  477.  *
  478.  * int open_mnttab();
  479.  *        Initialize for mount table scan.  Return zero on success.
  480.  *
  481.  * struct mount_struct *read_mnttab();
  482.  *        Return information on next mount entry; NULL at end of table.
  483.  *
  484.  * int close_mnttab();
  485.  *        Close the mount table.  Return zero on success, nonzero on error.
  486.  *
  487.  ****************************************************************************/
  488.  
  489.  
  490. #ifdef USE_MOUNT_MNTTAB /*{*/
  491.  
  492. #ifndef MNTTAB
  493. #    ifdef PNMNTTAB
  494. #        define MNTTAB PNMNTTAB
  495. #    else
  496. #        define MNTTAB "/etc/mnttab"
  497. #    endif
  498. #endif
  499.  
  500. #ifndef ISMNTFREE
  501. #    define ISMNTFREE(mp)    ((mp)->mt_dev[0] == '\0')
  502. #endif
  503.  
  504. static FILE *fpmnt;
  505.  
  506. int open_mnttab()
  507. {
  508.     return ((fpmnt = fopen(MNTTAB, "r")) != NULL ? 0 : -1);
  509. }
  510.  
  511. struct mnttab *read_mnttab()
  512. {
  513.     static struct mnttab mbuf;
  514.     while (fread((PTRTYPE *)&mbuf, sizeof(mbuf), 1, fpmnt) == 1) {
  515.         if (!ISMNTFREE(&mbuf))
  516.             return &mbuf;
  517.     }
  518.     return (struct mnttab *)NULL;
  519. }
  520.  
  521. int close_mnttab()
  522. {
  523.     return fclose(fpmnt);
  524. }
  525.  
  526. #endif /*} USE_MOUNT_MNTTAB*/
  527.  
  528.  
  529. /****************************************************************************/
  530.  
  531.  
  532. #ifdef USE_MOUNT_R4MNTTAB /*{*/
  533.  
  534. #ifndef MNTTAB
  535. #    define MNTTAB "/etc/mnttab"
  536. #endif
  537.  
  538. static FILE *fpmnt;
  539.  
  540. int open_mnttab()
  541. {
  542.     return ((fpmnt = fopen(MNTTAB, "r")) != NULL ? 0 : -1);
  543. }
  544.  
  545. struct mnttab *read_mnttab()
  546. {
  547.     static struct mnttab mbuf;
  548.     mntnull(&mbuf);
  549.     return (getmntent(fpmnt, &mbuf) == 0 ? &mbuf : (struct mnttab *)NULL);
  550. }
  551.  
  552. int close_mnttab()
  553. {
  554.     return fclose(fpmnt);
  555. }
  556.  
  557. #endif /*} USE_MOUNT_R4MNTTAB*/
  558.  
  559.  
  560. /****************************************************************************/
  561.  
  562.  
  563. #ifdef USE_MOUNT_MNTENT /*{*/
  564.  
  565. #ifndef MNT_MNTTAB
  566. #    define MNT_MNTTAB "/etc/mtab"
  567. #endif
  568.  
  569. static FILE *fpmnt;
  570.  
  571. int open_mnttab()
  572. {
  573.     return ((fpmnt = setmntent(MNT_MNTTAB, "r")) != NULL ? 0 : -1);
  574. }
  575.  
  576. struct mntent *read_mnttab()
  577. {
  578.     return getmntent(fpmnt);
  579. }
  580.  
  581. int close_mnttab()
  582. {
  583.     return endmntent(fpmnt);
  584. }
  585.  
  586. #endif /*}USE_MOUNT_MNTENT*/
  587.  
  588.  
  589. /****************************************************************************/
  590.  
  591.  
  592. #ifdef USE_MOUNT_FSTAB /*{*/
  593.  
  594. int open_mnttab()
  595. {
  596.     return (setfsent() ? 0 : -1);
  597. }
  598.  
  599. struct fstab *read_mnttab()
  600. {
  601.     return getfsent();
  602. }
  603.  
  604. int close_mnttab()
  605. {
  606.     endfsent();
  607.     return 0;
  608. }
  609.  
  610. #endif /*}USE_MOUNT_FSTAB*/
  611.  
  612.  
  613. /****************************************************************************/
  614.  
  615.  
  616. #ifdef USE_MOUNT_MNTCTL /*{*/
  617.  
  618. /*
  619.  * Here is a fine example of what happens when you have ivory tower
  620.  * doctorates instead of experienced system programmers designing stuff.
  621.  */
  622.  
  623. static char *mntctl_buf;
  624. static char *mntctl_entry;
  625. static int mntctl_fscount;
  626.  
  627. int open_mnttab()
  628. {
  629.     unsigned n;
  630.  
  631.     if (mntctl(MCTL_QUERY, sizeof(n), (char *)&n) != 0)
  632.         return -1;
  633.     n += 1024;
  634.     mntctl_buf = xmalloc(n);
  635.     if ((mntctl_fscount = mntctl(MCTL_QUERY, n, mntctl_buf)) == -1)
  636.         return -1;
  637.     mntctl_entry = mntctl_buf;
  638.     return 0;
  639. }
  640.  
  641. struct mount_struct *read_mnttab()
  642. {
  643.     struct vmount *v;
  644.     unsigned n, m;
  645.     static struct mount_struct mbuf = { NULL, NULL };
  646.  
  647.     if (mntctl_fscount <= 0)
  648.         return (struct mount_struct *) NULL;
  649.  
  650.     v = (struct vmount *)mntctl_entry;
  651.     mntctl_entry += v->vmt_length;
  652.     --mntctl_fscount;
  653.  
  654.     if (mbuf.mount_device != NULL)
  655.         free(mbuf.mount_device);
  656.     if (mbuf.mount_point != NULL)
  657.         free(mbuf.mount_point);
  658.  
  659.     n = vmt2datasize(v, VMT_STUB);
  660.     mbuf.mount_point = xmalloc(n+1);
  661.     (void) strncpy(mbuf.mount_point, vmt2dataptr(v, VMT_STUB), n);
  662.     mbuf.mount_point[n] = '\0';
  663.  
  664.     n = vmt2datasize(v, VMT_HOST);
  665.     m = vmt2datasize(v, VMT_OBJECT);
  666.     mbuf.mount_device = xmalloc(n+sizeof((char)':')+m+1);
  667.     if (v->vmt_flags & MNT_REMOTE) {
  668.         (void) strncpy(mbuf.mount_device, vmt2dataptr(v, VMT_HOST), n);
  669.         mbuf.mount_device[n] = '\0';
  670.         n = strlen(mbuf.mount_device);
  671.         mbuf.mount_device[n++] = ':';
  672.     } else {
  673.         n = 0;
  674.     }
  675.     (void) strncpy(mbuf.mount_device+n, vmt2dataptr(v, VMT_OBJECT), m);
  676.     mbuf.mount_device[n+m] = '\0';
  677.  
  678.     return &mbuf;
  679. }
  680.  
  681. int close_mnttab()
  682. {
  683.     free((PTRTYPE *)mntctl_buf);
  684.     return 0;
  685. }
  686.  
  687. #endif /*} USE_MOUNT_MNTCTL*/
  688.  
  689.