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