1From ec19461cecdac81c48bbbe4783624167754349a2 Mon Sep 17 00:00:00 2001
2From: Mike Frysinger <vapier@gentoo.org>
3Date: Thu, 8 Mar 2012 17:40:52 -0500
4Subject: [PATCH] getdents: rewrite syscall handling completely
5
6The inline asm has many problems, but rather than attempt to fix
7them, just use syscall() for everyone.  This allows us to drop the
8i386-specific checks and have the tests run on all arches.
9
10Further, add a layer between the kernel and the dirent struct that
11the tests uses.  The kernel packs the results, so we need to expand
12the raw buffer returned by the kernel into the userland structs we
13pass around.
14
15Signed-off-by: Mike Frysinger <vapier@gentoo.org>
16---
17 testcases/kernel/syscalls/getdents/getdents.h   |   73 +++++++++++++++++------
18 testcases/kernel/syscalls/getdents/getdents01.c |   20 +-----
19 testcases/kernel/syscalls/getdents/getdents02.c |   27 +--------
20 testcases/kernel/syscalls/getdents/getdents03.c |   27 +--------
21 testcases/kernel/syscalls/getdents/getdents04.c |   26 +-------
22 5 files changed, 67 insertions(+), 106 deletions(-)
23
24diff --git a/testcases/kernel/syscalls/getdents/getdents.h b/testcases/kernel/syscalls/getdents/getdents.h
25index 3ab3fd2..a5ddfea 100644
26--- a/testcases/kernel/syscalls/getdents/getdents.h
27+++ b/testcases/kernel/syscalls/getdents/getdents.h
28@@ -23,25 +23,62 @@
29
30 #ifndef __GETDENTS_H
31 #define __GETDENTS_H	1
32+
33+#include <dirent.h>
34+#include <stdio.h>
35+#include <string.h>
36+#include <unistd.h>
37 #include <sys/syscall.h>
38
39-#ifdef __i386__
40-	#define GETDENTS_ASM() ({ int __rval;				\
41-				__asm__ __volatile__("			\
42-					movl	%4, %%edx \n		\
43-					movl	%3, %%ecx \n		\
44-					movl	%2, %%ebx \n		\
45-					movl	%1, %%eax \n		\
46-					int	$0x80 \n		\
47-					movl	%%eax, %0"		\
48-				: "=a" (__rval)				\
49-				: "a" (cnum), "b" (fd), "c" (dirp), "d" (count)\
50-				: "memory"				\
51-				);					\
52-				__rval;					\
53-		    	})
54-#else
55-	#define GETDENTS_ASM() 0
56-#endif /* __i386__ */
57+/*
58+ * The dirent struct that the C library exports is not the same
59+ * as the kernel ABI, so we can't include dirent.h and use the
60+ * dirent struct from there.  Further, since the Linux headers
61+ * don't export their vision of the struct either, we have to
62+ * declare our own here.  Wheeeeee.
63+ */
64+
65+struct linux_dirent {
66+	unsigned long   d_ino;
67+	unsigned long   d_off;
68+	unsigned short  d_reclen;
69+	char            d_name[];
70+};
71+
72+static inline int
73+getdents(unsigned int fd, struct dirent *dirp, unsigned int count)
74+{
75+	union {
76+		struct linux_dirent *dirp;
77+		char *buf;
78+	} ptrs;
79+	char buf[count];
80+	long ret;
81+	unsigned int i;
82+
83+	ptrs.buf = buf;
84+	ret = syscall(SYS_getdents, fd, buf, count);
85+	if (ret < 0)
86+		return ret;
87+
88+#define kdircpy(field) memcpy(&dirp[i].field, &ptrs.dirp->field, sizeof(dirp[i].field))
89+
90+	i = 0;
91+	while (i < count && i < ret) {
92+		unsigned long reclen;
93+
94+		kdircpy(d_ino);
95+		kdircpy(d_reclen);
96+		reclen = dirp[i].d_reclen;
97+		kdircpy(d_off);
98+		strcpy(dirp[i].d_name, ptrs.dirp->d_name);
99+
100+		ptrs.buf += reclen;
101+
102+		i += reclen;
103+	}
104+
105+	return ret;
106+}
107
108 #endif /* getdents.h */
109diff --git a/testcases/kernel/syscalls/getdents/getdents01.c b/testcases/kernel/syscalls/getdents/getdents01.c
110index 266a9c0..8afb08a 100644
111--- a/testcases/kernel/syscalls/getdents/getdents01.c
112+++ b/testcases/kernel/syscalls/getdents/getdents01.c
113@@ -81,17 +81,6 @@ int main(int ac, char **av)
114 	char *dir_name = NULL;
115 	struct dirent *dirp;
116
117-	/*
118-	 * Here's a case where invoking the system call directly
119-	 * doesn't seem to work.  getdents.h has an assembly
120-	 * macro to do the job.
121-	 *
122-	 * equivalent to  - getdents(fd, dirp, count);
123-	 * if we could call getdents that way.
124-	 */
125-
126-#define getdents(arg1, arg2, arg3) syscall(__NR_getdents, arg1, arg2, arg3)
127-
128 	if ((msg = parse_opts(ac, av, NULL, NULL)) != NULL)
129 		tst_brkm(TBROK, NULL, "OPTION PARSING ERROR - %s", msg);
130
131@@ -120,17 +109,14 @@ int main(int ac, char **av)
132 		rval = getdents(fd, dirp, count);
133 		if (rval < 0) {
134
135-			rval *= -1;
136-			TEST_ERROR_LOG(rval);
137+			TEST_ERROR_LOG(errno);
138
139-			tst_resm(TFAIL, "%s call failed - errno = %d "
140-				 ": %s", TCID, rval, strerror(rval));
141+			tst_resm(TFAIL|TERRNO, "getdents failed unexpectedly");
142 			continue;
143 		}
144
145 		if (rval == 0) {
146-			tst_resm(TFAIL, "%s call failed - returned "
147-				 "end of directory", TCID);
148+			tst_resm(TFAIL, "getdents failed - returned end of directory");
149 			continue;
150 		}
151
152diff --git a/testcases/kernel/syscalls/getdents/getdents02.c b/testcases/kernel/syscalls/getdents/getdents02.c
153index 46d1133..af826d1 100644
154--- a/testcases/kernel/syscalls/getdents/getdents02.c
155+++ b/testcases/kernel/syscalls/getdents/getdents02.c
156@@ -69,21 +69,12 @@ int TST_TOTAL = 1;
157
158 int exp_enos[] = { EBADF, 0 };	/* 0 terminated list of expected errnos */
159
160-#ifndef __i386__
161-int main(void)
162-{
163-	tst_brkm(TCONF, NULL, "this test will only run on i386");
164-	tst_exit();
165-}
166-#else
167-
168 int main(int ac, char **av)
169 {
170 	int lc;
171 	char *msg;
172 	int rval, fd;
173 	int count;
174-	const int cnum = __NR_getdents;
175 	size_t size = 0;
176 	char *dir_name = NULL;
177 	struct dirent *dirp;
178@@ -109,25 +100,15 @@ int main(int ac, char **av)
179
180 		fd = -5;
181
182-		/*
183-		 * here's a case where invoking the system call directly
184-		 * doesn't seem to work.  getdents.h has an assembly
185-		 * macro to do the job.
186-		 *
187-		 * equivalent to  - getdents(fd, dirp, count);
188-		 * if we could call getdents that way.
189-		 */
190-
191-		rval = GETDENTS_ASM();
192+		rval = getdents(fd, dirp, count);
193
194 		/*
195 		 * Hopefully we get an error due to the bad file descriptor.
196 		 */
197 		if (rval < 0) {
198-			rval *= -1;
199-			TEST_ERROR_LOG(rval);
200+			TEST_ERROR_LOG(errno);
201
202-			switch (rval) {
203+			switch (errno) {
204 			case EBADF:
205 				tst_resm(TPASS,
206 				    "failed as expected with EBADF");
207@@ -170,5 +151,3 @@ void cleanup(void)
208
209 	tst_rmdir();
210 }
211-
212-#endif /* __i386__ */
213diff --git a/testcases/kernel/syscalls/getdents/getdents03.c b/testcases/kernel/syscalls/getdents/getdents03.c
214index 8582346..ffd137f 100644
215--- a/testcases/kernel/syscalls/getdents/getdents03.c
216+++ b/testcases/kernel/syscalls/getdents/getdents03.c
217@@ -72,21 +72,12 @@ int TST_TOTAL = 1;
218
219 int exp_enos[] = { EINVAL, 0 };	/* 0 terminated list of expected errnos */
220
221-#ifndef __i386__
222-int main(void)
223-{
224-	tst_brkm(TCONF, NULL, "this test will only run on i386");
225-	tst_exit();
226-}
227-#else
228-
229 int main(int ac, char **av)
230 {
231 	int lc;
232 	char *msg;
233 	int rval, fd;
234 	int count;
235-	const int cnum = __NR_getdents;
236 	size_t size = 0;
237 	char *dir_name = NULL;
238 	struct dirent *dirp;
239@@ -114,26 +105,16 @@ int main(int ac, char **av)
240 		if ((fd = open(dir_name, O_RDONLY)) == -1)
241 			tst_brkm(TBROK, cleanup, "open of directory failed");
242
243-		/*
244-		 * here's a case where invoking the system call directly
245-		 * doesn't seem to work.  getdents.h has an assembly
246-		 * macro to do the job.
247-		 *
248-		 * equivalent to  - getdents(fd, dirp, count)
249-		 * if we could call getdents that way.
250-		 */
251-
252-		rval = GETDENTS_ASM();
253+		rval = getdents(fd, dirp, count);
254
255 		/*
256 		 * Hopefully we get an error due to the small buffer.
257 		 */
258
259 		if (rval < 0) {
260-			rval *= -1;
261-			TEST_ERROR_LOG(rval);
262+			TEST_ERROR_LOG(errno);
263
264-			switch (rval) {
265+			switch (errno) {
266 			case EINVAL:
267 				tst_resm(TPASS,
268 				    "getdents failed with EINVAL as expected");
269@@ -181,5 +162,3 @@ void cleanup(void)
270
271 	tst_rmdir();
272 }
273-
274-#endif /* __i386__ */
275diff --git a/testcases/kernel/syscalls/getdents/getdents04.c b/testcases/kernel/syscalls/getdents/getdents04.c
276index 5dd1634..141d3da 100644
277--- a/testcases/kernel/syscalls/getdents/getdents04.c
278+++ b/testcases/kernel/syscalls/getdents/getdents04.c
279@@ -73,20 +73,11 @@ int TST_TOTAL = 1;
280
281 int exp_enos[] = { ENOTDIR, 0 };	/* 0 terminated list of expected errnos */
282
283-#ifndef __i386__
284-int main(void)
285-{
286-	tst_brkm(TCONF, NULL, "this test will only run on i386");
287-	tst_exit();
288-}
289-#else
290-
291 int main(int ac, char **av)
292 {
293 	int lc;
294 	char *msg;
295 	int count, rval, fd;
296-	const int cnum = 141;
297 	size_t size = 0;
298 	char *dir_name = NULL;
299 	struct dirent *dirp;
300@@ -131,15 +122,7 @@ int main(int ac, char **av)
301 		if (S_ISDIR(sbuf->st_mode))
302 			tst_brkm(TBROK, cleanup, "fd is a directory");
303
304-		/*
305-		 * here's a case where invoking the system call directly
306-		 * doesn't seem to work.  getdents.h has an assembly
307-		 * macro to do the job.
308-		 *
309-		 * equivalent to getdents(fd, dirp, count);
310-		 */
311-
312-		rval = GETDENTS_ASM();
313+		rval = getdents(fd, dirp, count);
314
315 		/*
316 		 * Calling with a non directory file descriptor should give
317@@ -147,10 +130,9 @@ int main(int ac, char **av)
318 		 */
319
320 		if (rval < 0) {
321-			rval *= -1;
322-			TEST_ERROR_LOG(rval);
323+			TEST_ERROR_LOG(errno);
324
325-			switch (rval) {
326+			switch (errno) {
327 			case ENOTDIR:
328 				tst_resm(TPASS,
329 				    "getdents failed as expected with ENOTDIR");
330@@ -198,5 +180,3 @@ void cleanup(void)
331
332 	tst_rmdir();
333 }
334-
335-#endif /* __i386__ */
336--
3371.7.8.4
338
339