| From ec19461cecdac81c48bbbe4783624167754349a2 Mon Sep 17 00:00:00 2001 |
| From: Mike Frysinger <vapier@gentoo.org> |
| Date: Thu, 8 Mar 2012 17:40:52 -0500 |
| Subject: [PATCH] getdents: rewrite syscall handling completely |
| |
| The inline asm has many problems, but rather than attempt to fix |
| them, just use syscall() for everyone. This allows us to drop the |
| i386-specific checks and have the tests run on all arches. |
| |
| Further, add a layer between the kernel and the dirent struct that |
| the tests uses. The kernel packs the results, so we need to expand |
| the raw buffer returned by the kernel into the userland structs we |
| pass around. |
| |
| Signed-off-by: Mike Frysinger <vapier@gentoo.org> |
| --- |
| testcases/kernel/syscalls/getdents/getdents.h | 73 +++++++++++++++++------ |
| testcases/kernel/syscalls/getdents/getdents01.c | 20 +----- |
| testcases/kernel/syscalls/getdents/getdents02.c | 27 +-------- |
| testcases/kernel/syscalls/getdents/getdents03.c | 27 +-------- |
| testcases/kernel/syscalls/getdents/getdents04.c | 26 +------- |
| 5 files changed, 67 insertions(+), 106 deletions(-) |
| |
| diff --git a/testcases/kernel/syscalls/getdents/getdents.h b/testcases/kernel/syscalls/getdents/getdents.h |
| index 3ab3fd2..a5ddfea 100644 |
| --- a/testcases/kernel/syscalls/getdents/getdents.h |
| +++ b/testcases/kernel/syscalls/getdents/getdents.h |
| @@ -23,25 +23,62 @@ |
| |
| #ifndef __GETDENTS_H |
| #define __GETDENTS_H 1 |
| + |
| +#include <dirent.h> |
| +#include <stdio.h> |
| +#include <string.h> |
| +#include <unistd.h> |
| #include <sys/syscall.h> |
| |
| -#ifdef __i386__ |
| - #define GETDENTS_ASM() ({ int __rval; \ |
| - __asm__ __volatile__(" \ |
| - movl %4, %%edx \n \ |
| - movl %3, %%ecx \n \ |
| - movl %2, %%ebx \n \ |
| - movl %1, %%eax \n \ |
| - int $0x80 \n \ |
| - movl %%eax, %0" \ |
| - : "=a" (__rval) \ |
| - : "a" (cnum), "b" (fd), "c" (dirp), "d" (count)\ |
| - : "memory" \ |
| - ); \ |
| - __rval; \ |
| - }) |
| -#else |
| - #define GETDENTS_ASM() 0 |
| -#endif /* __i386__ */ |
| +/* |
| + * The dirent struct that the C library exports is not the same |
| + * as the kernel ABI, so we can't include dirent.h and use the |
| + * dirent struct from there. Further, since the Linux headers |
| + * don't export their vision of the struct either, we have to |
| + * declare our own here. Wheeeeee. |
| + */ |
| + |
| +struct linux_dirent { |
| + unsigned long d_ino; |
| + unsigned long d_off; |
| + unsigned short d_reclen; |
| + char d_name[]; |
| +}; |
| + |
| +static inline int |
| +getdents(unsigned int fd, struct dirent *dirp, unsigned int count) |
| +{ |
| + union { |
| + struct linux_dirent *dirp; |
| + char *buf; |
| + } ptrs; |
| + char buf[count]; |
| + long ret; |
| + unsigned int i; |
| + |
| + ptrs.buf = buf; |
| + ret = syscall(SYS_getdents, fd, buf, count); |
| + if (ret < 0) |
| + return ret; |
| + |
| +#define kdircpy(field) memcpy(&dirp[i].field, &ptrs.dirp->field, sizeof(dirp[i].field)) |
| + |
| + i = 0; |
| + while (i < count && i < ret) { |
| + unsigned long reclen; |
| + |
| + kdircpy(d_ino); |
| + kdircpy(d_reclen); |
| + reclen = dirp[i].d_reclen; |
| + kdircpy(d_off); |
| + strcpy(dirp[i].d_name, ptrs.dirp->d_name); |
| + |
| + ptrs.buf += reclen; |
| + |
| + i += reclen; |
| + } |
| + |
| + return ret; |
| +} |
| |
| #endif /* getdents.h */ |
| diff --git a/testcases/kernel/syscalls/getdents/getdents01.c b/testcases/kernel/syscalls/getdents/getdents01.c |
| index 266a9c0..8afb08a 100644 |
| --- a/testcases/kernel/syscalls/getdents/getdents01.c |
| +++ b/testcases/kernel/syscalls/getdents/getdents01.c |
| @@ -81,17 +81,6 @@ int main(int ac, char **av) |
| char *dir_name = NULL; |
| struct dirent *dirp; |
| |
| - /* |
| - * Here's a case where invoking the system call directly |
| - * doesn't seem to work. getdents.h has an assembly |
| - * macro to do the job. |
| - * |
| - * equivalent to - getdents(fd, dirp, count); |
| - * if we could call getdents that way. |
| - */ |
| - |
| -#define getdents(arg1, arg2, arg3) syscall(__NR_getdents, arg1, arg2, arg3) |
| - |
| if ((msg = parse_opts(ac, av, NULL, NULL)) != NULL) |
| tst_brkm(TBROK, NULL, "OPTION PARSING ERROR - %s", msg); |
| |
| @@ -120,17 +109,14 @@ int main(int ac, char **av) |
| rval = getdents(fd, dirp, count); |
| if (rval < 0) { |
| |
| - rval *= -1; |
| - TEST_ERROR_LOG(rval); |
| + TEST_ERROR_LOG(errno); |
| |
| - tst_resm(TFAIL, "%s call failed - errno = %d " |
| - ": %s", TCID, rval, strerror(rval)); |
| + tst_resm(TFAIL|TERRNO, "getdents failed unexpectedly"); |
| continue; |
| } |
| |
| if (rval == 0) { |
| - tst_resm(TFAIL, "%s call failed - returned " |
| - "end of directory", TCID); |
| + tst_resm(TFAIL, "getdents failed - returned end of directory"); |
| continue; |
| } |
| |
| diff --git a/testcases/kernel/syscalls/getdents/getdents02.c b/testcases/kernel/syscalls/getdents/getdents02.c |
| index 46d1133..af826d1 100644 |
| --- a/testcases/kernel/syscalls/getdents/getdents02.c |
| +++ b/testcases/kernel/syscalls/getdents/getdents02.c |
| @@ -69,21 +69,12 @@ int TST_TOTAL = 1; |
| |
| int exp_enos[] = { EBADF, 0 }; /* 0 terminated list of expected errnos */ |
| |
| -#ifndef __i386__ |
| -int main(void) |
| -{ |
| - tst_brkm(TCONF, NULL, "this test will only run on i386"); |
| - tst_exit(); |
| -} |
| -#else |
| - |
| int main(int ac, char **av) |
| { |
| int lc; |
| char *msg; |
| int rval, fd; |
| int count; |
| - const int cnum = __NR_getdents; |
| size_t size = 0; |
| char *dir_name = NULL; |
| struct dirent *dirp; |
| @@ -109,25 +100,15 @@ int main(int ac, char **av) |
| |
| fd = -5; |
| |
| - /* |
| - * here's a case where invoking the system call directly |
| - * doesn't seem to work. getdents.h has an assembly |
| - * macro to do the job. |
| - * |
| - * equivalent to - getdents(fd, dirp, count); |
| - * if we could call getdents that way. |
| - */ |
| - |
| - rval = GETDENTS_ASM(); |
| + rval = getdents(fd, dirp, count); |
| |
| /* |
| * Hopefully we get an error due to the bad file descriptor. |
| */ |
| if (rval < 0) { |
| - rval *= -1; |
| - TEST_ERROR_LOG(rval); |
| + TEST_ERROR_LOG(errno); |
| |
| - switch (rval) { |
| + switch (errno) { |
| case EBADF: |
| tst_resm(TPASS, |
| "failed as expected with EBADF"); |
| @@ -170,5 +151,3 @@ void cleanup(void) |
| |
| tst_rmdir(); |
| } |
| - |
| -#endif /* __i386__ */ |
| diff --git a/testcases/kernel/syscalls/getdents/getdents03.c b/testcases/kernel/syscalls/getdents/getdents03.c |
| index 8582346..ffd137f 100644 |
| --- a/testcases/kernel/syscalls/getdents/getdents03.c |
| +++ b/testcases/kernel/syscalls/getdents/getdents03.c |
| @@ -72,21 +72,12 @@ int TST_TOTAL = 1; |
| |
| int exp_enos[] = { EINVAL, 0 }; /* 0 terminated list of expected errnos */ |
| |
| -#ifndef __i386__ |
| -int main(void) |
| -{ |
| - tst_brkm(TCONF, NULL, "this test will only run on i386"); |
| - tst_exit(); |
| -} |
| -#else |
| - |
| int main(int ac, char **av) |
| { |
| int lc; |
| char *msg; |
| int rval, fd; |
| int count; |
| - const int cnum = __NR_getdents; |
| size_t size = 0; |
| char *dir_name = NULL; |
| struct dirent *dirp; |
| @@ -114,26 +105,16 @@ int main(int ac, char **av) |
| if ((fd = open(dir_name, O_RDONLY)) == -1) |
| tst_brkm(TBROK, cleanup, "open of directory failed"); |
| |
| - /* |
| - * here's a case where invoking the system call directly |
| - * doesn't seem to work. getdents.h has an assembly |
| - * macro to do the job. |
| - * |
| - * equivalent to - getdents(fd, dirp, count) |
| - * if we could call getdents that way. |
| - */ |
| - |
| - rval = GETDENTS_ASM(); |
| + rval = getdents(fd, dirp, count); |
| |
| /* |
| * Hopefully we get an error due to the small buffer. |
| */ |
| |
| if (rval < 0) { |
| - rval *= -1; |
| - TEST_ERROR_LOG(rval); |
| + TEST_ERROR_LOG(errno); |
| |
| - switch (rval) { |
| + switch (errno) { |
| case EINVAL: |
| tst_resm(TPASS, |
| "getdents failed with EINVAL as expected"); |
| @@ -181,5 +162,3 @@ void cleanup(void) |
| |
| tst_rmdir(); |
| } |
| - |
| -#endif /* __i386__ */ |
| diff --git a/testcases/kernel/syscalls/getdents/getdents04.c b/testcases/kernel/syscalls/getdents/getdents04.c |
| index 5dd1634..141d3da 100644 |
| --- a/testcases/kernel/syscalls/getdents/getdents04.c |
| +++ b/testcases/kernel/syscalls/getdents/getdents04.c |
| @@ -73,20 +73,11 @@ int TST_TOTAL = 1; |
| |
| int exp_enos[] = { ENOTDIR, 0 }; /* 0 terminated list of expected errnos */ |
| |
| -#ifndef __i386__ |
| -int main(void) |
| -{ |
| - tst_brkm(TCONF, NULL, "this test will only run on i386"); |
| - tst_exit(); |
| -} |
| -#else |
| - |
| int main(int ac, char **av) |
| { |
| int lc; |
| char *msg; |
| int count, rval, fd; |
| - const int cnum = 141; |
| size_t size = 0; |
| char *dir_name = NULL; |
| struct dirent *dirp; |
| @@ -131,15 +122,7 @@ int main(int ac, char **av) |
| if (S_ISDIR(sbuf->st_mode)) |
| tst_brkm(TBROK, cleanup, "fd is a directory"); |
| |
| - /* |
| - * here's a case where invoking the system call directly |
| - * doesn't seem to work. getdents.h has an assembly |
| - * macro to do the job. |
| - * |
| - * equivalent to getdents(fd, dirp, count); |
| - */ |
| - |
| - rval = GETDENTS_ASM(); |
| + rval = getdents(fd, dirp, count); |
| |
| /* |
| * Calling with a non directory file descriptor should give |
| @@ -147,10 +130,9 @@ int main(int ac, char **av) |
| */ |
| |
| if (rval < 0) { |
| - rval *= -1; |
| - TEST_ERROR_LOG(rval); |
| + TEST_ERROR_LOG(errno); |
| |
| - switch (rval) { |
| + switch (errno) { |
| case ENOTDIR: |
| tst_resm(TPASS, |
| "getdents failed as expected with ENOTDIR"); |
| @@ -198,5 +180,3 @@ void cleanup(void) |
| |
| tst_rmdir(); |
| } |
| - |
| -#endif /* __i386__ */ |
| -- |
| 1.7.8.4 |
| |