lib/replace/repdir_getdents.c

説明を見る。
00001 /* 
00002    Unix SMB/CIFS implementation.
00003 
00004    Copyright (C) Andrew Tridgell 2005
00005 
00006      ** NOTE! The following LGPL license applies to the replace
00007      ** library. This does NOT imply that all of Samba is released
00008      ** under the LGPL
00009    
00010    This library is free software; you can redistribute it and/or
00011    modify it under the terms of the GNU Lesser General Public
00012    License as published by the Free Software Foundation; either
00013    version 2 of the License, or (at your option) any later version.
00014 
00015    This library is distributed in the hope that it will be useful,
00016    but WITHOUT ANY WARRANTY; without even the implied warranty of
00017    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00018    Lesser General Public License for more details.
00019 
00020    You should have received a copy of the GNU Lesser General Public
00021    License along with this library; if not, write to the Free Software
00022    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00023 */
00024 /*
00025   a replacement for opendir/readdir/telldir/seekdir/closedir for BSD systems
00026 
00027   This is needed because the existing directory handling in FreeBSD
00028   and OpenBSD (and possibly NetBSD) doesn't correctly handle unlink()
00029   on files in a directory where telldir() has been used. On a block
00030   boundary it will occasionally miss a file when seekdir() is used to
00031   return to a position previously recorded with telldir().
00032 
00033   This also fixes a severe performance and memory usage problem with
00034   telldir() on BSD systems. Each call to telldir() in BSD adds an
00035   entry to a linked list, and those entries are cleaned up on
00036   closedir(). This means with a large directory closedir() can take an
00037   arbitrary amount of time, causing network timeouts as millions of
00038   telldir() entries are freed
00039 
00040   Note! This replacement code is not portable. It relies on getdents()
00041   always leaving the file descriptor at a seek offset that is a
00042   multiple of DIR_BUF_SIZE. If the code detects that this doesn't
00043   happen then it will abort(). It also does not handle directories
00044   with offsets larger than can be stored in a long,
00045 
00046   This code is available under other free software licenses as
00047   well. Contact the author.
00048 */
00049 
00050 #include <stdlib.h>
00051 #include <sys/stat.h>
00052 #include <unistd.h>
00053 #include <sys/types.h>
00054 #include <errno.h>
00055 #include <fcntl.h>
00056 #include <dirent.h>
00057 
00058 #define DIR_BUF_BITS 9
00059 #define DIR_BUF_SIZE (1<<DIR_BUF_BITS)
00060 
00061 struct dir_buf {
00062         int fd;
00063         int nbytes, ofs;
00064         off_t seekpos;
00065         char buf[DIR_BUF_SIZE];
00066 };
00067 
00068 DIR *opendir(const char *dname)
00069 {
00070         struct dir_buf *d;
00071         struct stat sb;
00072         d = malloc(sizeof(*d));
00073         if (d == NULL) {
00074                 errno = ENOMEM;
00075                 return NULL;
00076         }
00077         d->fd = open(dname, O_RDONLY);
00078         if (d->fd == -1) {
00079                 free(d);
00080                 return NULL;
00081         }
00082         if (fstat(d->fd, &sb) < 0) {
00083                 close(d->fd);
00084                 free(d);
00085                 return NULL;
00086         }
00087         if (!S_ISDIR(sb.st_mode)) {
00088                 close(d->fd);
00089                 free(d);   
00090                 errno = ENOTDIR;
00091                 return NULL;
00092         }
00093         d->ofs = 0;
00094         d->seekpos = 0;
00095         d->nbytes = 0;
00096         return (DIR *)d;
00097 }
00098 
00099 struct dirent *readdir(DIR *dir)
00100 {
00101         struct dir_buf *d = (struct dir_buf *)dir;
00102         struct dirent *de;
00103 
00104         if (d->ofs >= d->nbytes) {
00105                 d->seekpos = lseek(d->fd, 0, SEEK_CUR);
00106                 d->nbytes = getdents(d->fd, d->buf, DIR_BUF_SIZE);
00107                 d->ofs = 0;
00108         }
00109         if (d->ofs >= d->nbytes) {
00110                 return NULL;
00111         }
00112         de = (struct dirent *)&d->buf[d->ofs];
00113         d->ofs += de->d_reclen;
00114         return de;
00115 }
00116 
00117 long telldir(DIR *dir)
00118 {
00119         struct dir_buf *d = (struct dir_buf *)dir;
00120         if (d->ofs >= d->nbytes) {
00121                 d->seekpos = lseek(d->fd, 0, SEEK_CUR);
00122                 d->ofs = 0;
00123                 d->nbytes = 0;
00124         }
00125         /* this relies on seekpos always being a multiple of
00126            DIR_BUF_SIZE. Is that always true on BSD systems? */
00127         if (d->seekpos & (DIR_BUF_SIZE-1)) {
00128                 abort();
00129         }
00130         return d->seekpos + d->ofs;
00131 }
00132 
00133 void seekdir(DIR *dir, long ofs)
00134 {
00135         struct dir_buf *d = (struct dir_buf *)dir;
00136         d->seekpos = lseek(d->fd, ofs & ~(DIR_BUF_SIZE-1), SEEK_SET);
00137         d->nbytes = getdents(d->fd, d->buf, DIR_BUF_SIZE);
00138         d->ofs = 0;
00139         while (d->ofs < (ofs & (DIR_BUF_SIZE-1))) {
00140                 if (readdir(dir) == NULL) break;
00141         }
00142 }
00143 
00144 void rewinddir(DIR *dir)
00145 {
00146         seekdir(dir, 0);
00147 }
00148 
00149 int closedir(DIR *dir)
00150 {
00151         struct dir_buf *d = (struct dir_buf *)dir;
00152         int r = close(d->fd);
00153         if (r != 0) {
00154                 return r;
00155         }
00156         free(d);
00157         return 0;
00158 }
00159 
00160 #ifndef dirfd
00161 /* darn, this is a macro on some systems. */
00162 int dirfd(DIR *dir)
00163 {
00164         struct dir_buf *d = (struct dir_buf *)dir;
00165         return d->fd;
00166 }
00167 #endif

Sambaに対してSat Aug 29 21:22:58 2009に生成されました。  doxygen 1.4.7