2 pread_cache.c: a cache to map pread() calls to fread() to prevent small
3 reads on MacOSX network filesystems that don't do block cacheing
5 This is useful for Maya, which does very small reads (often 2 bytes or 4 bytes)
6 which when used with smbfs on MacOSX gives extremely poor performance.
8 tridge@samba.org January 2008
10 released under GNU GPLv3 or later
13 gcc pread_cache.c -Wall -g -o pread_cache.dylib -dynamiclib
17 DYLD_INSERT_LIBRARIES=$PWD/pread_cache.dylib DYLD_FORCE_FLAT_NAMESPACE=1 command
20 #include <sys/types.h>
30 /* chosen to match the smbfs on MacOSX read size */
31 #define MAX_READ 61000
33 /* to keep the data structures simple, we only cache the low MAX_FILES file descriptors */
34 #define MAX_FILES 1024
35 static FILE *file_handles[MAX_FILES];
36 static unsigned char file_state[MAX_FILES];
39 #define STATE_CACHED 1
40 #define STATE_DISABLE 2
42 /* catch close to free up any existing caches */
45 static int (*close_orig)(int fd);
46 if (close_orig == NULL) {
47 close_orig = dlsym(RTLD_NEXT, "close");
48 if (close_orig == NULL) {
52 if (fd < 0 || fd >= MAX_FILES) {
53 return close_orig(fd);
56 if (file_state[fd] == STATE_CACHED) {
58 file_state[fd] = STATE_NONE;
60 file_handles[fd] = NULL;
61 /* the fclose() does an implied close() */
65 file_state[fd] = STATE_NONE;
66 return close_orig(fd);
70 catch pread() and if possible map to fread()
72 ssize_t pread(int fd, void *buf, size_t nbytes, off_t offset)
74 static ssize_t (*pread_orig)(int d, void *buf, size_t nbytes, off_t offset);
77 if (pread_orig == NULL) {
78 pread_orig = dlsym(RTLD_NEXT, "pread");
79 if (pread_orig == NULL) {
82 pagesize = getpagesize();
85 /* large reads and reads on out of range file descriptors are not cached */
86 if (nbytes >= MAX_READ || fd < 0 || fd >= MAX_FILES) {
87 return pread_orig(fd, buf, nbytes, offset);
90 if (file_state[fd] == STATE_DISABLE) {
91 return pread_orig(fd, buf, nbytes, offset);
94 /* see if we already have a cache */
95 if (file_state[fd] == STATE_NONE) {
98 /* see if the file was opened read-write. This relies on the mmap error codes */
99 ptr = mmap(NULL, pagesize, PROT_READ|PROT_WRITE, MAP_FILE|MAP_SHARED, fd, 0);
100 if (ptr != (void *)-1) {
101 munmap(ptr, pagesize);
102 file_state[fd] = STATE_DISABLE;
103 return pread_orig(fd, buf, nbytes, offset);
105 if (errno != EACCES) {
106 file_state[fd] = STATE_DISABLE;
107 return pread_orig(fd, buf, nbytes, offset);
110 file_handles[fd] = fdopen(fd, "r");
111 if (file_handles[fd] == NULL) {
112 file_state[fd] = STATE_DISABLE;
113 return pread_orig(fd, buf, nbytes, offset);
116 /* ensure we have a big enough buffer */
117 setvbuf(file_handles[fd], NULL, _IOFBF, MAX_READ);
119 file_state[fd] = STATE_CACHED;
122 /* seek to the right place if need be */
123 if (ftello(file_handles[fd]) != offset &&
124 fseeko(file_handles[fd], offset, SEEK_SET) != 0) {
125 return pread_orig(fd, buf, nbytes, offset);
128 return fread(buf, 1, nbytes, file_handles[fd]);