fdwalk should return 0 on an empty directory
[kopensolaris-gnu/glibc.git] / sysdeps / unix / readdir_r.c
1 /* Copyright (C) 1991,92,93,94,95,96,97,98,99,2000,02
2         Free Software Foundation, Inc.
3    This file is part of the GNU C Library.
4
5    The GNU C Library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Lesser General Public
7    License as published by the Free Software Foundation; either
8    version 2.1 of the License, or (at your option) any later version.
9
10    The GNU C Library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Lesser General Public License for more details.
14
15    You should have received a copy of the GNU Lesser General Public
16    License along with the GNU C Library; if not, write to the Free
17    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
18    02111-1307 USA.  */
19
20 #include <errno.h>
21 #include <limits.h>
22 #include <stddef.h>
23 #include <string.h>
24 #include <dirent.h>
25 #include <unistd.h>
26 #include <sys/types.h>
27 #include <assert.h>
28
29 #include <dirstream.h>
30
31 #ifndef __READDIR_R
32 # define __READDIR_R __readdir_r
33 # define __GETDENTS __getdents
34 # define DIRENT_TYPE struct dirent
35 # define __READDIR_R_ALIAS
36 #endif
37
38 /* Read a directory entry from DIRP.  */
39 int
40 __READDIR_R (DIR *dirp, DIRENT_TYPE *entry, DIRENT_TYPE **result)
41 {
42   DIRENT_TYPE *dp;
43   size_t reclen;
44   const int saved_errno = errno;
45
46   __libc_lock_lock (dirp->lock);
47
48   do
49     {
50       if (dirp->offset >= dirp->size)
51         {
52           /* We've emptied out our buffer.  Refill it.  */
53
54           size_t maxread;
55           ssize_t bytes;
56
57 #ifndef _DIRENT_HAVE_D_RECLEN
58           /* Fixed-size struct; must read one at a time (see below).  */
59           maxread = sizeof *dp;
60 #else
61           maxread = dirp->allocation;
62 #endif
63
64           bytes = __GETDENTS (dirp->fd, dirp->data, maxread);
65           if (bytes <= 0)
66             {
67               /* On some systems getdents fails with ENOENT when the
68                  open directory has been rmdir'd already.  POSIX.1
69                  requires that we treat this condition like normal EOF.  */
70               if (bytes < 0 && errno == ENOENT)
71                 {
72                   bytes = 0;
73                   __set_errno (saved_errno);
74                 }
75
76               dp = NULL;
77               /* Reclen != 0 signals that an error occurred.  */
78               reclen = bytes != 0;
79               break;
80             }
81           dirp->size = (size_t) bytes;
82
83           /* Reset the offset into the buffer.  */
84           dirp->offset = 0;
85         }
86
87       dp = (DIRENT_TYPE *) &dirp->data[dirp->offset];
88
89 #ifdef _DIRENT_HAVE_D_RECLEN
90       reclen = dp->d_reclen;
91 #else
92       /* The only version of `struct dirent*' that lacks `d_reclen'
93          is fixed-size.  */
94       assert (sizeof dp->d_name > 1);
95       reclen = sizeof *dp;
96       /* The name is not terminated if it is the largest possible size.
97          Clobber the following byte to ensure proper null termination.  We
98          read just one entry at a time above so we know that byte will not
99          be used later.  */
100       dp->d_name[sizeof dp->d_name] = '\0';
101 #endif
102
103       dirp->offset += reclen;
104
105 #ifdef _DIRENT_HAVE_D_OFF
106       dirp->filepos = dp->d_off;
107 #else
108       dirp->filepos += reclen;
109 #endif
110
111       /* Skip deleted files.  */
112     }
113   while (dp->d_ino == 0);
114
115   if (dp != NULL)
116     *result = memcpy (entry, dp, reclen);
117   else
118     *result = NULL;
119
120   __libc_lock_unlock (dirp->lock);
121
122   return dp != NULL ? 0 : reclen ? errno : 0;
123 }
124
125 #ifdef __READDIR_R_ALIAS
126 weak_alias (__readdir_r, readdir_r)
127 #endif