source: box/trunk/lib/intercept/intercept.cpp @ 2351

Revision 2351, 13.7 KB checked in by chris, 4 years ago (diff)

Ensure that _FILE_OFFSET_BITS is defined before testing its value.

Move definition of DEFINE_ONLY_OPEN64 into intercept.cpp which is
the only place that should use it.

  • Property svn:eol-style set to native
Line 
1// --------------------------------------------------------------------------
2//
3// File
4//              Name:    intercept.cpp
5//              Purpose: Syscall interception code for the raidfile test
6//              Created: 2003/07/22
7//
8// --------------------------------------------------------------------------
9
10#include "Box.h"
11
12#include "intercept.h"
13
14#ifdef HAVE_SYS_SYSCALL_H
15        #include <sys/syscall.h>
16#endif
17#include <sys/types.h>
18#include <unistd.h>
19
20#ifdef HAVE_SYS_UIO_H
21        #include <sys/uio.h>
22#endif
23
24#include <errno.h>
25#include <stdarg.h>
26
27#ifdef HAVE_DLFCN_H
28#include <dlfcn.h>
29#endif
30
31#ifndef PLATFORM_CLIB_FNS_INTERCEPTION_IMPOSSIBLE
32
33#if !defined(HAVE_SYSCALL) && !defined(HAVE___SYSCALL) && !defined(HAVE___SYSCALL_NEED_DEFN)
34        #define PLATFORM_NO_SYSCALL
35#endif
36
37#ifdef PLATFORM_NO_SYSCALL
38        // For some reason, syscall just doesn't work on Darwin
39        // so instead, we build functions using assembler in a varient
40        // of the technique used in the Darwin Libc
41        extern "C" int
42        TEST_open(const char *path, int flags, mode_t mode);
43        extern "C" int
44        TEST_close(int d);
45        extern "C" ssize_t
46        TEST_write(int d, const void *buf, size_t nbytes);
47        extern "C" ssize_t
48        TEST_read(int d, void *buf, size_t nbytes);
49        extern "C" ssize_t
50        TEST_readv(int d, const struct iovec *iov, int iovcnt);
51        extern "C" off_t
52        TEST_lseek(int fildes, off_t offset, int whence);
53#else
54        // if we have __syscall, we should use it for everything
55        // (on FreeBSD 7 this is required for 64-bit alignment of off_t).
56        // if not, we should continue to use the old syscall().
57        #ifdef HAVE___SYSCALL_NEED_DEFN
58                // Need this, not declared in syscall.h nor unistd.h
59                extern "C" off_t __syscall(quad_t number, ...);
60        #endif
61        #ifdef HAVE___SYSCALL
62                #undef syscall
63                #define syscall __syscall
64        #endif
65#endif
66
67#include <string.h>
68#include <stdio.h>
69
70#include "MemLeakFindOn.h"
71
72int intercept_count = 0;
73const char *intercept_filename = 0;
74int intercept_filedes = -1;
75off_t intercept_errorafter = 0;
76int intercept_errno = 0;
77int intercept_syscall = 0;
78off_t intercept_filepos = 0;
79int intercept_delay_ms = 0;
80
81static opendir_t*  opendir_real  = NULL;
82static readdir_t*  readdir_real  = NULL;
83static readdir_t*  readdir_hook  = NULL;
84static closedir_t* closedir_real = NULL;
85static lstat_t*    lstat_real    = NULL;
86static lstat_t*    lstat_hook    = NULL;
87static const char* lstat_file    = NULL;
88static lstat_t*    stat_real     = NULL;
89static lstat_t*    stat_hook     = NULL;
90static const char* stat_file     = NULL;
91
92static lstat_post_hook_t* lstat_post_hook = NULL;
93static lstat_post_hook_t* stat_post_hook  = NULL;
94
95#define SIZE_ALWAYS_ERROR       -773
96
97void intercept_clear_setup()
98{
99        intercept_count = 0;
100        intercept_filename = 0;
101        intercept_filedes = -1;
102        intercept_errorafter = 0;
103        intercept_syscall = 0;
104        intercept_filepos = 0;
105        intercept_delay_ms = 0;
106        readdir_hook = NULL;
107        stat_hook = NULL;
108        lstat_hook = NULL;
109        stat_post_hook = NULL;
110        lstat_post_hook = NULL;
111}
112
113bool intercept_triggered()
114{
115        return intercept_count == 0;
116}
117
118void intercept_setup_error(const char *filename, unsigned int errorafter, int errortoreturn, int syscalltoerror)
119{
120        BOX_TRACE("Setup for error: " << filename << 
121                ", after " << errorafter <<
122                ", err " << errortoreturn <<
123                ", syscall " << syscalltoerror);
124
125        intercept_count = 1;
126        intercept_filename = filename;
127        intercept_filedes = -1;
128        intercept_errorafter = errorafter;
129        intercept_syscall = syscalltoerror;
130        intercept_errno = errortoreturn;
131        intercept_filepos = 0;
132        intercept_delay_ms = 0;
133}
134
135void intercept_setup_delay(const char *filename, unsigned int delay_after, 
136        int delay_ms, int syscall_to_delay, int num_delays)
137{
138        BOX_TRACE("Setup for delay: " << filename <<
139                ", after " << delay_after <<
140                ", wait " << delay_ms << " ms" <<
141                ", times " << num_delays <<
142                ", syscall " << syscall_to_delay);
143
144        intercept_count = num_delays;
145        intercept_filename = filename;
146        intercept_filedes = -1;
147        intercept_errorafter = delay_after;
148        intercept_syscall = syscall_to_delay;
149        intercept_errno = 0;
150        intercept_filepos = 0;
151        intercept_delay_ms = delay_ms;
152}
153
154bool intercept_errornow(int d, int size, int syscallnum)
155{
156        ASSERT(intercept_count > 0)
157
158        if (intercept_filedes == -1)
159        {
160                return false; // no error please!
161        }
162
163        if (d != intercept_filedes)
164        {
165                return false; // no error please!
166        }
167
168        if (syscallnum != intercept_syscall)
169        {
170                return false; // no error please!
171        }
172
173        bool ret = false; // no error unless one of the conditions matches
174
175        //printf("Checking for err, %d, %d, %d\n", d, size, syscallnum);
176
177        if (intercept_delay_ms != 0)
178        {
179                BOX_TRACE("Delaying " << intercept_delay_ms << " ms " <<
180                        " for syscall " << syscallnum << 
181                        " at " << intercept_filepos);
182
183                struct timespec tm;
184                tm.tv_sec = intercept_delay_ms / 1000;
185                tm.tv_nsec = (intercept_delay_ms % 1000) * 1000000;
186                while (nanosleep(&tm, &tm) != 0 &&
187                        errno == EINTR) { }
188        }
189
190        if (size == SIZE_ALWAYS_ERROR)
191        {
192                // Looks good for an error!
193                BOX_TRACE("Returning error " << intercept_errno <<
194                        " for syscall " << syscallnum);
195                ret = true;
196        }
197        else if (intercept_filepos + size < intercept_errorafter)
198        {
199                return false; // no error please
200        }
201        else if (intercept_errno != 0)
202        {
203                BOX_TRACE("Returning error " << intercept_errno << 
204                        " for syscall " << syscallnum <<
205                        " at " << intercept_filepos);
206                ret = true;
207        }
208
209        intercept_count--;
210        if (intercept_count == 0)
211        {
212                intercept_clear_setup();
213        }
214
215        return ret;
216}
217
218int intercept_reterr()
219{
220        int err = intercept_errno;
221        intercept_clear_setup();
222        return err;
223}
224
225#define CHECK_FOR_FAKE_ERROR_COND(D, S, CALL, FAILRES) \
226        if(intercept_count > 0) \
227        { \
228                if(intercept_errornow(D, S, CALL)) \
229                { \
230                        errno = intercept_reterr(); \
231                        return FAILRES; \
232                } \
233        }
234
235#if defined _FILE_OFFSET_BITS && _FILE_OFFSET_BITS == 64
236        #define DEFINE_ONLY_OPEN64
237#endif
238
239extern "C" int
240#ifdef DEFINE_ONLY_OPEN64
241        open64(const char *path, int flags, ...)
242#else
243        open(const char *path, int flags, ...)
244#endif // DEFINE_ONLY_OPEN64
245{
246        if(intercept_count > 0)
247        {
248                if(intercept_filename != NULL &&
249                        intercept_syscall == SYS_open &&
250                        strcmp(path, intercept_filename) == 0)
251                {
252                        errno = intercept_reterr();
253                        return -1;
254                }
255        }
256
257        mode_t mode = 0;
258        if (flags & O_CREAT)
259        {
260                va_list ap;
261                va_start(ap, flags);
262                mode = va_arg(ap, int);
263                va_end(ap);
264        }
265
266#ifdef PLATFORM_NO_SYSCALL
267        int r = TEST_open(path, flags, mode);
268#else
269        int r = syscall(SYS_open, path, flags, mode);
270#endif
271
272        if(intercept_filename != NULL && 
273                intercept_count > 0 && 
274                intercept_filedes == -1)
275        {
276                // Right file?
277                if(strcmp(intercept_filename, path) == 0)
278                {
279                        intercept_filedes = r;
280                        //printf("Found file to intercept, h = %d\n", r);
281                }
282        }
283
284        return r;
285}
286
287#ifndef DEFINE_ONLY_OPEN64
288extern "C" int
289// open64(const char *path, int flags, mode_t mode)
290// open64(const char *path, int flags, ...)
291open64 (__const char *path, int flags, ...)
292{
293        mode_t mode = 0;
294        if (flags & O_CREAT)
295        {
296                va_list ap;
297                va_start(ap, flags);
298                mode = va_arg(ap, int);
299                va_end(ap);
300        }
301
302        // With _FILE_OFFSET_BITS set to 64 this should really use (flags |
303        // O_LARGEFILE) here, but not actually necessary for the tests and not
304        // worth the trouble finding O_LARGEFILE
305        return open(path, flags, mode);
306}
307#endif // !DEFINE_ONLY_OPEN64
308
309extern "C" int
310close(int d)
311{
312        CHECK_FOR_FAKE_ERROR_COND(d, SIZE_ALWAYS_ERROR, SYS_close, -1);
313#ifdef PLATFORM_NO_SYSCALL
314        int r = TEST_close(d);
315#else
316        int r = syscall(SYS_close, d);
317#endif
318        if(r == 0)
319        {
320                if(d == intercept_filedes)
321                {
322                        intercept_filedes = -1;
323                }
324        }
325        return r;
326}
327
328extern "C" ssize_t
329write(int d, const void *buf, size_t nbytes)
330{
331        CHECK_FOR_FAKE_ERROR_COND(d, nbytes, SYS_write, -1);
332#ifdef PLATFORM_NO_SYSCALL
333        int r = TEST_write(d, buf, nbytes);
334#else
335        int r = syscall(SYS_write, d, buf, nbytes);
336#endif
337        if(r != -1)
338        {
339                intercept_filepos += r;
340        }
341        return r;
342}
343
344extern "C" ssize_t
345read(int d, void *buf, size_t nbytes)
346{
347        CHECK_FOR_FAKE_ERROR_COND(d, nbytes, SYS_read, -1);
348#ifdef PLATFORM_NO_SYSCALL
349        int r = TEST_read(d, buf, nbytes);
350#else
351        int r = syscall(SYS_read, d, buf, nbytes);
352#endif
353        if(r != -1)
354        {
355                intercept_filepos += r;
356        }
357        return r;
358}
359
360extern "C" ssize_t
361readv(int d, const struct iovec *iov, int iovcnt)
362{
363        // how many bytes?
364        int nbytes = 0;
365        for(int b = 0; b < iovcnt; ++b)
366        {
367                nbytes += iov[b].iov_len;
368        }
369
370        CHECK_FOR_FAKE_ERROR_COND(d, nbytes, SYS_readv, -1);
371#ifdef PLATFORM_NO_SYSCALL
372        int r = TEST_readv(d, iov, iovcnt);
373#else
374        int r = syscall(SYS_readv, d, iov, iovcnt);
375#endif
376        if(r != -1)
377        {
378                intercept_filepos += r;
379        }
380        return r;
381}
382
383extern "C" off_t
384lseek(int fildes, off_t offset, int whence)
385{
386        // random magic for lseek syscall, see /usr/src/lib/libc/sys/lseek.c
387        CHECK_FOR_FAKE_ERROR_COND(fildes, 0, SYS_lseek, -1);
388#ifdef PLATFORM_NO_SYSCALL
389        int r = TEST_lseek(fildes, offset, whence);
390#else
391        #ifdef HAVE_LSEEK_DUMMY_PARAM
392                off_t r = syscall(SYS_lseek, fildes, 0 /* extra 0 required here! */, offset, whence);
393        #elif defined(_FILE_OFFSET_BITS)
394                // Don't bother trying to call SYS__llseek on 32 bit since it is
395                // fiddly and not needed for the tests
396                off_t r = syscall(SYS_lseek, fildes, (uint32_t)offset, whence);
397        #else
398                off_t r = syscall(SYS_lseek, fildes, offset, whence);
399        #endif
400#endif
401        if(r != -1)
402        {
403                intercept_filepos = r;
404        }
405        return r;
406}
407
408void intercept_setup_readdir_hook(const char *dirname, readdir_t hookfn)
409{
410        if (hookfn != NULL && dirname == NULL)
411        {
412                dirname = intercept_filename;
413                ASSERT(dirname != NULL);
414        }
415
416        if (hookfn != NULL)
417        {
418                BOX_TRACE("readdir hooked to " << hookfn << " for " << dirname);
419        }
420        else if (intercept_filename != NULL)
421        {
422                BOX_TRACE("readdir unhooked from " << readdir_hook << 
423                        " for " << intercept_filename);
424        }
425
426        intercept_filename = dirname;
427        readdir_hook = hookfn;
428}
429
430void intercept_setup_lstat_hook(const char *filename, lstat_t hookfn)
431{
432        /*
433        if (hookfn != NULL)
434        {
435                BOX_TRACE("lstat hooked to " << hookfn << " for " << filename);
436        }
437        else
438        {
439                BOX_TRACE("lstat unhooked from " << lstat_hook << " for " <<
440                        lstat_file);
441        }
442        */
443
444        lstat_file = filename;
445        lstat_hook = hookfn;
446}
447
448void intercept_setup_lstat_post_hook(lstat_post_hook_t hookfn)
449{
450        /*
451        if (hookfn != NULL)
452        {
453                BOX_TRACE("lstat hooked to " << hookfn << " for " << filename);
454        }
455        else
456        {
457                BOX_TRACE("lstat unhooked from " << lstat_hook << " for " <<
458                        lstat_file);
459        }
460        */
461
462        lstat_post_hook = hookfn;
463}
464
465void intercept_setup_stat_post_hook(lstat_post_hook_t hookfn)
466{
467        /*
468        if (hookfn != NULL)
469        {
470                BOX_TRACE("lstat hooked to " << hookfn << " for " << filename);
471        }
472        else
473        {
474                BOX_TRACE("lstat unhooked from " << lstat_hook << " for " <<
475                        lstat_file);
476        }
477        */
478
479        stat_post_hook = hookfn;
480}
481
482static void * find_function(const char *pName)
483{
484        dlerror();
485        void *result = NULL;
486
487        #ifdef HAVE_LARGE_FILE_SUPPORT
488        {
489                // search for the 64-bit version first
490                std::string name64(pName);
491                name64 += "64";
492                result = dlsym(RTLD_NEXT, name64.c_str());
493                if (dlerror() == NULL && result != NULL)
494                {
495                        return result;
496                }
497        }
498        #endif
499
500        result = dlsym(RTLD_NEXT, pName);
501        const char *errmsg = (const char *)dlerror();
502
503        if (errmsg == NULL)
504        {
505                return result;
506        }
507
508        BOX_ERROR("Failed to find real " << pName << " function: " << errmsg);
509        return NULL;
510}
511
512extern "C" 
513DIR *opendir(const char *dirname)
514{
515        if (opendir_real == NULL)
516        {
517                opendir_real = (opendir_t*)find_function("opendir");
518        }
519
520        if (opendir_real == NULL)
521        {
522                perror("cannot find real opendir");
523                return NULL;
524        }
525
526        DIR* r = opendir_real(dirname);
527
528        if (readdir_hook != NULL && 
529                intercept_filename != NULL && 
530                intercept_filedes == -1 && 
531                strcmp(intercept_filename, dirname) == 0)
532        {
533                intercept_filedes = dirfd(r);
534                //printf("Found file to intercept, h = %d\n", r);
535        }
536
537        return r;
538}
539
540extern "C"
541struct dirent *readdir(DIR *dir)
542{
543        if (readdir_hook != NULL && dirfd(dir) == intercept_filedes)
544        {
545                return readdir_hook(dir);
546        }
547
548        if (readdir_real == NULL)
549        {
550                readdir_real = (readdir_t*)find_function("readdir");
551        }
552
553        if (readdir_real == NULL)
554        {
555                perror("cannot find real readdir");
556                return NULL;
557        }
558
559        return readdir_real(dir);
560}
561
562extern "C"
563int closedir(DIR *dir)
564{
565        if (dirfd(dir) == intercept_filedes)
566        {
567                intercept_filedes = -1;
568        }
569
570        if (closedir_real == NULL)
571        {
572                closedir_real = (closedir_t*)find_function("closedir");
573        }
574
575        if (closedir_real == NULL)
576        {
577                perror("cannot find real closedir");
578                errno = ENOSYS;
579                return -1;
580        }
581
582        return closedir_real(dir);
583}
584
585extern "C" int 
586#ifdef LINUX_WEIRD_LSTAT
587__lxstat(int ver, const char *file_name, STAT_STRUCT *buf)
588#else
589lstat(const char *file_name, STAT_STRUCT *buf)
590#endif
591{
592        if (lstat_real == NULL)
593        {
594        #ifdef LINUX_WEIRD_LSTAT
595                lstat_real = (lstat_t*)find_function("__lxstat");
596        #else
597                lstat_real = (lstat_t*)find_function("lstat");
598        #endif
599        }
600
601        if (lstat_real == NULL)
602        {
603                perror("cannot find real lstat");
604                errno = ENOSYS;
605                return -1;
606        }
607
608        if (lstat_hook == NULL || strcmp(file_name, lstat_file) != 0)
609        {
610        #ifdef LINUX_WEIRD_LSTAT
611                int ret = lstat_real(ver, file_name, buf);
612        #else
613                int ret = lstat_real(file_name, buf);
614        #endif
615                if (lstat_post_hook != NULL)
616                {
617                        ret = lstat_post_hook(ret, file_name, buf);
618                }
619                return ret;
620        }
621
622        #ifdef LINUX_WEIRD_LSTAT
623        return lstat_hook(ver, file_name, buf);
624        #else
625        return lstat_hook(file_name, buf);
626        #endif
627}
628
629extern "C" int 
630#ifdef LINUX_WEIRD_LSTAT
631__xstat(int ver, const char *file_name, STAT_STRUCT *buf)
632#else
633stat(const char *file_name, STAT_STRUCT *buf)
634#endif
635{
636        if (stat_real == NULL)
637        {
638        #ifdef LINUX_WEIRD_LSTAT
639                stat_real = (lstat_t*)find_function("__xstat");
640        #else
641                stat_real = (lstat_t*)find_function("stat");
642        #endif
643        }
644
645        if (stat_real == NULL)
646        {
647                perror("cannot find real stat");
648                errno = ENOSYS;
649                return -1;
650        }
651
652        if (stat_hook == NULL || strcmp(file_name, stat_file) != 0)
653        {
654        #ifdef LINUX_WEIRD_LSTAT
655                int ret = stat_real(ver, file_name, buf);
656        #else
657                int ret = stat_real(file_name, buf);
658        #endif
659                if (stat_post_hook != NULL)
660                {
661                        ret = stat_post_hook(ret, file_name, buf);
662                }
663                return ret;
664        }
665
666        #ifdef LINUX_WEIRD_LSTAT
667        return stat_hook(ver, file_name, buf);
668        #else
669        return stat_hook(file_name, buf);
670        #endif
671}
672
673#endif // n PLATFORM_CLIB_FNS_INTERCEPTION_IMPOSSIBLE
Note: See TracBrowser for help on using the repository browser.