diff options
author | Aurelien Jarno <aurelien@aurel32.net> | 2011-02-09 19:53:36 +0100 |
---|---|---|
committer | Aurelien Jarno <aurelien@aurel32.net> | 2011-02-09 19:53:36 +0100 |
commit | 1c0de9fa509c48aeeb6ef2465307d603abc9ee4e (patch) | |
tree | 69353b33fdb34e4acb5fc081d2e14bacef37cd4c | |
parent | 923e65097d2101a78f716f7932f4291315d88bbf (diff) | |
parent | 898b1bebf97e5f973624a4e513da45dffefdb9b7 (diff) |
Merge branch 'linux-user-for-upstream' of git://gitorious.org/qemu-maemo/qemu
* 'linux-user-for-upstream' of git://gitorious.org/qemu-maemo/qemu:
linux-user: fix for loopmount ioctl
linux-user: fix build errors for mmap2-only ports
user: speed up init_paths a bit
linux-user: implement sched_{g,s}etaffinity
linux-user/FLAT: allow targets to override FLAT processing
linux-user/FLAT: fix auto-stack sizing
linux-user: decode MAP_{UNINITIALIZED,EXECUTABLE} in strace
linux-user: add ppoll syscall support
linux-user/elfload: add FDPIC support
linux-user: fix sizeof handling for getsockopt
linux-user: Fix possible realloc memory leak
linux-user: Add support for -version option
-rw-r--r-- | Makefile.target | 2 | ||||
-rw-r--r-- | elf.h | 19 | ||||
-rw-r--r-- | linux-user/elfload.c | 79 | ||||
-rw-r--r-- | linux-user/flatload.c | 38 | ||||
-rw-r--r-- | linux-user/ioctls.h | 2 | ||||
-rw-r--r-- | linux-user/main.c | 17 | ||||
-rw-r--r-- | linux-user/qemu.h | 7 | ||||
-rw-r--r-- | linux-user/strace.c | 6 | ||||
-rw-r--r-- | linux-user/syscall.c | 128 | ||||
-rw-r--r-- | linux-user/syscall_defs.h | 1 | ||||
-rw-r--r-- | linux-user/target_flat.h | 10 | ||||
-rw-r--r-- | path.c | 28 |
12 files changed, 298 insertions, 39 deletions
diff --git a/Makefile.target b/Makefile.target index b0ba95f76e..48e6c00a5d 100644 --- a/Makefile.target +++ b/Makefile.target @@ -107,7 +107,7 @@ ifdef CONFIG_LINUX_USER $(call set-vpath, $(SRC_PATH)/linux-user:$(SRC_PATH)/linux-user/$(TARGET_ABI_DIR)) -QEMU_CFLAGS+=-I$(SRC_PATH)/linux-user -I$(SRC_PATH)/linux-user/$(TARGET_ABI_DIR) +QEMU_CFLAGS+=-I$(SRC_PATH)/linux-user/$(TARGET_ABI_DIR) -I$(SRC_PATH)/linux-user obj-y = main.o syscall.o strace.o mmap.o signal.o thunk.o \ elfload.o linuxload.o uaccess.o gdbstub.o cpu-uname.o \ qemu-malloc.o $(oslib-obj-y) @@ -1191,6 +1191,25 @@ typedef struct elf64_note { Elf64_Word n_type; /* Content type */ } Elf64_Nhdr; + +/* This data structure represents a PT_LOAD segment. */ +struct elf32_fdpic_loadseg { + /* Core address to which the segment is mapped. */ + Elf32_Addr addr; + /* VMA recorded in the program header. */ + Elf32_Addr p_vaddr; + /* Size of this segment in memory. */ + Elf32_Word p_memsz; +}; +struct elf32_fdpic_loadmap { + /* Protocol version number, must be zero. */ + Elf32_Half version; + /* Number of segments in this map. */ + Elf32_Half nsegs; + /* The actual memory map. */ + struct elf32_fdpic_loadseg segs[/*nsegs*/]; +}; + #ifdef ELF_CLASS #if ELF_CLASS == ELFCLASS32 diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 33d776de41..2de83e4bfb 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -1075,6 +1075,33 @@ static void zero_bss(abi_ulong elf_bss, abi_ulong last_bss, int prot) } } +#ifdef CONFIG_USE_FDPIC +static abi_ulong loader_build_fdpic_loadmap(struct image_info *info, abi_ulong sp) +{ + uint16_t n; + struct elf32_fdpic_loadseg *loadsegs = info->loadsegs; + + /* elf32_fdpic_loadseg */ + n = info->nsegs; + while (n--) { + sp -= 12; + put_user_u32(loadsegs[n].addr, sp+0); + put_user_u32(loadsegs[n].p_vaddr, sp+4); + put_user_u32(loadsegs[n].p_memsz, sp+8); + } + + /* elf32_fdpic_loadmap */ + sp -= 4; + put_user_u16(0, sp+0); /* version */ + put_user_u16(info->nsegs, sp+2); /* nsegs */ + + info->personality = PER_LINUX_FDPIC; + info->loadmap_addr = sp; + + return sp; +} +#endif + static abi_ulong create_elf_tables(abi_ulong p, int argc, int envc, struct elfhdr *exec, struct image_info *info, @@ -1087,6 +1114,21 @@ static abi_ulong create_elf_tables(abi_ulong p, int argc, int envc, const int n = sizeof(elf_addr_t); sp = p; + +#ifdef CONFIG_USE_FDPIC + /* Needs to be before we load the env/argc/... */ + if (elf_is_fdpic(exec)) { + /* Need 4 byte alignment for these structs */ + sp &= ~3; + sp = loader_build_fdpic_loadmap(info, sp); + info->other_info = interp_info; + if (interp_info) { + interp_info->other_info = info; + sp = loader_build_fdpic_loadmap(interp_info, sp); + } + } +#endif + u_platform = 0; k_platform = ELF_PLATFORM; if (k_platform) { @@ -1197,6 +1239,11 @@ static void load_elf_image(const char *image_name, int image_fd, } bswap_phdr(phdr, ehdr->e_phnum); +#ifdef CONFIG_USE_FDPIC + info->nsegs = 0; + info->pt_dynamic_addr = 0; +#endif + /* Find the maximum size of the image and allocate an appropriate amount of memory to handle that. */ loaddr = -1, hiaddr = 0; @@ -1210,6 +1257,9 @@ static void load_elf_image(const char *image_name, int image_fd, if (a > hiaddr) { hiaddr = a; } +#ifdef CONFIG_USE_FDPIC + ++info->nsegs; +#endif } } @@ -1290,6 +1340,27 @@ static void load_elf_image(const char *image_name, int image_fd, } load_bias = load_addr - loaddr; +#ifdef CONFIG_USE_FDPIC + { + struct elf32_fdpic_loadseg *loadsegs = info->loadsegs = + qemu_malloc(sizeof(*loadsegs) * info->nsegs); + + for (i = 0; i < ehdr->e_phnum; ++i) { + switch (phdr[i].p_type) { + case PT_DYNAMIC: + info->pt_dynamic_addr = phdr[i].p_vaddr + load_bias; + break; + case PT_LOAD: + loadsegs->addr = phdr[i].p_vaddr + load_bias; + loadsegs->p_vaddr = phdr[i].p_vaddr; + loadsegs->p_memsz = phdr[i].p_memsz; + ++loadsegs; + break; + } + } + } +#endif + info->load_bias = load_bias; info->load_addr = load_addr; info->entry = ehdr->e_entry + load_bias; @@ -1481,7 +1552,7 @@ static void load_symbols(struct elfhdr *hdr, int fd, abi_ulong load_bias) struct elf_shdr *shdr; char *strings; struct syminfo *s; - struct elf_sym *syms; + struct elf_sym *syms, *new_syms; shnum = hdr->e_shnum; i = shnum * sizeof(struct elf_shdr); @@ -1550,12 +1621,14 @@ static void load_symbols(struct elfhdr *hdr, int fd, abi_ulong load_bias) that we threw away. Whether or not this has any effect on the memory allocation depends on the malloc implementation and how many symbols we managed to discard. */ - syms = realloc(syms, nsyms * sizeof(*syms)); - if (syms == NULL) { + new_syms = realloc(syms, nsyms * sizeof(*syms)); + if (new_syms == NULL) { free(s); + free(syms); free(strings); return; } + syms = new_syms; qsort(syms, nsyms, sizeof(*syms), symcmp); diff --git a/linux-user/flatload.c b/linux-user/flatload.c index 8f9f4a5fcc..cd7af7cdff 100644 --- a/linux-user/flatload.c +++ b/linux-user/flatload.c @@ -41,6 +41,8 @@ #include "qemu.h" #include "flat.h" +#define ntohl(x) be32_to_cpu(x) +#include <target_flat.h> //#define DEBUG @@ -50,14 +52,6 @@ #define DBG_FLT(...) #endif -#define flat_reloc_valid(reloc, size) ((reloc) <= (size)) -#define flat_old_ram_flag(flag) (flag) -#ifdef TARGET_WORDS_BIGENDIAN -#define flat_get_relocate_addr(relval) (relval) -#else -#define flat_get_relocate_addr(relval) bswap32(relval) -#endif - #define RELOC_FAILED 0xff00ff01 /* Relocation incorrect somewhere */ #define UNLOADED_LIB 0x7ff000ff /* Placeholder for unused library */ @@ -78,8 +72,6 @@ static int load_flat_shared_library(int id, struct lib_info *p); struct linux_binprm; -#define ntohl(x) be32_to_cpu(x) - /****************************************************************************/ /* * create_flat_tables() parses the env- and arg-strings in new user @@ -625,6 +617,7 @@ static int load_flat_file(struct linux_binprm * bprm, * __start to address 4 so that is okay). */ if (rev > OLD_FLAT_VERSION) { + abi_ulong persistent = 0; for (i = 0; i < relocs; i++) { abi_ulong addr, relval; @@ -633,6 +626,9 @@ static int load_flat_file(struct linux_binprm * bprm, relocated first). */ if (get_user_ual(relval, reloc + i * sizeof(abi_ulong))) return -EFAULT; + relval = ntohl(relval); + if (flat_set_persistent(relval, &persistent)) + continue; addr = flat_get_relocate_addr(relval); rp = calc_reloc(addr, libinfo, id, 1); if (rp == RELOC_FAILED) @@ -641,22 +637,20 @@ static int load_flat_file(struct linux_binprm * bprm, /* Get the pointer's value. */ if (get_user_ual(addr, rp)) return -EFAULT; + addr = flat_get_addr_from_rp(rp, relval, flags, &persistent); if (addr != 0) { /* * Do the relocation. PIC relocs in the data section are * already in target order */ - -#ifndef TARGET_WORDS_BIGENDIAN if ((flags & FLAT_FLAG_GOTPIC) == 0) - addr = bswap32(addr); -#endif + addr = ntohl(addr); addr = calc_reloc(addr, libinfo, id, 0); if (addr == RELOC_FAILED) return -ENOEXEC; /* Write back the relocated pointer. */ - if (put_user_ual(addr, rp)) + if (flat_put_addr_at_rp(rp, addr, relval)) return -EFAULT; } } @@ -733,8 +727,15 @@ int load_flt_binary(struct linux_binprm * bprm, struct target_pt_regs * regs, * pedantic and include space for the argv/envp array as it may have * a lot of entries. */ -#define TOP_OF_ARGS (TARGET_PAGE_SIZE * MAX_ARG_PAGES - sizeof(void *)) - stack_len = TOP_OF_ARGS - bprm->p; /* the strings */ + stack_len = 0; + for (i = 0; i < bprm->argc; ++i) { + /* the argv strings */ + stack_len += strlen(bprm->argv[i]); + } + for (i = 0; i < bprm->envc; ++i) { + /* the envp strings */ + stack_len += strlen(bprm->envp[i]); + } stack_len += (bprm->argc + 1) * 4; /* the argv array */ stack_len += (bprm->envc + 1) * 4; /* the envp array */ @@ -775,7 +776,8 @@ int load_flt_binary(struct linux_binprm * bprm, struct target_pt_regs * regs, stack_len *= sizeof(abi_ulong); if ((sp + stack_len) & 15) sp -= 16 - ((sp + stack_len) & 15); - sp = loader_build_argptr(bprm->envc, bprm->argc, sp, p, 1); + sp = loader_build_argptr(bprm->envc, bprm->argc, sp, p, + flat_argvp_envp_on_stack()); /* Fake some return addresses to ensure the call chain will * initialise library in order for us. We are required to call diff --git a/linux-user/ioctls.h b/linux-user/ioctls.h index acff781081..526aaa2a76 100644 --- a/linux-user/ioctls.h +++ b/linux-user/ioctls.h @@ -312,10 +312,8 @@ IOCTL(LOOP_CLR_FD, 0, TYPE_INT) IOCTL(LOOP_SET_STATUS, IOC_W, MK_PTR(MK_STRUCT(STRUCT_loop_info))) IOCTL(LOOP_GET_STATUS, IOC_W, MK_PTR(MK_STRUCT(STRUCT_loop_info))) -#if 0 /* These have some problems - not fully tested */ IOCTL(LOOP_SET_STATUS64, IOC_W, MK_PTR(MK_STRUCT(STRUCT_loop_info64))) IOCTL(LOOP_GET_STATUS64, IOC_W, MK_PTR(MK_STRUCT(STRUCT_loop_info64))) -#endif IOCTL(LOOP_CHANGE_FD, 0, TYPE_INT) IOCTL(MTIOCTOP, IOC_W, MK_PTR(MK_STRUCT(STRUCT_mtop))) diff --git a/linux-user/main.c b/linux-user/main.c index 0d627d68dd..e651bfdad8 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -2624,14 +2624,21 @@ void cpu_loop (CPUState *env) } #endif /* TARGET_ALPHA */ +static void version(void) +{ + printf("qemu-" TARGET_ARCH " version " QEMU_VERSION QEMU_PKGVERSION + ", Copyright (c) 2003-2008 Fabrice Bellard\n"); +} + static void usage(void) { - printf("qemu-" TARGET_ARCH " version " QEMU_VERSION QEMU_PKGVERSION ", Copyright (c) 2003-2008 Fabrice Bellard\n" - "usage: qemu-" TARGET_ARCH " [options] program [arguments...]\n" + version(); + printf("usage: qemu-" TARGET_ARCH " [options] program [arguments...]\n" "Linux CPU emulator (compiled for %s emulation)\n" "\n" "Standard options:\n" "-h print this help\n" + "-version display version information and exit\n" "-g port wait gdb connection to port\n" "-L path set the elf interpreter prefix (default=%s)\n" "-s size set the stack size in bytes (default=%ld)\n" @@ -2886,8 +2893,10 @@ int main(int argc, char **argv, char **envp) singlestep = 1; } else if (!strcmp(r, "strace")) { do_strace = 1; - } else - { + } else if (!strcmp(r, "version")) { + version(); + exit(0); + } else { usage(); } } diff --git a/linux-user/qemu.h b/linux-user/qemu.h index 32de2413f8..250814d9f7 100644 --- a/linux-user/qemu.h +++ b/linux-user/qemu.h @@ -51,6 +51,13 @@ struct image_info { abi_ulong arg_start; abi_ulong arg_end; int personality; +#ifdef CONFIG_USE_FDPIC + abi_ulong loadmap_addr; + uint16_t nsegs; + void *loadsegs; + abi_ulong pt_dynamic_addr; + struct image_info *other_info; +#endif }; #ifdef TARGET_I386 diff --git a/linux-user/strace.c b/linux-user/strace.c index bf9a0d9391..18366661cd 100644 --- a/linux-user/strace.c +++ b/linux-user/strace.c @@ -398,6 +398,7 @@ UNUSED static struct flags mmap_flags[] = { FLAG_TARGET(MAP_DENYWRITE), FLAG_TARGET(MAP_FIXED), FLAG_TARGET(MAP_GROWSDOWN), + FLAG_TARGET(MAP_EXECUTABLE), #ifdef MAP_LOCKED FLAG_TARGET(MAP_LOCKED), #endif @@ -408,6 +409,9 @@ UNUSED static struct flags mmap_flags[] = { #ifdef MAP_POPULATE FLAG_TARGET(MAP_POPULATE), #endif +#ifdef TARGET_MAP_UNINITIALIZED + FLAG_TARGET(MAP_UNINITIALIZED), +#endif FLAG_END, }; @@ -1199,7 +1203,7 @@ print_utimensat(const struct syscallname *name, } #endif -#ifdef TARGET_NR_mmap +#if defined(TARGET_NR_mmap) || defined(TARGET_NR_mmap2) static void print_mmap(const struct syscallname *name, abi_long arg0, abi_long arg1, abi_long arg2, diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 499c4d7d62..4412a9b143 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -235,6 +235,12 @@ _syscall6(int,sys_futex,int *,uaddr,int,op,int,val, const struct timespec *,timeout,int *,uaddr2,int,val3) #endif #endif +#define __NR_sys_sched_getaffinity __NR_sched_getaffinity +_syscall3(int, sys_sched_getaffinity, pid_t, pid, unsigned int, len, + unsigned long *, user_mask_ptr); +#define __NR_sys_sched_setaffinity __NR_sched_setaffinity +_syscall3(int, sys_sched_setaffinity, pid_t, pid, unsigned int, len, + unsigned long *, user_mask_ptr); static bitmask_transtbl fcntl_flags_tbl[] = { { TARGET_O_ACCMODE, TARGET_O_WRONLY, O_ACCMODE, O_WRONLY, }, @@ -529,6 +535,15 @@ static int sys_inotify_init1(int flags) #undef TARGET_NR_inotify_rm_watch #endif /* CONFIG_INOTIFY */ +#if defined(TARGET_NR_ppoll) +#ifndef __NR_ppoll +# define __NR_ppoll -1 +#endif +#define __NR_sys_ppoll __NR_ppoll +_syscall5(int, sys_ppoll, struct pollfd *, fds, nfds_t, nfds, + struct timespec *, timeout, const __sigset_t *, sigmask, + size_t, sigsetsize) +#endif extern int personality(int); extern int flock(int, int); @@ -1448,7 +1463,7 @@ static abi_long do_getsockopt(int sockfd, int level, int optname, return -TARGET_EFAULT; if (len < 0) return -TARGET_EINVAL; - lv = sizeof(int); + lv = sizeof(lv); ret = get_errno(getsockopt(sockfd, level, optname, &val, &lv)); if (ret < 0) return ret; @@ -1485,7 +1500,7 @@ static abi_long do_getsockopt(int sockfd, int level, int optname, return -TARGET_EFAULT; if (len < 0) return -TARGET_EINVAL; - lv = sizeof(int); + lv = sizeof(lv); ret = get_errno(getsockopt(sockfd, level, optname, &val, &lv)); if (ret < 0) return ret; @@ -6230,8 +6245,13 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, ret = do_select(arg1, arg2, arg3, arg4, arg5); break; #endif -#ifdef TARGET_NR_poll +#if defined(TARGET_NR_poll) || defined(TARGET_NR_ppoll) +# ifdef TARGET_NR_poll case TARGET_NR_poll: +# endif +# ifdef TARGET_NR_ppoll + case TARGET_NR_ppoll: +# endif { struct target_pollfd *target_pfd; unsigned int nfds = arg2; @@ -6242,12 +6262,51 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, target_pfd = lock_user(VERIFY_WRITE, arg1, sizeof(struct target_pollfd) * nfds, 1); if (!target_pfd) goto efault; + pfd = alloca(sizeof(struct pollfd) * nfds); for(i = 0; i < nfds; i++) { pfd[i].fd = tswap32(target_pfd[i].fd); pfd[i].events = tswap16(target_pfd[i].events); } - ret = get_errno(poll(pfd, nfds, timeout)); + +# ifdef TARGET_NR_ppoll + if (num == TARGET_NR_ppoll) { + struct timespec _timeout_ts, *timeout_ts = &_timeout_ts; + target_sigset_t *target_set; + sigset_t _set, *set = &_set; + + if (arg3) { + if (target_to_host_timespec(timeout_ts, arg3)) { + unlock_user(target_pfd, arg1, 0); + goto efault; + } + } else { + timeout_ts = NULL; + } + + if (arg4) { + target_set = lock_user(VERIFY_READ, arg4, sizeof(target_sigset_t), 1); + if (!target_set) { + unlock_user(target_pfd, arg1, 0); + goto efault; + } + target_to_host_sigset(set, target_set); + } else { + set = NULL; + } + + ret = get_errno(sys_ppoll(pfd, nfds, timeout_ts, set, _NSIG/8)); + + if (!is_error(ret) && arg3) { + host_to_target_timespec(arg3, timeout_ts); + } + if (arg4) { + unlock_user(target_set, arg4, 0); + } + } else +# endif + ret = get_errno(poll(pfd, nfds, timeout)); + if (!is_error(ret)) { for(i = 0; i < nfds; i++) { target_pfd[i].revents = tswap16(pfd[i].revents); @@ -6301,6 +6360,67 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, return value. */ ret = -TARGET_ENOTDIR; break; + case TARGET_NR_sched_getaffinity: + { + unsigned int mask_size; + unsigned long *mask; + + /* + * sched_getaffinity needs multiples of ulong, so need to take + * care of mismatches between target ulong and host ulong sizes. + */ + if (arg2 & (sizeof(abi_ulong) - 1)) { + ret = -TARGET_EINVAL; + break; + } + mask_size = (arg2 + (sizeof(*mask) - 1)) & ~(sizeof(*mask) - 1); + + mask = alloca(mask_size); + ret = get_errno(sys_sched_getaffinity(arg1, mask_size, mask)); + + if (!is_error(ret)) { + if (arg2 > ret) { + /* Zero out any extra space kernel didn't fill */ + unsigned long zero = arg2 - ret; + p = alloca(zero); + memset(p, 0, zero); + if (copy_to_user(arg3 + zero, p, zero)) { + goto efault; + } + arg2 = ret; + } + if (copy_to_user(arg3, mask, arg2)) { + goto efault; + } + ret = arg2; + } + } + break; + case TARGET_NR_sched_setaffinity: + { + unsigned int mask_size; + unsigned long *mask; + + /* + * sched_setaffinity needs multiples of ulong, so need to take + * care of mismatches between target ulong and host ulong sizes. + */ + if (arg2 & (sizeof(abi_ulong) - 1)) { + ret = -TARGET_EINVAL; + break; + } + mask_size = (arg2 + (sizeof(*mask) - 1)) & ~(sizeof(*mask) - 1); + + mask = alloca(mask_size); + if (!lock_user_struct(VERIFY_READ, p, arg3, 1)) { + goto efault; + } + memcpy(mask, p, arg2); + unlock_user_struct(p, arg2, 0); + + ret = get_errno(sys_sched_setaffinity(arg1, mask_size, mask)); + } + break; case TARGET_NR_sched_setparam: { struct sched_param *target_schp; diff --git a/linux-user/syscall_defs.h b/linux-user/syscall_defs.h index d02a9bf401..4742ac0272 100644 --- a/linux-user/syscall_defs.h +++ b/linux-user/syscall_defs.h @@ -999,6 +999,7 @@ struct target_winsize { #define TARGET_MAP_NORESERVE 0x4000 /* don't check for reservations */ #define TARGET_MAP_POPULATE 0x8000 /* populate (prefault) pagetables */ #define TARGET_MAP_NONBLOCK 0x10000 /* do not block on IO */ +#define TARGET_MAP_UNINITIALIZED 0x4000000 /* for anonymous mmap, memory could be uninitialized */ #endif #if (defined(TARGET_I386) && defined(TARGET_ABI32)) || defined(TARGET_ARM) || defined(TARGET_CRIS) diff --git a/linux-user/target_flat.h b/linux-user/target_flat.h new file mode 100644 index 0000000000..0ba6bdd12e --- /dev/null +++ b/linux-user/target_flat.h @@ -0,0 +1,10 @@ +/* If your arch needs to do custom stuff, create your own target_flat.h + * header file in linux-user/<your arch>/ + */ +#define flat_argvp_envp_on_stack() 1 +#define flat_reloc_valid(reloc, size) ((reloc) <= (size)) +#define flat_old_ram_flag(flag) (flag) +#define flat_get_relocate_addr(relval) (relval) +#define flat_get_addr_from_rp(rp, relval, flags, persistent) (rp) +#define flat_set_persistent(relval, persistent) (*persistent) +#define flat_put_addr_at_rp(rp, addr, relval) put_user_ual(addr, rp) @@ -38,7 +38,8 @@ static int strneq(const char *s1, unsigned int n, const char *s2) return s2[i] == 0; } -static struct pathelem *add_entry(struct pathelem *root, const char *name); +static struct pathelem *add_entry(struct pathelem *root, const char *name, + unsigned char type); static struct pathelem *new_entry(const char *root, struct pathelem *parent, @@ -56,6 +57,15 @@ static struct pathelem *new_entry(const char *root, #define streq(a,b) (strcmp((a), (b)) == 0) +/* Not all systems provide this feature */ +#if defined(DT_DIR) && defined(DT_UNKNOWN) +# define dirent_type(dirent) ((dirent)->d_type) +# define is_dir_maybe(type) ((type) == DT_DIR || (type) == DT_UNKNOWN) +#else +# define dirent_type(dirent) (1) +# define is_dir_maybe(type) (type) +#endif + static struct pathelem *add_dir_maybe(struct pathelem *path) { DIR *dir; @@ -65,7 +75,7 @@ static struct pathelem *add_dir_maybe(struct pathelem *path) while ((dirent = readdir(dir)) != NULL) { if (!streq(dirent->d_name,".") && !streq(dirent->d_name,"..")){ - path = add_entry(path, dirent->d_name); + path = add_entry(path, dirent->d_name, dirent_type(dirent)); } } closedir(dir); @@ -73,16 +83,22 @@ static struct pathelem *add_dir_maybe(struct pathelem *path) return path; } -static struct pathelem *add_entry(struct pathelem *root, const char *name) +static struct pathelem *add_entry(struct pathelem *root, const char *name, + unsigned char type) { + struct pathelem **e; + root->num_entries++; root = realloc(root, sizeof(*root) + sizeof(root->entries[0])*root->num_entries); + e = &root->entries[root->num_entries-1]; + + *e = new_entry(root->pathname, root, name); + if (is_dir_maybe(type)) { + *e = add_dir_maybe(*e); + } - root->entries[root->num_entries-1] = new_entry(root->pathname, root, name); - root->entries[root->num_entries-1] - = add_dir_maybe(root->entries[root->num_entries-1]); return root; } |