diff options
author | Patrick Mooney <patrick.f.mooney@gmail.com> | 2015-05-19 20:57:50 +0000 |
---|---|---|
committer | Patrick Mooney <patrick.f.mooney@gmail.com> | 2015-05-20 14:04:42 +0000 |
commit | eb8244c4ef2358dc7ec61e6acb96f4a9c7d9fa4d (patch) | |
tree | 1019eebbc03637f80fc9761b9742214c874240f0 /usr/src | |
parent | ed907ffaebd1dabd93b28c7a9ddfd607ef5290bc (diff) | |
download | illumos-joyent-eb8244c4ef2358dc7ec61e6acb96f4a9c7d9fa4d.tar.gz |
OS-4320 lxbrand convert getdents to IKE
OS-4282 lxbrand segfault in lx_getdents
Reviewed by: Jerry Jelinek <jerry.jelinek@joyent.com>
Diffstat (limited to 'usr/src')
-rw-r--r-- | usr/src/lib/brand/lx/lx_brand/common/dir.c | 195 | ||||
-rw-r--r-- | usr/src/lib/brand/lx/lx_brand/common/lx_brand.c | 8 | ||||
-rw-r--r-- | usr/src/lib/brand/lx/lx_brand/sys/lx_syscall.h | 2 | ||||
-rw-r--r-- | usr/src/uts/common/brand/lx/os/lx_syscall.c | 8 | ||||
-rw-r--r-- | usr/src/uts/common/brand/lx/sys/lx_syscalls.h | 3 | ||||
-rw-r--r-- | usr/src/uts/common/brand/lx/syscall/lx_getdents.c | 349 | ||||
-rw-r--r-- | usr/src/uts/intel/Makefile.files | 1 |
7 files changed, 362 insertions, 204 deletions
diff --git a/usr/src/lib/brand/lx/lx_brand/common/dir.c b/usr/src/lib/brand/lx/lx_brand/common/dir.c index 0fda763c19..ed10dfb822 100644 --- a/usr/src/lib/brand/lx/lx_brand/common/dir.c +++ b/usr/src/lib/brand/lx/lx_brand/common/dir.c @@ -21,7 +21,7 @@ /* * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. - * Copyright 2014 Joyent, Inc. All rights reserved. + * Copyright 2015 Joyent, Inc. */ #include <string.h> @@ -46,54 +46,6 @@ struct lx_old_dirent { }; /* - * The positioning of d_type depends on d_reclen, since it follows - * d_name which varies in size. Because d_type is not being emulated - * beyond DT_UNKNOWN (0), this can be achieved by zero-filling the - * structure to d_reclen bytes after the end of d_name. - */ -struct lx_dirent { - long d_ino; - long d_off; - ushort_t d_reclen; - char d_name[LX_NAMEMAX]; - uchar_t d_type; -}; - -/* base definition of linux_dirent from readdir.c - sizeof is 12 */ -typedef struct { - ulong_t d_ino; - ulong_t d_off; - ushort_t d_reclen; - char d_name[1]; -} lx_linux_dirent_t; - -/* - * Record must be long enough to house d_name string, null terminator and - * d_type field. It's then padded to nearest 8-byte boundary - */ -#define LX_RECLEN(namelen) \ - ((offsetof(struct lx_dirent, d_name) + 2 + (namelen) + 7) & ~7) - -/* - * Bytes after d_name string until d_reclen should be zeroed. - * Includes zero-terminating d_name - */ -#define LX_ZEROLEN(namelen) \ - (LX_RECLEN(namelen) - \ - ((offsetof(struct lx_dirent, d_name) + (namelen)))) - -struct lx_dirent64 { - uint64_t d_ino; - int64_t d_off; - ushort_t d_reclen; - uchar_t d_type; - char d_name[LX_NAMEMAX]; -}; - -#define LX_RECLEN64(namelen) \ - ((offsetof(struct lx_dirent64, d_name) + 1 + (namelen) + 7) & ~7) - -/* * Read in one dirent structure from fd into dirp. * p3 (count) is ignored. */ @@ -128,148 +80,3 @@ lx_readdir(uintptr_t p1, uintptr_t p2, uintptr_t p3) return (rc); } - -/* - * Read in dirent structures from p1 (fd) into p2 (buffer). - * p3 (count) is the size of the memory area. - */ -long -lx_getdents(uintptr_t p1, uintptr_t p2, uintptr_t p3) -{ - int fd = (int)p1; - void *buf = (void *)p2; - void *sbuf, *lbuf; - int lbufsz = (uint_t)p3; - int sbufsz; - int namelen; - struct dirent *sd; - struct lx_dirent *ld; - int bytes, rc; - - /* - * readdir will pass in the full size, but some test code calls getdents - * directly and uses the bare struct. For these, just pretend we got - * a single full-size entry so we can obtain the proper errno. - */ - if (lbufsz == sizeof (lx_linux_dirent_t)) - lbufsz = sizeof (struct lx_dirent); - - if (lbufsz < sizeof (struct lx_dirent)) - return (-EINVAL); - - /* - * The Linux dirent is bigger than the Solaris dirent. To - * avoid inadvertently consuming more of the directory than we can - * pass back to the Linux app, we hand the kernel a smaller buffer - * than the app handed us. - */ - sbufsz = (lbufsz / 32) * 24; - - sbuf = SAFE_ALLOCA(sbufsz); - lbuf = SAFE_ALLOCA(lbufsz); - if (sbuf == NULL || lbuf == NULL) - return (-ENOMEM); - - if ((bytes = getdents(fd, sbuf, sbufsz)) < 0) - return (-errno); - - /* munge the Solaris buffer to a linux buffer. */ - sd = (struct dirent *)sbuf; - ld = (struct lx_dirent *)lbuf; - rc = 0; - while (bytes > 0) { - namelen = strlen(sd->d_name); - if (namelen >= LX_NAMEMAX) - namelen = LX_NAMEMAX - 1; - ld->d_ino = (uint64_t)sd->d_ino; - ld->d_off = (int64_t)sd->d_off; - - (void) strncpy(ld->d_name, sd->d_name, namelen); - /* - * Zero out the rest of the record. - * This effective sets ld->d_type = 0 and zero-terminates d_name - */ - memset(ld->d_name + namelen, '\0', LX_ZEROLEN(namelen)); - ld->d_reclen = (ushort_t)LX_RECLEN(namelen); - - bytes -= (int)sd->d_reclen; - rc += (int)ld->d_reclen; - - sd = (struct dirent *)(void *)((caddr_t)sd + sd->d_reclen); - ld = (struct lx_dirent *)(void *)((caddr_t)ld + ld->d_reclen); - } - - /* now copy the lbuf to the userland buffer */ - assert(rc <= lbufsz); - if (uucopy(lbuf, buf, rc) != 0) - return (-EFAULT); - - return (rc); -} - -/* - * Read in dirent64 structures from p1 (fd) into p2 (buffer). - * p3 (count) is the size of the memory area. - */ -long -lx_getdents64(uintptr_t p1, uintptr_t p2, uintptr_t p3) -{ - int fd = (uint_t)p1; - void *buf = (void *)p2; - void *sbuf, *lbuf; - int lbufsz = (uint_t)p3; - int sbufsz; - int namelen; - struct dirent *sd; - struct lx_dirent64 *ld; - int bytes, rc; - - if (lbufsz < sizeof (struct lx_dirent64)) - return (-EINVAL); - - /* - * The Linux dirent64 is bigger than the Solaris dirent64. To - * avoid inadvertently consuming more of the directory than we can - * pass back to the Linux app, we hand the kernel a smaller buffer - * than the app handed us. - */ - sbufsz = (lbufsz / 32) * 24; - - sbuf = SAFE_ALLOCA(sbufsz); - lbuf = SAFE_ALLOCA(lbufsz); - if (sbuf == NULL || lbuf == NULL) - return (-ENOMEM); - - if ((bytes = getdents(fd, sbuf, sbufsz)) < 0) - return (-errno); - - /* munge the Solaris buffer to a linux buffer. */ - sd = (struct dirent *)sbuf; - ld = (struct lx_dirent64 *)lbuf; - rc = 0; - while (bytes > 0) { - namelen = strlen(sd->d_name); - if (namelen >= LX_NAMEMAX) - namelen = LX_NAMEMAX - 1; - ld->d_ino = (uint64_t)sd->d_ino; - ld->d_off = (int64_t)sd->d_off; - ld->d_type = 0; - - (void) strncpy(ld->d_name, sd->d_name, namelen); - ld->d_name[namelen] = 0; - ld->d_reclen = (ushort_t)LX_RECLEN64(namelen); - - bytes -= (int)sd->d_reclen; - rc += (int)ld->d_reclen; - - sd = (struct dirent *)(void *)((caddr_t)sd + sd->d_reclen); - ld = (struct lx_dirent64 *)(void *)((caddr_t)ld + ld->d_reclen); - } - - /* now copy the lbuf to the userland buffer */ - assert(rc <= lbufsz); - if (uucopy(lbuf, buf, rc) != 0) - return (-EFAULT); - - return (rc); -} diff --git a/usr/src/lib/brand/lx/lx_brand/common/lx_brand.c b/usr/src/lib/brand/lx/lx_brand/common/lx_brand.c index e7083bec87..21da0771fc 100644 --- a/usr/src/lib/brand/lx/lx_brand/common/lx_brand.c +++ b/usr/src/lib/brand/lx/lx_brand/common/lx_brand.c @@ -992,7 +992,7 @@ static lx_syscall_handler_t lx_handlers[] = { lx_fdatasync, /* 75: fdatasync */ lx_truncate, /* 76: truncate */ lx_ftruncate, /* 77: ftruncate */ - lx_getdents, /* 78: getdents */ + NULL, /* 78: getdents */ lx_getcwd, /* 79: getcwd */ lx_chdir, /* 80: chdir */ lx_fchdir, /* 81: fchdir */ @@ -1131,7 +1131,7 @@ static lx_syscall_handler_t lx_handlers[] = { NULL, /* 214: epoll_ctl_old */ NULL, /* 215: epoll_wait_old */ NULL, /* 216: remap_file_pages */ - lx_getdents64, /* 217: getdents64 */ + NULL, /* 217: getdents64 */ NULL, /* 218: set_tid_address */ NULL, /* 219: restart_syscall */ lx_semtimedop, /* 220: semtimedop */ @@ -1386,7 +1386,7 @@ static lx_syscall_handler_t lx_handlers[] = { lx_setfsuid16, /* 138: setfsuid16 */ lx_setfsgid16, /* 139: setfsgid16 */ lx_llseek, /* 140: llseek */ - lx_getdents, /* 141: getdents */ + NULL, /* 141: getdents */ lx_select, /* 142: select */ lx_flock, /* 143: flock */ lx_msync, /* 144: msync */ @@ -1465,7 +1465,7 @@ static lx_syscall_handler_t lx_handlers[] = { NULL, /* 217: pivot_root */ lx_mincore, /* 218: mincore */ lx_madvise, /* 219: madvise */ - lx_getdents64, /* 220: getdents64 */ + NULL, /* 220: getdents64 */ NULL, /* 221: fcntl64 */ NULL, /* 222: tux */ NULL, /* 223: security */ diff --git a/usr/src/lib/brand/lx/lx_brand/sys/lx_syscall.h b/usr/src/lib/brand/lx/lx_brand/sys/lx_syscall.h index 6eff748ff5..ed89a9083b 100644 --- a/usr/src/lib/brand/lx/lx_brand/sys/lx_syscall.h +++ b/usr/src/lib/brand/lx/lx_brand/sys/lx_syscall.h @@ -67,8 +67,6 @@ extern long lx_fcntl64(uintptr_t, uintptr_t, uintptr_t); extern long lx_flock(uintptr_t, uintptr_t); extern long lx_readlink(uintptr_t, uintptr_t, uintptr_t); extern long lx_readdir(uintptr_t, uintptr_t, uintptr_t); -extern long lx_getdents(uintptr_t, uintptr_t, uintptr_t); -extern long lx_getdents64(uintptr_t, uintptr_t, uintptr_t); extern long lx_execve(uintptr_t, uintptr_t, uintptr_t); extern long lx_dup2(uintptr_t, uintptr_t); extern long lx_dup3(uintptr_t, uintptr_t, uintptr_t); diff --git a/usr/src/uts/common/brand/lx/os/lx_syscall.c b/usr/src/uts/common/brand/lx/os/lx_syscall.c index 965aff2ca9..cce902a943 100644 --- a/usr/src/uts/common/brand/lx/os/lx_syscall.c +++ b/usr/src/uts/common/brand/lx/os/lx_syscall.c @@ -661,7 +661,7 @@ lx_sysent_t lx_sysent32[] = { {"setfsuid16", NULL, 0, 1}, /* 138 */ {"setfsgid16", NULL, 0, 1}, /* 139 */ {"llseek", NULL, 0, 5}, /* 140 */ - {"getdents", NULL, 0, 3}, /* 141 */ + {"getdents", lx_getdents_32, 0, 3}, /* 141 */ {"select", NULL, 0, 5}, /* 142 */ {"flock", NULL, 0, 2}, /* 143 */ {"msync", NULL, 0, 3}, /* 144 */ @@ -740,7 +740,7 @@ lx_sysent_t lx_sysent32[] = { {"pivot_root", NULL, NOSYS_KERNEL, 0}, /* 217 */ {"mincore", NULL, 0, 3}, /* 218 */ {"madvise", NULL, 0, 3}, /* 219 */ - {"getdents64", NULL, 0, 3}, /* 220 */ + {"getdents64", lx_getdents64, 0, 3}, /* 220 */ {"fcntl64", lx_fcntl64, 0, 3}, /* 221 */ {"tux", NULL, NOSYS_NO_EQUIV, 0}, /* 222 */ {"security", NULL, NOSYS_NO_EQUIV, 0}, /* 223 */ @@ -969,7 +969,7 @@ lx_sysent_t lx_sysent64[] = { {"fdatasync", NULL, 0, 1}, /* 75 */ {"truncate", NULL, 0, 2}, /* 76 */ {"ftruncate", NULL, 0, 2}, /* 77 */ - {"getdents", NULL, 0, 3}, /* 78 */ + {"getdents", lx_getdents_64, 0, 3}, /* 78 */ {"getcwd", NULL, 0, 2}, /* 79 */ {"chdir", NULL, 0, 1}, /* 80 */ {"fchdir", NULL, 0, 1}, /* 81 */ @@ -1108,7 +1108,7 @@ lx_sysent_t lx_sysent64[] = { {"epoll_ctl_old", NULL, NOSYS_NULL, 0}, /* 214 */ {"epoll_wait_old", NULL, NOSYS_NULL, 0}, /* 215 */ {"remap_file_pages", NULL, NOSYS_NO_EQUIV, 0}, /* 216 */ - {"getdents64", NULL, 0, 3}, /* 217 */ + {"getdents64", lx_getdents64, 0, 3}, /* 217 */ {"set_tid_address", lx_set_tid_address, 0, 1}, /* 218 */ {"restart_syscall", NULL, NOSYS_NULL, 0}, /* 219 */ {"semtimedop", NULL, 0, 4}, /* 220 */ diff --git a/usr/src/uts/common/brand/lx/sys/lx_syscalls.h b/usr/src/uts/common/brand/lx/sys/lx_syscalls.h index 8cbf9751a2..0f2977c47c 100644 --- a/usr/src/uts/common/brand/lx/sys/lx_syscalls.h +++ b/usr/src/uts/common/brand/lx/sys/lx_syscalls.h @@ -53,6 +53,9 @@ extern long lx_futex(); extern long lx_get_robust_list(); extern long lx_get_thread_area(); extern long lx_getcpu(); +extern long lx_getdents_32(); +extern long lx_getdents_64(); +extern long lx_getdents64(); extern long lx_getpid(); extern long lx_getppid(); extern long lx_getrandom(); diff --git a/usr/src/uts/common/brand/lx/syscall/lx_getdents.c b/usr/src/uts/common/brand/lx/syscall/lx_getdents.c new file mode 100644 index 0000000000..a41ab19d65 --- /dev/null +++ b/usr/src/uts/common/brand/lx/syscall/lx_getdents.c @@ -0,0 +1,349 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2015 Joyent, Inc. + */ + +#include <sys/systm.h> +#include <sys/filio.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/inttypes.h> +#include <sys/vnode.h> +#include <sys/dirent.h> +#include <sys/errno.h> +#include <sys/file.h> +#include <sys/sysmacros.h> +#include <sys/kmem.h> +#include <sys/sunddi.h> + +#include <sys/lx_types.h> +#include <sys/lx_misc.h> + +#define LX_NAMEMAX 256 + +#define LX_GETDENTS_MAX_BUFSZ 65536 + +/* + * Because the Linux dirent has an extra field (d_type), it's possible that + * each entry will be 8 bytes larger due to padding. To prevent overrun during + * translation, the illumos-native buffer is sized pessimistically. + */ +#define LTOS_GETDENTS_BUFSZ(bufsz, datasz) \ + (((bufsz) / (datasz + 8)) * sizeof (struct dirent)) + +/* + * Record must be long enough to house d_name string, null terminator and + * d_type field. It's then padded to nearest 8-byte boundary + */ +#define LX_RECLEN(l, t) \ + ((offsetof(t, d_name) + 2 + (l) + 7) & ~7) + +/* + * Bytes after d_name string until d_reclen should be zeroed. + * Includes zero-terminating d_name + */ +#define LX_ZEROLEN(l, t) \ + (LX_RECLEN(l, t) - \ + ((offsetof(t, d_name) + (l)))) + +/* The output format of getdents differs if the caller is 32 or 64 bit. */ +struct lx_dirent_32 { + uint32_t d_ino; + int32_t d_off; + ushort_t d_reclen; + char d_name[1]; + uchar_t d_type; +}; + +struct lx_dirent_64 { + uint64_t d_ino; + int64_t d_off; + ushort_t d_reclen; + char d_name[1]; + uchar_t d_type; +}; + +static long +lx_getdents_common(int fd, caddr_t uptr, size_t count, + unsigned int lx_size, int (*outcb)(caddr_t, caddr_t, int)) +{ + vnode_t *vp; + file_t *fp; + struct uio auio; + struct iovec aiov; + int error; + int sbufsz, lbufsz, bufsz; + void *lbuf, *sbuf; + size_t outb = 0; + + if (count < lx_size) { + return (set_errno(EINVAL)); + } + if ((fp = getf(fd)) == NULL) { + return (set_errno(EBADF)); + } + vp = fp->f_vnode; + if (vp->v_type != VDIR) { + releasef(fd); + return (set_errno(ENOTDIR)); + } + if (!(fp->f_flag & FREAD)) { + releasef(fd); + return (set_errno(EBADF)); + } + + if (count > LX_GETDENTS_MAX_BUFSZ) { + /* + * If the target buffer passed to us is huge, keep the + * translation buffers moderate in size. Iteration will be + * used to fill the request. + */ + lbufsz = LX_GETDENTS_MAX_BUFSZ; + sbufsz = LTOS_GETDENTS_BUFSZ(LX_GETDENTS_MAX_BUFSZ, lx_size); + } else if (count < (lx_size + MAXPATHLEN)) { + /* + * If the target buffer is tiny, allocate a Linux-format buffer + * big enough to hold at least one max-length row while keeping + * the illumos-format buffer pesimistic in size. + * + * Assuming the buffer is truely tiny, it's likely that the + * result will not fit and an EINVAL will be tossed. + */ + lbufsz = (lx_size + MAXPATHLEN); + sbufsz = MAX((LTOS_GETDENTS_BUFSZ(count, lx_size)), + sizeof (struct dirent)); + } else { + lbufsz = count; + sbufsz = LTOS_GETDENTS_BUFSZ(count, lx_size); + } + bufsz = sbufsz; + lbuf = kmem_alloc(lbufsz, KM_SLEEP); + sbuf = kmem_alloc(sbufsz, KM_SLEEP); + + aiov.iov_base = sbuf; + aiov.iov_len = sbufsz; + auio.uio_iov = &aiov; + auio.uio_iovcnt = 1; + auio.uio_loffset = fp->f_offset; + auio.uio_segflg = UIO_SYSSPACE; + auio.uio_resid = sbufsz; + auio.uio_fmode = 0; + auio.uio_extflg = UIO_COPY_CACHED; + + /* + * Since we use a conservative buffer allocation for the differing + * struct sizing and Linux places fewer limits on getdents buffers in + * general, there's a chance we'll undershoot on the record count. + * When this happens, we can simply repeat the READDIR operation until + * the available records are exhausted or we've filled the user buffer. + */ + while (1) { + int at_eof, res; + (void) VOP_RWLOCK(vp, V_WRITELOCK_FALSE, NULL); + error = VOP_READDIR(vp, &auio, fp->f_cred, &at_eof, NULL, 0); + VOP_RWUNLOCK(vp, V_WRITELOCK_FALSE, NULL); + if (error != 0 || auio.uio_resid == sbufsz) { + break; + } + res = outcb(sbuf, lbuf, bufsz - auio.uio_resid); + VERIFY(res <= lbufsz); + if (res == 0) { + /* no records to copyout from this batch */ + break; + } else if (res > count) { + /* + * For very small buffer sizes, it's possible that a + * single record is too large due to a long filename. + */ + error = EINVAL; + break; + } + + VERIFY(outb + res <= count); + if (copyout(lbuf, (void *)(uptr + outb), res) != 0) { + error = EFAULT; + break; + } + outb += res; + + if (at_eof != 0 || (count - outb) < (lx_size + MAXPATHLEN)) { + /* + * If there are no records left or the remaining buffer + * space is not large enough to hold a max-length + * filename, do not continue iteration. + */ + break; + } + + /* + * We undershot the request buffer. + * Reset for another READDIR, taking care not to overshoot. + */ + bufsz = MIN(sbufsz, LTOS_GETDENTS_BUFSZ(count - outb, lx_size)); + auio.uio_resid = bufsz; + aiov.iov_len = bufsz; + aiov.iov_base = sbuf; + } + + kmem_free(lbuf, lbufsz); + kmem_free(sbuf, sbufsz); + + if (error) { + releasef(fd); + return (set_errno(error)); + } + + fp->f_offset = auio.uio_loffset; + releasef(fd); + return (outb); +} + + +static int +lx_getdents_format32(caddr_t sbuf, caddr_t lbuf, int len) +{ + struct dirent *sd; + struct lx_dirent_32 *ld; + int namelen; + int size = 0; + + while (len > 0) { + sd = (struct dirent *)sbuf; + ld = (struct lx_dirent_32 *)lbuf; + namelen = MIN(strlen(sd->d_name), LX_NAMEMAX - 1); + + ld->d_ino = sd->d_ino; + ld->d_off = sd->d_off; + (void) strncpy(ld->d_name, sd->d_name, namelen); + ld->d_name[namelen] = 0; + ld->d_reclen = (ushort_t)LX_RECLEN(namelen, + struct lx_dirent_32); + /* Zero out any alignment padding and d_type */ + bzero(ld->d_name + namelen, + LX_ZEROLEN(namelen, struct lx_dirent_32)); + + len -= sd->d_reclen; + size += ld->d_reclen; + sbuf += sd->d_reclen; + lbuf += ld->d_reclen; + } + return (size); +} + +static int +lx_getdents_format64(caddr_t sbuf, caddr_t lbuf, int len) +{ + struct dirent *sd; + struct lx_dirent_64 *ld; + int namelen; + int size = 0; + + while (len > 0) { + sd = (struct dirent *)sbuf; + ld = (struct lx_dirent_64 *)lbuf; + namelen = MIN(strlen(sd->d_name), LX_NAMEMAX - 1); + + ld->d_ino = sd->d_ino; + ld->d_off = sd->d_off; + (void) strncpy(ld->d_name, sd->d_name, namelen); + ld->d_name[namelen] = 0; + ld->d_reclen = (ushort_t)LX_RECLEN(namelen, + struct lx_dirent_64); + /* Zero out any alignment padding and d_type */ + bzero(ld->d_name + namelen, + LX_ZEROLEN(namelen, struct lx_dirent_64)); + + len -= sd->d_reclen; + size += ld->d_reclen; + sbuf += sd->d_reclen; + lbuf += ld->d_reclen; + } + return (size); +} + +long +lx_getdents_32(int fd, caddr_t buf, size_t count) +{ + return (lx_getdents_common(fd, buf, count, + sizeof (struct lx_dirent_32), lx_getdents_format32)); +} + +long +lx_getdents_64(int fd, caddr_t buf, size_t count) +{ + return (lx_getdents_common(fd, buf, count, + sizeof (struct lx_dirent_64), lx_getdents_format64)); +} + +struct lx_dirent64 { + uint64_t d_ino; + int64_t d_off; + ushort_t d_reclen; + uchar_t d_type; + char d_name[1]; +}; + +#define LX_RECLEN64(namelen) \ + ((offsetof(struct lx_dirent64, d_name) + 1 + (namelen) + 7) & ~7) + +#define LX_ZEROLEN64(namelen) \ + (LX_RECLEN64(namelen) - \ + ((offsetof(struct lx_dirent64, d_name) + (namelen)))) + +static int +lx_getdents64_format(caddr_t sbuf, caddr_t lbuf, int len) +{ + struct dirent *sd; + struct lx_dirent64 *ld; + int namelen; + int size = 0; + + while (len > 0) { + sd = (struct dirent *)sbuf; + ld = (struct lx_dirent64 *)lbuf; + namelen = MIN(strlen(sd->d_name), LX_NAMEMAX - 1); + + ld->d_ino = sd->d_ino; + ld->d_off = sd->d_off; + ld->d_type = 0; + (void) strncpy(ld->d_name, sd->d_name, namelen); + ld->d_name[namelen] = 0; + ld->d_reclen = (ushort_t)LX_RECLEN64(namelen); + /* Zero out any alignment padding */ + bzero(ld->d_name + namelen, LX_ZEROLEN64(namelen)); + + len -= sd->d_reclen; + size += ld->d_reclen; + sbuf += sd->d_reclen; + lbuf += ld->d_reclen; + } + return (size); +} + + +long +lx_getdents64(int fd, caddr_t buf, size_t count) +{ + return (lx_getdents_common(fd, buf, count, + sizeof (struct lx_dirent64), lx_getdents64_format)); +} diff --git a/usr/src/uts/intel/Makefile.files b/usr/src/uts/intel/Makefile.files index add15d039a..5b6c75d630 100644 --- a/usr/src/uts/intel/Makefile.files +++ b/usr/src/uts/intel/Makefile.files @@ -291,6 +291,7 @@ LX_BRAND_OBJS = \ lx_errno.o \ lx_fcntl.o \ lx_futex.o \ + lx_getdents.o \ lx_getpid.o \ lx_getrandom.o \ lx_id.o \ |