Mike Frysinger | 6d86aec | 2012-03-09 11:09:12 -0500 | [diff] [blame] | 1 | From ec19461cecdac81c48bbbe4783624167754349a2 Mon Sep 17 00:00:00 2001 |
| 2 | From: Mike Frysinger <vapier@gentoo.org> |
| 3 | Date: Thu, 8 Mar 2012 17:40:52 -0500 |
| 4 | Subject: [PATCH] getdents: rewrite syscall handling completely |
| 5 | |
| 6 | The inline asm has many problems, but rather than attempt to fix |
| 7 | them, just use syscall() for everyone. This allows us to drop the |
| 8 | i386-specific checks and have the tests run on all arches. |
| 9 | |
| 10 | Further, add a layer between the kernel and the dirent struct that |
| 11 | the tests uses. The kernel packs the results, so we need to expand |
| 12 | the raw buffer returned by the kernel into the userland structs we |
| 13 | pass around. |
| 14 | |
| 15 | Signed-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 | |
| 24 | diff --git a/testcases/kernel/syscalls/getdents/getdents.h b/testcases/kernel/syscalls/getdents/getdents.h |
| 25 | index 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 */ |
| 109 | diff --git a/testcases/kernel/syscalls/getdents/getdents01.c b/testcases/kernel/syscalls/getdents/getdents01.c |
| 110 | index 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 | |
| 152 | diff --git a/testcases/kernel/syscalls/getdents/getdents02.c b/testcases/kernel/syscalls/getdents/getdents02.c |
| 153 | index 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__ */ |
| 213 | diff --git a/testcases/kernel/syscalls/getdents/getdents03.c b/testcases/kernel/syscalls/getdents/getdents03.c |
| 214 | index 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__ */ |
| 275 | diff --git a/testcases/kernel/syscalls/getdents/getdents04.c b/testcases/kernel/syscalls/getdents/getdents04.c |
| 276 | index 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 | -- |
| 337 | 1.7.8.4 |
| 338 | |