Back to index

glibc  2.9
getdents.c
Go to the documentation of this file.
00001 /* Copyright (C) 1993,95,96,97,98, 2004 Free Software Foundation, Inc.
00002    This file is part of the GNU C Library.
00003 
00004    The GNU C Library is free software; you can redistribute it and/or
00005    modify it under the terms of the GNU Lesser General Public
00006    License as published by the Free Software Foundation; either
00007    version 2.1 of the License, or (at your option) any later version.
00008 
00009    The GNU C Library is distributed in the hope that it will be useful,
00010    but WITHOUT ANY WARRANTY; without even the implied warranty of
00011    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012    Lesser General Public License for more details.
00013 
00014    You should have received a copy of the GNU Lesser General Public
00015    License along with the GNU C Library; if not, write to the Free
00016    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
00017    02111-1307 USA.  */
00018 
00019 #include <alloca.h>
00020 #include <dirent.h>
00021 #include <stddef.h>
00022 #include <string.h>
00023 #include <unistd.h>
00024 #include <sys/param.h>
00025 #include <sys/types.h>
00026 
00027 #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
00028 
00029 
00030 extern int __getdents (int fd, char *buf, size_t nbytes);
00031 
00032 /* For Solaris we need a special version of this file since the
00033    definition of `struct dirent' is not the same for the kernel and
00034    the libc.  There is one additional field which might be introduced
00035    in the kernel structure in the future.
00036 
00037    He is the system definition of `struct dirent' as of 2.6:  */
00038 
00039 struct kernel_dirent
00040   {
00041     ino_t d_ino;
00042     off_t d_off;
00043     unsigned short int d_reclen;
00044     char d_name[256];
00045   };
00046 
00047 #ifdef GETDENTS64
00048 #define __getdirentries __getdirentries64
00049 #define dirent dirent64
00050 #endif
00051 
00052 /* The problem here is that we cannot simply read the next NBYTES
00053    bytes.  We need to take the additional field into account.  We use
00054    some heuristic.  Assuming the directory contains names with 14
00055    characters on average we can compute an estimate number of entries
00056    which fit in the buffer.  Taking this number allows us to specify a
00057    correct number of bytes to read.  If we should be wrong, we can reset
00058    the file descriptor.  */
00059 ssize_t
00060 __getdirentries (int fd, char *buf, size_t nbytes, off_t *basep)
00061 {
00062   off_t base = __lseek (fd, (off_t) 0, SEEK_CUR);
00063   off_t last_offset = base;
00064   size_t red_nbytes;
00065   struct kernel_dirent *skdp, *kdp;
00066   struct dirent *dp;
00067   int retval;
00068   const size_t size_diff = (offsetof (struct dirent, d_name)
00069                          - offsetof (struct kernel_dirent, d_name));
00070 
00071   red_nbytes = nbytes - ((nbytes / (offsetof (struct dirent, d_name) + 14))
00072                       * size_diff);
00073 
00074   dp = (struct dirent *) buf;
00075   skdp = kdp = __alloca (red_nbytes);
00076 
00077   retval = __getdents (fd, (char *) kdp, red_nbytes);
00078 
00079   while ((char *) kdp < (char *) skdp + retval)
00080     {
00081       const size_t alignment = __alignof__ (struct dirent);
00082       /* Since kdp->d_reclen is already aligned for the kernel structure
00083         this may compute a value that is bigger than necessary.  */
00084       size_t new_reclen = ((kdp->d_reclen + size_diff + alignment - 1)
00085                         & ~(alignment - 1));
00086       if ((char *) dp + new_reclen > buf + nbytes)
00087        {
00088          /* Our heuristic failed.  We read too many entries.  Reset
00089             the stream.  */
00090          __lseek (fd, last_offset, SEEK_SET);
00091          break;
00092        }
00093 
00094       last_offset = kdp->d_off;
00095       dp->d_ino = kdp->d_ino;
00096       dp->d_off = kdp->d_off;
00097       dp->d_reclen = new_reclen;
00098       dp->d_type = DT_UNKNOWN;
00099       memcpy (dp->d_name, kdp->d_name,
00100              kdp->d_reclen - offsetof (struct kernel_dirent, d_name));
00101 
00102       dp = (struct dirent *) ((char *) dp + new_reclen);
00103       kdp = (struct kernel_dirent *) (((char *) kdp) + kdp->d_reclen);
00104     }
00105 
00106   if (basep)
00107     *basep = base;
00108 
00109   return (char *) dp - buf;
00110 }
00111 
00112 #ifndef GETDENTS64
00113 weak_alias (__getdirentries, getdirentries)
00114 #endif