home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / unix / volume9 / gwyn-dir-lib / getdents.c < prev    next >
Encoding:
C/C++ Source or Header  |  1987-04-30  |  6.2 KB  |  243 lines

  1. /*
  2.     getdents -- get directory entries in a file system independent format
  3.             (SVR3 system call emulation)
  4.  
  5.     last edit:    25-Apr-1987    D A Gwyn
  6.  
  7.     This single source file supports several different methods of
  8.     getting directory entries from the operating system.  Define
  9.     whichever one of the following describes your system:
  10.  
  11.     UFS    original UNIX filesystem (14-character name limit)
  12.     BFS    4.2BSD (also 4.3BSD) native filesystem (long names)
  13.     NFS    getdirentries() system call
  14.  
  15.     Also define any of the following that are pertinent:
  16.  
  17.     BRL    BRL UNIX System V emulation environment on 4.nBSD
  18.     UNK    have _getdents() system call, but kernel may not support it
  19.  
  20.     If your C library has a getdents() system call interface, but you
  21.     can't count on all kernels on which your application binaries may
  22.     run to support it, change the system call interface name to
  23.     _getdents() and define "UNK" to enable the system-call validity
  24.     test in this "wrapper" around _getdents().
  25.  
  26.     If your system has a getdents() system call that is guaranteed 
  27.     to always work, you shouldn't be using this source file at all.
  28. */
  29.  
  30. #include    <sys/errno.h>
  31. #include    <sys/types.h>
  32. #ifdef BRL
  33. #include    <sys/_dir.h>        /* BSD flavor, not System V */
  34. #else
  35. #include    <sys/dir.h>
  36. #undef    MAXNAMLEN            /* avoid conflict with SVR3 */
  37.     /* Good thing we don't need to use the DIRSIZ() macro! */
  38. #ifdef d_ino                /* 4.3BSD/NFS using d_fileno */
  39. #undef    d_ino                /* (not absolutely necessary) */
  40. #else
  41. #define    d_fileno    d_ino        /* (struct direct) member */
  42. #endif
  43. #endif
  44. #include    <sys/dirent.h>
  45. #include    <sys/stat.h>
  46. #ifdef UNK
  47. #ifndef UFS
  48. #include "***** ERROR ***** UNK applies only to UFS"
  49. /* One could do something similar for getdirentries(), but I didn't bother. */
  50. #endif
  51. #include    <signal.h>
  52. #endif
  53.  
  54. #if defined(UFS) + defined(BFS) + defined(NFS) != 1    /* sanity check */
  55. #include "***** ERROR ***** exactly one of UFS, BFS, or NFS must be defined"
  56. #endif
  57.  
  58. #ifdef UFS
  59. #define    RecLen( dp )    (sizeof(struct direct))    /* fixed-length entries */
  60. #else    /* BFS || NFS */
  61. #define    RecLen( dp )    ((dp)->d_reclen)    /* variable-length entries */
  62. #endif
  63.  
  64. #ifdef NFS
  65. #ifdef BRL
  66. #define    getdirentries    _getdirentries    /* package hides this system call */
  67. #endif
  68. extern int    getdirentries();
  69. static long    dummy;            /* getdirentries() needs basep */
  70. #define    GetBlock( fd, buf, n )    getdirentries( fd, buf, (unsigned)n, &dummy )
  71. #else    /* UFS || BFS */
  72. #ifdef BRL
  73. #define read    _read            /* avoid emulation overhead */
  74. #endif
  75. extern int    read();
  76. #define    GetBlock( fd, buf, n )    read( fd, buf, (unsigned)n )
  77. #endif
  78.  
  79. #ifdef UNK
  80. extern int    _getdents();        /* actual system call */
  81. #endif
  82.  
  83. extern char    *strncpy();
  84. extern int    fstat(), strlen();
  85. extern off_t    lseek();
  86.  
  87. extern int    errno;
  88.  
  89. #ifndef DIRBLKSIZ
  90. #define    DIRBLKSIZ    4096        /* directory file read buffer size */
  91. #endif
  92.  
  93. #ifndef NULL
  94. #define    NULL    0
  95. #endif
  96.  
  97. #ifndef SEEK_CUR
  98. #define    SEEK_CUR    1
  99. #endif
  100.  
  101. #ifndef S_ISDIR                /* macro to test for directory file */
  102. #define    S_ISDIR( mode )        (((mode) & S_IFMT) == S_IFDIR)
  103. #endif
  104.  
  105. #ifdef UNK
  106. static enum    { maybe, no, yes }    state = maybe;
  107.                     /* does _getdents() work? */
  108.  
  109. /*ARGSUSED*/
  110. static void
  111. sig_catch( sig )
  112.     int    sig;            /* must be SIGSYS */
  113.     {
  114.     state = no;            /* attempted _getdents() faulted */
  115.     }
  116. #endif
  117.  
  118. int
  119. getdents( fildes, buf, nbyte )        /* returns # bytes read;
  120.                        0 on EOF, -1 on error */
  121.     int            fildes;    /* directory file descriptor */
  122.     char            *buf;    /* where to put the (struct dirent)s */
  123.     unsigned        nbyte;    /* size of buf[] */
  124.     {
  125.     int            serrno;    /* entry errno */
  126.     off_t            offset;    /* initial directory file offset */
  127.     struct stat        statb;    /* fstat() info */
  128.     union    {
  129.         char        dblk[DIRBLKSIZ];
  130.                     /* directory file block buffer */
  131.         struct direct    dummy;    /* just for alignment */
  132.         }    u;        /* (avoids having to malloc()) */
  133.     register struct direct    *dp;    /* -> u.dblk[.] */
  134.     register struct dirent    *bp;    /* -> buf[.] */
  135.  
  136. #ifdef UNK
  137.     switch ( state )
  138.         {
  139.         void        (*shdlr)();    /* entry SIGSYS handler */
  140.         register int    retval;    /* return from _getdents() if any */
  141.  
  142.     case yes:            /* _getdents() is known to work */
  143.         return _getdents( fildes, buf, nbyte );
  144.  
  145.     case maybe:            /* first time only */
  146.         shdlr = signal( SIGSYS, sig_catch );
  147.         retval = _getdents( fildes, buf, nbyte );    /* try it */
  148.         (void)signal( SIGSYS, shdlr );
  149.  
  150.         if ( state == maybe )    /* SIGSYS did not occur */
  151.             {
  152.             state = yes;    /* so _getdents() must have worked */
  153.             return retval;
  154.             }
  155.         /* else fall through into emulation */
  156.  
  157. /*    case no:    /* fall through into emulation */
  158.         }
  159. #endif
  160.  
  161.     if ( buf == NULL || (unsigned long)buf % sizeof(long) != 0 /* ugh */ )
  162.         {
  163.         errno = EFAULT;        /* invalid pointer */
  164.         return -1;
  165.         }
  166.  
  167.     if ( fstat( fildes, &statb ) != 0 )
  168.         return -1;        /* errno set by fstat() */
  169.  
  170.     if ( !S_ISDIR( statb.st_mode ) )
  171.         {
  172.         errno = ENOTDIR;    /* not a directory */
  173.         return -1;
  174.         }
  175.  
  176.     if ( (offset = lseek( fildes, (off_t)0, SEEK_CUR )) < 0 )
  177.         return -1;        /* errno set by lseek() */
  178.  
  179. #ifdef BFS                /* no telling what remote hosts do */
  180.     if ( (unsigned long)offset % DIRBLKSIZ != 0 )
  181.         {
  182.         errno = ENOENT;        /* file pointer probably misaligned */
  183.         return -1;
  184.         }
  185. #endif
  186.  
  187.     serrno = errno;            /* save entry errno */
  188.  
  189.     for ( bp = (struct dirent *)buf; bp == (struct dirent *)buf; )
  190.         {            /* convert next directory block */
  191.         int    size;
  192.  
  193.         do    size = GetBlock( fildes, u.dblk, DIRBLKSIZ );
  194.         while ( size == -1 && errno == EINTR );
  195.  
  196.         if ( size <= 0 )
  197.             return size;    /* EOF or error (EBADF) */
  198.  
  199.         for ( dp = (struct direct *)u.dblk;
  200.               (char *)dp < &u.dblk[size];
  201.               dp = (struct direct *)((char *)dp + RecLen( dp ))
  202.             )    {
  203. #ifndef UFS
  204.             if ( dp->d_reclen <= 0 )
  205.                 {
  206.                 errno = EIO;    /* corrupted directory */
  207.                 return -1;
  208.                 }
  209. #endif
  210.  
  211.             if ( dp->d_fileno != 0 )
  212.                 {    /* non-empty; copy to user buffer */
  213.                 register int    reclen =
  214.                     DIRENTSIZ( strlen( dp->d_name ) );
  215.  
  216.                 if ( (char *)bp + reclen > &buf[nbyte] )
  217.                     {
  218.                     errno = EINVAL;
  219.                     return -1;    /* buf too small */
  220.                     }
  221.  
  222.                 bp->d_ino = dp->d_fileno;
  223.                 bp->d_off = offset + ((char *)dp - u.dblk);
  224.                 bp->d_reclen = reclen;
  225.                 (void)strncpy( bp->d_name, dp->d_name,
  226.                            reclen - DIRENTBASESIZ
  227.                          );    /* adds NUL padding */
  228.  
  229.                 bp = (struct dirent *)((char *)bp + reclen);
  230.                 }
  231.             }
  232.  
  233.         if ( (char *)dp > &u.dblk[size] )
  234.             {
  235.             errno = EIO;    /* corrupted directory */
  236.             return -1;
  237.             }
  238.         }
  239.  
  240.     errno = serrno;            /* restore entry errno */
  241.     return (char *)bp - buf;    /* return # bytes read */
  242.     }
  243.