1 /*
2  * Copyright (C) 2012-2013  ProFUSION embedded systems
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 #include <assert.h>
19 #include <dirent.h>
20 #include <dlfcn.h>
21 #include <errno.h>
22 #include <fcntl.h>
23 #include <limits.h>
24 #include <stdarg.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <unistd.h>
29 #include <sys/stat.h>
30 #include <sys/types.h>
31 
32 #include <shared/util.h>
33 
34 #include "testsuite.h"
35 
36 static void *nextlib;
37 static const char *rootpath;
38 static size_t rootpathlen;
39 
need_trap(const char * path)40 static inline bool need_trap(const char *path)
41 {
42 	return path != NULL && path[0] == '/'
43 		&& !strstartswith(path, ABS_TOP_BUILDDIR);
44 }
45 
trap_path(const char * path,char buf[PATH_MAX * 2])46 static const char *trap_path(const char *path, char buf[PATH_MAX * 2])
47 {
48 	size_t len;
49 
50 	if (!need_trap(path))
51 		return path;
52 
53 	len = strlen(path);
54 
55 	if (len + rootpathlen > PATH_MAX * 2) {
56 		errno = ENAMETOOLONG;
57 		return NULL;
58 	}
59 
60 	memcpy(buf, rootpath, rootpathlen);
61 	strcpy(buf + rootpathlen, path);
62 	return buf;
63 }
64 
get_rootpath(const char * f)65 static bool get_rootpath(const char *f)
66 {
67 	if (rootpath != NULL)
68 		return true;
69 
70 	rootpath = getenv(S_TC_ROOTFS);
71 	if (rootpath == NULL) {
72 		ERR("TRAP %s(): missing export %s?\n", f, S_TC_ROOTFS);
73 		errno = ENOENT;
74 		return false;
75 	}
76 
77 	rootpathlen = strlen(rootpath);
78 
79 	return true;
80 }
81 
get_libc_func(const char * f)82 static void *get_libc_func(const char *f)
83 {
84 	void *fp;
85 
86 	if (nextlib == NULL) {
87 #ifdef RTLD_NEXT
88 		nextlib = RTLD_NEXT;
89 #else
90 		nextlib = dlopen("libc.so.6", RTLD_LAZY);
91 #endif
92 	}
93 
94 	fp = dlsym(nextlib, f);
95 	assert(fp);
96 
97 	return fp;
98 }
99 
100 /* wrapper template for a function with one "const char* path" argument */
101 #define WRAP_1ARG(rettype, failret, name) \
102 TS_EXPORT rettype name(const char *path) \
103 { \
104 	const char *p;				\
105 	char buf[PATH_MAX * 2];                 \
106 	static rettype (*_fn)(const char*);	\
107 						\
108 	if (!get_rootpath(__func__))		\
109 		return failret;			\
110 	_fn = get_libc_func(#name);		\
111 	p = trap_path(path, buf);		\
112 	if (p == NULL)				\
113 		return failret;			\
114 	return (*_fn)(p);			\
115 }
116 
117 /* wrapper template for a function with "const char* path" and another argument */
118 #define WRAP_2ARGS(rettype, failret, name, arg2t)	\
119 TS_EXPORT rettype name(const char *path, arg2t arg2)	\
120 { \
121 	const char *p;					\
122 	char buf[PATH_MAX * 2];				\
123 	static rettype (*_fn)(const char*, arg2t arg2);	\
124 							\
125 	if (!get_rootpath(__func__))			\
126 		return failret;				\
127 	_fn = get_libc_func(#name);			\
128 	p = trap_path(path, buf);			\
129 	if (p == NULL)					\
130 		return failret;				\
131 	return (*_fn)(p, arg2);				\
132 }
133 
134 /* wrapper template for open family */
135 #define WRAP_OPEN(suffix)					\
136 TS_EXPORT int open ## suffix (const char *path, int flags, ...)	\
137 { \
138 	const char *p;						\
139 	char buf[PATH_MAX * 2];					\
140 	static int (*_fn)(const char *path, int flags, ...);	\
141 								\
142 	if (!get_rootpath(__func__))				\
143 		return -1;					\
144 	_fn = get_libc_func("open" #suffix);			\
145 	p = trap_path(path, buf);				\
146 	if (p == NULL)						\
147 		return -1;					\
148 								\
149 	if (flags & O_CREAT) {					\
150 		mode_t mode;					\
151 		va_list ap;					\
152 								\
153 		va_start(ap, flags);				\
154 		mode = va_arg(ap, mode_t);			\
155 		va_end(ap);					\
156 		return _fn(p, flags, mode);			\
157 	}							\
158 								\
159 	return _fn(p, flags);					\
160 }
161 
162 /* wrapper template for __xstat family */
163 #define WRAP_VERSTAT(prefix, suffix)			    \
164 TS_EXPORT int prefix ## stat ## suffix (int ver,	    \
165 			      const char *path,		    \
166 	                      struct stat ## suffix *st)    \
167 { \
168 	const char *p;					    \
169 	char buf[PATH_MAX * 2];				    \
170 	static int (*_fn)(int ver, const char *path,	    \
171 		          struct stat ## suffix *);	    \
172 	_fn = get_libc_func(#prefix "stat" #suffix);	    \
173 							    \
174 	if (!get_rootpath(__func__))			    \
175 		return -1;				    \
176 	p = trap_path(path, buf);			    \
177 	if (p == NULL)					    \
178 		return -1;				    \
179 							    \
180 	return _fn(ver, p, st);				    \
181 }
182 
183 WRAP_1ARG(DIR*, NULL, opendir);
184 
185 WRAP_2ARGS(FILE*, NULL, fopen, const char*);
186 WRAP_2ARGS(int, -1, mkdir, mode_t);
187 WRAP_2ARGS(int, -1, access, int);
188 WRAP_2ARGS(int, -1, stat, struct stat*);
189 WRAP_2ARGS(int, -1, lstat, struct stat*);
190 #ifndef _FILE_OFFSET_BITS
191 WRAP_2ARGS(int, -1, stat64, struct stat64*);
192 WRAP_2ARGS(int, -1, lstat64, struct stat64*);
193 WRAP_OPEN(64);
194 #endif
195 
196 WRAP_OPEN();
197 
198 #ifdef HAVE___XSTAT
199 WRAP_VERSTAT(__x,);
200 WRAP_VERSTAT(__lx,);
201 #ifndef _FILE_OFFSET_BITS
202 WRAP_VERSTAT(__x,64);
203 WRAP_VERSTAT(__lx,64);
204 #endif
205 #endif
206