4 * (C) Copyright 2003, 2004, 2008 by Theodore Ts'o.
6 * 2008-06-08 Modified by Ross Boylan <RossBoylan stanfordalumni org>
7 * Added support for readdir_r and readdir64_r calls. Note
8 * this has not been tested on anything other than GNU/Linux i386,
9 * and that the regular readdir wrapper will take slightly more
10 * space than Ted's original since it now includes a lock.
12 * Compile using the command:
14 * gcc -o spd_readdir.so -shared -fpic spd_readdir.c -ldl
16 * Use it by setting the LD_PRELOAD environment variable:
18 * export LD_PRELOAD=/usr/local/sbin/spd_readdir.so
21 * This file may be redistributed under the terms of the GNU Public
27 #define ALLOC_STEPSIZE 100
31 /* Util we autoconfiscate spd_readdir... */
32 #define HAVE___SECURE_GETENV 1
34 #define HAVE_SYS_PRCTL_H 1
37 #define DEBUG_DIR(x) {if (do_debug) { x; }}
43 #define __USE_LARGEFILE64
47 #include <sys/types.h>
54 #ifdef HAVE_SYS_PRCTL_H
55 #include <sys/prctl.h>
57 #define PR_GET_DUMPABLE 3
62 unsigned long long d_ino
;
64 unsigned short int d_reclen
;
71 pthread_mutex_t lock
; /* Mutex lock for this structure. */
77 struct dirent ret_dir
;
78 struct dirent64 ret_dir64
;
81 static int (*real_closedir
)(DIR *dir
) = 0;
82 static DIR *(*real_opendir
)(const char *name
) = 0;
83 static DIR *(*real_fdopendir
)(int fd
) = 0;
84 static void *(*real_rewinddir
)(DIR *dirp
) = 0;
85 static struct dirent
*(*real_readdir
)(DIR *dir
) = 0;
86 static int (*real_readdir_r
)(DIR *dir
, struct dirent
*entry
,
87 struct dirent
**result
) = 0;
88 static struct dirent64
*(*real_readdir64
)(DIR *dir
) = 0;
89 static int (*real_readdir64_r
)(DIR *dir
, struct dirent64
*entry
,
90 struct dirent64
**result
) = 0;
91 static off_t (*real_telldir
)(DIR *dir
) = 0;
92 static void (*real_seekdir
)(DIR *dir
, off_t offset
) = 0;
93 static int (*real_dirfd
)(DIR *dir
) = 0;
94 static unsigned long max_dirsize
= MAX_DIRSIZE
;
95 static int num_open
= 0;
97 static int do_debug
= 0;
100 static char *safe_getenv(const char *arg
)
102 if ((getuid() != geteuid()) || (getgid() != getegid()))
105 if (prctl(PR_GET_DUMPABLE
, 0, 0, 0, 0) == 0)
108 #if (defined(linux) && defined(SYS_prctl))
109 if (syscall(SYS_prctl
, PR_GET_DUMPABLE
, 0, 0, 0, 0) == 0)
114 #if HAVE___SECURE_GETENV
115 return __secure_getenv(arg
);
121 static void setup_ptr()
125 real_opendir
= dlsym(RTLD_NEXT
, "opendir");
126 real_fdopendir
= dlsym(RTLD_NEXT
, "fdopendir");
127 real_closedir
= dlsym(RTLD_NEXT
, "closedir");
128 real_rewinddir
= dlsym(RTLD_NEXT
, "rewinddir");
129 real_readdir
= dlsym(RTLD_NEXT
, "readdir");
130 real_readdir_r
= dlsym(RTLD_NEXT
, "readdir_r");
131 real_readdir64
= dlsym(RTLD_NEXT
, "readdir64");
132 real_readdir64_r
= dlsym(RTLD_NEXT
, "readdir64_r");
133 real_telldir
= dlsym(RTLD_NEXT
, "telldir");
134 real_seekdir
= dlsym(RTLD_NEXT
, "seekdir");
135 real_dirfd
= dlsym(RTLD_NEXT
, "dirfd");
136 if ((cp
= safe_getenv("SPD_READDIR_MAX_SIZE")) != NULL
) {
137 max_dirsize
= atol(cp
);
140 if (safe_getenv("SPD_READDIR_DEBUG")) {
141 printf("initialized!\n");
147 static void free_cached_dir(struct dir_s
*dirstruct
)
151 pthread_mutex_destroy(&(dirstruct
->lock
));
156 for (i
=0; i
< dirstruct
->num
; i
++) {
157 free(dirstruct
->dp
[i
].d_name
);
161 dirstruct
->max
= dirstruct
->num
= 0;
164 static int ino_cmp(const void *a
, const void *b
)
166 const struct dirent_s
*ds_a
= (const struct dirent_s
*) a
;
167 const struct dirent_s
*ds_b
= (const struct dirent_s
*) b
;
173 if (ds_a
->d_name
[0] == '.') {
174 if (ds_a
->d_name
[1] == 0)
176 else if ((ds_a
->d_name
[1] == '.') && (ds_a
->d_name
[2] == 0))
179 if (ds_b
->d_name
[0] == '.') {
180 if (ds_b
->d_name
[1] == 0)
182 else if ((ds_b
->d_name
[1] == '.') && (ds_b
->d_name
[2] == 0))
189 static struct dir_s
*alloc_dirstruct(DIR *dir
)
191 struct dir_s
*dirstruct
;
192 static pthread_mutexattr_t mutexattr
;
193 mutexattr
.__align
= PTHREAD_MUTEX_RECURSIVE
;
195 dirstruct
= malloc(sizeof(struct dir_s
));
197 memset(dirstruct
, 0, sizeof(struct dir_s
));
198 dirstruct
->dir
= dir
;
199 pthread_mutex_init(&(dirstruct
->lock
), &mutexattr
);
203 static void cache_dirstruct(struct dir_s
*dirstruct
)
205 struct dirent_s
*ds
, *dnew
;
208 while ((d
= (*real_readdir64
)(dirstruct
->dir
)) != NULL
) {
209 if (dirstruct
->num
>= dirstruct
->max
) {
210 dirstruct
->max
+= ALLOC_STEPSIZE
;
211 DEBUG_DIR(printf("Reallocating to size %d\n",
213 dnew
= realloc(dirstruct
->dp
,
214 dirstruct
->max
* sizeof(struct dir_s
));
217 dirstruct
->dp
= dnew
;
219 ds
= &dirstruct
->dp
[dirstruct
->num
++];
220 ds
->d_ino
= d
->d_ino
;
221 ds
->d_off
= d
->d_off
;
222 ds
->d_reclen
= d
->d_reclen
;
223 ds
->d_type
= d
->d_type
;
224 if ((ds
->d_name
= malloc(strlen(d
->d_name
)+1)) == NULL
) {
228 strcpy(ds
->d_name
, d
->d_name
);
229 DEBUG_DIR(printf("readdir: %lu %s\n",
230 (unsigned long) d
->d_ino
, d
->d_name
));
232 qsort(dirstruct
->dp
, dirstruct
->num
, sizeof(struct dirent_s
), ino_cmp
);
235 DEBUG_DIR(printf("No memory, backing off to direct readdir\n"));
236 free_cached_dir(dirstruct
);
237 dirstruct
->direct
= 1;
240 DIR *opendir(const char *name
)
243 struct dir_s
*dirstruct
;
249 DEBUG_DIR(printf("Opendir(%s) (%d open)\n", name
, num_open
++));
250 dir
= (*real_opendir
)(name
);
254 dirstruct
= alloc_dirstruct(dir
);
256 (*real_closedir
)(dir
);
261 if (max_dirsize
&& (stat(name
, &st
) == 0) &&
262 (st
.st_size
> max_dirsize
)) {
263 DEBUG_DIR(printf("Directory size %ld, using direct readdir\n",
265 dirstruct
->direct
= 1;
266 return (DIR *) dirstruct
;
269 cache_dirstruct(dirstruct
);
270 return ((DIR *) dirstruct
);
273 DIR *fdopendir(int fd
)
276 struct dir_s
*dirstruct
;
282 DEBUG_DIR(printf("fdopendir(%d) (%d open)\n", fd
, num_open
++));
283 dir
= (*real_fdopendir
)(fd
);
287 dirstruct
= alloc_dirstruct(dir
);
289 (*real_closedir
)(dir
);
294 if (max_dirsize
&& (fstat(fd
, &st
) == 0) &&
295 (st
.st_size
> max_dirsize
)) {
296 DEBUG_DIR(printf("Directory size %ld, using direct readdir\n",
298 dirstruct
->dir
= dir
;
299 dirstruct
->direct
= 1;
300 return (DIR *) dirstruct
;
303 cache_dirstruct(dirstruct
);
304 return ((DIR *) dirstruct
);
307 int closedir(DIR *dir
)
309 struct dir_s
*dirstruct
= (struct dir_s
*) dir
;
311 DEBUG_DIR(printf("Closedir (%d open)\n", --num_open
));
313 (*real_closedir
)(dirstruct
->dir
);
315 free_cached_dir(dirstruct
);
320 struct dirent
*readdir(DIR *dir
)
322 struct dir_s
*dirstruct
= (struct dir_s
*) dir
;
325 if (dirstruct
->direct
)
326 return (*real_readdir
)(dirstruct
->dir
);
328 if (dirstruct
->pos
>= dirstruct
->num
)
331 ds
= &dirstruct
->dp
[dirstruct
->pos
++];
332 dirstruct
->ret_dir
.d_ino
= ds
->d_ino
;
333 dirstruct
->ret_dir
.d_off
= ds
->d_off
;
334 dirstruct
->ret_dir
.d_reclen
= ds
->d_reclen
;
335 dirstruct
->ret_dir
.d_type
= ds
->d_type
;
336 strncpy(dirstruct
->ret_dir
.d_name
, ds
->d_name
,
337 sizeof(dirstruct
->ret_dir
.d_name
));
339 return (&dirstruct
->ret_dir
);
342 int readdir_r(DIR *dir
, struct dirent
*entry
, struct dirent
**result
)
344 struct dir_s
*dirstruct
= (struct dir_s
*) dir
;
347 if (dirstruct
->direct
)
348 return (*real_readdir_r
)(dirstruct
->dir
, entry
, result
);
350 pthread_mutex_lock(&(dirstruct
->lock
));
351 if (dirstruct
->pos
>= dirstruct
->num
) {
354 ds
= &dirstruct
->dp
[dirstruct
->pos
++];
355 entry
->d_ino
= ds
->d_ino
;
356 entry
->d_off
= ds
->d_off
;
357 entry
->d_reclen
= ds
->d_reclen
;
358 entry
->d_type
= ds
->d_type
;
359 strncpy(entry
->d_name
, ds
->d_name
, sizeof(entry
->d_name
));
362 pthread_mutex_unlock(&(dirstruct
->lock
));
366 struct dirent64
*readdir64(DIR *dir
)
368 struct dir_s
*dirstruct
= (struct dir_s
*) dir
;
371 if (dirstruct
->direct
)
372 return (*real_readdir64
)(dirstruct
->dir
);
374 if (dirstruct
->pos
>= dirstruct
->num
)
377 ds
= &dirstruct
->dp
[dirstruct
->pos
++];
378 dirstruct
->ret_dir64
.d_ino
= ds
->d_ino
;
379 dirstruct
->ret_dir64
.d_off
= ds
->d_off
;
380 dirstruct
->ret_dir64
.d_reclen
= ds
->d_reclen
;
381 dirstruct
->ret_dir64
.d_type
= ds
->d_type
;
382 strncpy(dirstruct
->ret_dir64
.d_name
, ds
->d_name
,
383 sizeof(dirstruct
->ret_dir64
.d_name
));
385 return (&dirstruct
->ret_dir64
);
388 int readdir64_r (DIR *__restrict dir
,
389 struct dirent64
*__restrict entry
,
390 struct dirent64
**__restrict result
)
392 struct dir_s
*dirstruct
= (struct dir_s
*) dir
;
395 if (dirstruct
->direct
)
396 return (*real_readdir64_r
)(dir
, entry
, result
);
397 pthread_mutex_lock(&(dirstruct
->lock
));
398 if (dirstruct
->pos
>= dirstruct
->num
) {
401 ds
= &dirstruct
->dp
[dirstruct
->pos
++];
402 entry
->d_ino
= ds
->d_ino
;
403 entry
->d_off
= ds
->d_off
;
404 entry
->d_reclen
= ds
->d_reclen
;
405 entry
->d_type
= ds
->d_type
;
406 strncpy(entry
->d_name
, ds
->d_name
,
407 sizeof(entry
->d_name
));
410 pthread_mutex_unlock(&(dirstruct
->lock
));
414 off_t
telldir(DIR *dir
)
416 struct dir_s
*dirstruct
= (struct dir_s
*) dir
;
418 if (dirstruct
->direct
)
419 return (*real_telldir
)(dirstruct
->dir
);
421 return ((off_t
) dirstruct
->pos
);
424 void seekdir(DIR *dir
, off_t offset
)
426 struct dir_s
*dirstruct
= (struct dir_s
*) dir
;
428 if (dirstruct
->direct
) {
429 (*real_seekdir
)(dirstruct
->dir
, offset
);
433 dirstruct
->pos
= offset
;
436 void rewinddir(DIR *dir
)
438 struct dir_s
*dirstruct
= (struct dir_s
*) dir
;
440 (*real_rewinddir
)(dirstruct
->dir
);
441 if (dirstruct
->direct
)
444 pthread_mutex_lock(&(dirstruct
->lock
));
446 free_cached_dir(dirstruct
);
447 cache_dirstruct(dirstruct
);
448 pthread_mutex_unlock(&(dirstruct
->lock
));
453 struct dir_s
*dirstruct
= (struct dir_s
*) dir
;
454 int fd
= (*real_dirfd
)(dirstruct
->dir
);
456 DEBUG_DIR(printf("dirfd %d, %p\n", fd
, real_dirfd
));