lib/replace/repdir_getdirentries.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
00026   systems using getdirentries
00027 
00028   This is needed because the existing directory handling in FreeBSD
00029   and OpenBSD (and possibly NetBSD) doesn't correctly handle unlink()
00030   on files in a directory where telldir() has been used. On a block
00031   boundary it will occasionally miss a file when seekdir() is used to
00032   return to a position previously recorded with telldir().
00033 
00034   This also fixes a severe performance and memory usage problem with
00035   telldir() on BSD systems. Each call to telldir() in BSD adds an
00036   entry to a linked list, and those entries are cleaned up on
00037   closedir(). This means with a large directory closedir() can take an
00038   arbitrary amount of time, causing network timeouts as millions of
00039   telldir() entries are freed
00040 
00041   Note! This replacement code is not portable. It relies on
00042   getdirentries() always leaving the file descriptor at a seek offset
00043   that is a multiple of DIR_BUF_SIZE. If the code detects that this
00044   doesn't happen then it will abort(). It also does not handle
00045   directories with offsets larger than can be stored in a long,
00046 
00047   This code is available under other free software licenses as
00048   well. Contact the author.
00049 */
00050 
00051 #include "replace.h"
00052 #include <stdlib.h>
00053 #include <sys/stat.h>
00054 #include <unistd.h>
00055 #include <sys/types.h>
00056 #include <errno.h>
00057 #include <fcntl.h>
00058 #include <dirent.h>
00059 
00060 #define DIR_BUF_BITS 9
00061 #define DIR_BUF_SIZE (1<<DIR_BUF_BITS)
00062 
00063 struct dir_buf {
00064         int fd;
00065         int nbytes, ofs;
00066         off_t seekpos;
00067         char buf[DIR_BUF_SIZE];
00068 };
00069 
00070 DIR *opendir(const char *dname)
00071 {
00072         struct dir_buf *d;
00073         struct stat sb;
00074         d = malloc(sizeof(*d));
00075         if (d == NULL) {
00076                 errno = ENOMEM;
00077                 return NULL;
00078         }
00079         d->fd = open(dname, O_RDONLY);
00080         if (d->fd == -1) {
00081                 free(d);
00082                 return NULL;
00083         }
00084         if (fstat(d->fd, &sb) < 0) {
00085                 close(d->fd);
00086                 free(d);
00087                 return NULL;
00088         }
00089         if (!S_ISDIR(sb.st_mode)) {
00090                 close(d->fd);
00091                 free(d);   
00092                 errno = ENOTDIR;
00093                 return NULL;
00094         }
00095         d->ofs = 0;
00096         d->seekpos = 0;
00097         d->nbytes = 0;
00098         return (DIR *)d;
00099 }
00100 
00101 struct dirent *readdir(DIR *dir)
00102 {
00103         struct dir_buf *d = (struct dir_buf *)dir;
00104         struct dirent *de;
00105 
00106         if (d->ofs >= d->nbytes) {
00107                 long pos;
00108                 d->nbytes = getdirentries(d->fd, d->buf, DIR_BUF_SIZE, &pos);
00109                 d->seekpos = pos;
00110                 d->ofs = 0;
00111         }
00112         if (d->ofs >= d->nbytes) {
00113                 return NULL;
00114         }
00115         de = (struct dirent *)&d->buf[d->ofs];
00116         d->ofs += de->d_reclen;
00117         return de;
00118 }
00119 
00120 #ifdef TELLDIR_TAKES_CONST_DIR
00121 long telldir(const DIR *dir)
00122 #else
00123 long telldir(DIR *dir)
00124 #endif
00125 {
00126         struct dir_buf *d = (struct dir_buf *)dir;
00127         if (d->ofs >= d->nbytes) {
00128                 d->seekpos = lseek(d->fd, 0, SEEK_CUR);
00129                 d->ofs = 0;
00130                 d->nbytes = 0;
00131         }
00132         /* this relies on seekpos always being a multiple of
00133            DIR_BUF_SIZE. Is that always true on BSD systems? */
00134         if (d->seekpos & (DIR_BUF_SIZE-1)) {
00135                 abort();
00136         }
00137         return d->seekpos + d->ofs;
00138 }
00139 
00140 #ifdef SEEKDIR_RETURNS_INT
00141 int seekdir(DIR *dir, long ofs)
00142 #else
00143 void seekdir(DIR *dir, long ofs)
00144 #endif
00145 {
00146         struct dir_buf *d = (struct dir_buf *)dir;
00147         long pos;
00148         d->seekpos = lseek(d->fd, ofs & ~(DIR_BUF_SIZE-1), SEEK_SET);
00149         d->nbytes = getdirentries(d->fd, d->buf, DIR_BUF_SIZE, &pos);
00150         d->ofs = 0;
00151         while (d->ofs < (ofs & (DIR_BUF_SIZE-1))) {
00152                 if (readdir(dir) == NULL) break;
00153         }
00154 #ifdef SEEKDIR_RETURNS_INT
00155         return -1;
00156 #endif
00157 }
00158 
00159 void rewinddir(DIR *dir)
00160 {
00161         seekdir(dir, 0);
00162 }
00163 
00164 int closedir(DIR *dir)
00165 {
00166         struct dir_buf *d = (struct dir_buf *)dir;
00167         int r = close(d->fd);
00168         if (r != 0) {
00169                 return r;
00170         }
00171         free(d);
00172         return 0;
00173 }
00174 
00175 #ifndef dirfd
00176 /* darn, this is a macro on some systems. */
00177 int dirfd(DIR *dir)
00178 {
00179         struct dir_buf *d = (struct dir_buf *)dir;
00180         return d->fd;
00181 }
00182 #endif
00183 
00184 

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