lib/repdir.c

説明を見る。
00001 /* 
00002    Unix SMB/CIFS implementation.
00003 
00004    Copyright (C) Andrew Tridgell 2005
00005    Updated for Samba3 64-bit cleanliness (C) Jeremy Allison 2006
00006    
00007    This program is free software; you can redistribute it and/or modify
00008    it under the terms of the GNU General Public License as published by
00009    the Free Software Foundation; either version 2 of the License, or
00010    (at your option) any later version.
00011    
00012    This program is distributed in the hope that it will be useful,
00013    but WITHOUT ANY WARRANTY; without even the implied warranty of
00014    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015    GNU General Public License for more details.
00016    
00017    You should have received a copy of the GNU General Public License
00018    along with this program; if not, write to the Free Software
00019    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
00020 */
00021 /*
00022   a replacement for opendir/readdir/telldir/seekdir/closedir for BSD systems
00023 
00024   This is needed because the existing directory handling in FreeBSD
00025   and OpenBSD (and possibly NetBSD) doesn't correctly handle unlink()
00026   on files in a directory where telldir() has been used. On a block
00027   boundary it will occasionally miss a file when seekdir() is used to
00028   return to a position previously recorded with telldir().
00029 
00030   This also fixes a severe performance and memory usage problem with
00031   telldir() on BSD systems. Each call to telldir() in BSD adds an
00032   entry to a linked list, and those entries are cleaned up on
00033   closedir(). This means with a large directory closedir() can take an
00034   arbitrary amount of time, causing network timeouts as millions of
00035   telldir() entries are freed
00036 
00037   Note! This replacement code is not portable. It relies on getdents()
00038   always leaving the file descriptor at a seek offset that is a
00039   multiple of DIR_BUF_SIZE. If the code detects that this doesn't
00040   happen then it will abort(). It also does not handle directories
00041   with offsets larger than can be stored in a long,
00042 
00043   This code is available under other free software licenses as
00044   well. Contact the author.
00045 */
00046 
00047 #include <include/includes.h>
00048 
00049  void replace_readdir_dummy(void);
00050  void replace_readdir_dummy(void) {}
00051 
00052 #if defined(REPLACE_READDIR)
00053 
00054 #if defined(PARANOID_MALLOC_CHECKER)
00055 #ifdef malloc
00056 #undef malloc
00057 #endif
00058 #endif
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         SMB_OFF_T seekpos;
00067         char buf[DIR_BUF_SIZE];
00068 };
00069 
00070 #if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_OPENDIR64)
00071  SMB_STRUCT_DIR *opendir64(const char *dname)
00072 #else
00073  SMB_STRUCT_DIR *opendir(const char *dname)
00074 #endif
00075 {
00076         struct dir_buf *d;
00077         d = malloc(sizeof(*d));
00078         if (d == NULL) {
00079                 errno = ENOMEM;
00080                 return NULL;
00081         }
00082 #if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_OPEN64)
00083         d->fd = open64(dname, O_RDONLY);
00084 #else
00085         d->fd = open(dname, O_RDONLY);
00086 #endif
00087 
00088         if (d->fd == -1) {
00089                 free(d);
00090                 return NULL;
00091         }
00092         d->ofs = 0;
00093         d->seekpos = 0;
00094         d->nbytes = 0;
00095         return (SMB_STRUCT_DIR *)d;
00096 }
00097 
00098 #if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_READDIR64)
00099  SMB_STRUCT_DIRENT *readdir64(SMB_STRUCT_DIR *dir)
00100 #else
00101  SMB_STRUCT_DIRENT *readdir(SMB_STRUCT_DIR *dir)
00102 #endif
00103 {
00104         struct dir_buf *d = (struct dir_buf *)dir;
00105         SMB_STRUCT_DIRENT *de;
00106 
00107         if (d->ofs >= d->nbytes) {
00108 #if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_LSEEK64)
00109                 d->seekpos = lseek64(d->fd, 0, SEEK_CUR);
00110 #else
00111                 d->seekpos = lseek(d->fd, 0, SEEK_CUR);
00112 #endif
00113 
00114 #if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_GETDENTS64)
00115                 d->nbytes = getdents64(d->fd, d->buf, DIR_BUF_SIZE);
00116 #else
00117                 d->nbytes = getdents(d->fd, d->buf, DIR_BUF_SIZE);
00118 #endif
00119                 d->ofs = 0;
00120         }
00121         if (d->ofs >= d->nbytes) {
00122                 return NULL;
00123         }
00124         de = (SMB_STRUCT_DIRENT *)&d->buf[d->ofs];
00125         d->ofs += de->d_reclen;
00126         return de;
00127 }
00128 
00129 #if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_TELLDIR64)
00130  long telldir64(SMB_STRUCT_DIR *dir)
00131 #else
00132  long telldir(SMB_STRUCT_DIR *dir)
00133 #endif
00134 {
00135         struct dir_buf *d = (struct dir_buf *)dir;
00136         if (d->ofs >= d->nbytes) {
00137 #if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_LSEEK64)
00138                 d->seekpos = lseek64(d->fd, 0, SEEK_CUR);
00139 #else
00140                 d->seekpos = lseek(d->fd, 0, SEEK_CUR);
00141 #endif
00142                 d->ofs = 0;
00143                 d->nbytes = 0;
00144         }
00145         /* this relies on seekpos always being a multiple of
00146            DIR_BUF_SIZE. Is that always true on BSD systems? */
00147         if (d->seekpos & (DIR_BUF_SIZE-1)) {
00148                 abort();
00149         }
00150         return d->seekpos + d->ofs;
00151 }
00152 
00153 #if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_SEEKDIR64)
00154  void seekdir64(SMB_STRUCT_DIR *dir, long ofs)
00155 #else
00156  void seekdir(SMB_STRUCT_DIR *dir, long ofs)
00157 #endif
00158 {
00159         struct dir_buf *d = (struct dir_buf *)dir;
00160 #if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_LSEEK64)
00161         d->seekpos = lseek64(d->fd, ofs & ~(DIR_BUF_SIZE-1), SEEK_SET);
00162 #else
00163         d->seekpos = lseek(d->fd, ofs & ~(DIR_BUF_SIZE-1), SEEK_SET);
00164 #endif
00165 
00166 #if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_GETDENTS64)
00167         d->nbytes = getdents64(d->fd, d->buf, DIR_BUF_SIZE);
00168 #else
00169         d->nbytes = getdents(d->fd, d->buf, DIR_BUF_SIZE);
00170 #endif
00171 
00172         d->ofs = 0;
00173         while (d->ofs < (ofs & (DIR_BUF_SIZE-1))) {
00174 #if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_READDIR64)
00175                 if (readdir64(dir) == NULL) break;
00176 #else
00177                 if (readdir(dir) == NULL) break;
00178 #endif
00179         }
00180 }
00181 
00182 #if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_REWINDDIR64)
00183  void rewinddir64(SMB_STRUCT_DIR *dir)
00184 #else
00185  void rewinddir(SMB_STRUCT_DIR *dir)
00186 #endif
00187 {
00188 #if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_SEEKDIR64)
00189         seekdir64(dir, 0);
00190 #else
00191         seekdir(dir, 0);
00192 #endif
00193 }
00194 
00195 #if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_CLOSEDIR64)
00196  int closedir64(SMB_STRUCT_DIR *dir)
00197 #else
00198  int closedir(SMB_STRUCT_DIR *dir)
00199 #endif
00200 {
00201         struct dir_buf *d = (struct dir_buf *)dir;
00202         int r = close(d->fd);
00203         if (r != 0) {
00204                 return r;
00205         }
00206         free(d);
00207         return 0;
00208 }
00209 
00210 #ifndef dirfd
00211 /* darn, this is a macro on some systems. */
00212  int dirfd(SMB_STRUCT_DIR *dir)
00213 {
00214         struct dir_buf *d = (struct dir_buf *)dir;
00215         return d->fd;
00216 }
00217 #endif
00218 #endif /* REPLACE_READDIR */

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