1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 /*
  22  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
  23  * Use is subject to license terms.
  24  */
  25 
  26 #pragma ident   "%Z%%M% %I%     %E% SMI"
  27 
  28 #include <string.h>
  29 #include <stddef.h>
  30 #include <errno.h>
  31 #include <unistd.h>
  32 #include <assert.h>
  33 #include <sys/types.h>
  34 #include <sys/systm.h>
  35 #include <sys/dirent.h>
  36 #include <sys/lx_misc.h>
  37 #include <sys/lx_debug.h>
  38 
  39 #define LX_NAMEMAX      256
  40 
  41 struct lx_dirent {
  42         long            d_ino;  /* not l_ino_t */
  43         long            d_off;
  44         ushort_t        d_reclen;
  45         char            d_name[LX_NAMEMAX];
  46 };
  47 
  48 struct lx_dirent64 {
  49         uint64_t        d_ino;
  50         int64_t         d_off;
  51         ushort_t        d_reclen;
  52         uchar_t         d_type;
  53         char            d_name[LX_NAMEMAX];
  54 };
  55 
  56 #define LX_RECLEN(namelen)      \
  57         ((offsetof(struct lx_dirent64, d_name) + 1 + (namelen) + 7) & ~7)
  58 
  59 /*
  60  * Read in one dirent structure from fd into dirp.
  61  * p3 (count) is ignored.
  62  */
  63 /*ARGSUSED*/
  64 int
  65 lx_readdir(uintptr_t p1, uintptr_t p2, uintptr_t p3)
  66 {
  67         int fd = (int)p1;
  68         struct lx_dirent *dirp = (struct lx_dirent *)p2;
  69         uint_t count = sizeof (struct lx_dirent);
  70         int rc = 0;
  71         struct lx_dirent _ld;
  72         struct dirent *sd = (struct dirent *)&_ld;
  73 
  74         /*
  75          * The return value from getdents is not applicable, as
  76          * it might have squeezed more than one dirent in the buffer
  77          * we provided.
  78          *
  79          * getdents() will deal with the case of dirp == NULL
  80          */
  81         if ((rc = getdents(fd, sd, count)) < 0)
  82                 return (-errno);
  83 
  84         /*
  85          * Set rc 1 (pass), or 0 (end of directory).
  86          */
  87         rc = (sd->d_reclen == 0) ? 0 : 1;
  88 
  89         if (uucopy(sd, dirp, count) != 0)
  90                 return (-errno);
  91 
  92         return (rc);
  93 }
  94 
  95 /*
  96  * Read in dirent64 structures from p1 (fd) into p2 (buffer).
  97  * p3 (count) is the size of the memory area.
  98  */
  99 int
 100 lx_getdents64(uintptr_t p1, uintptr_t p2, uintptr_t p3)
 101 {
 102         int fd = (uint_t)p1;
 103         void *buf = (void *)p2;
 104         void *sbuf, *lbuf;
 105         int lbufsz = (uint_t)p3;
 106         int sbufsz;
 107         int namelen;
 108         struct dirent *sd;
 109         struct lx_dirent64 *ld;
 110         int bytes, rc;
 111 
 112         if (lbufsz < sizeof (struct lx_dirent64))
 113                 return (-EINVAL);
 114 
 115         /*
 116          * The Linux dirent64 is bigger than the Solaris dirent64.  To
 117          * avoid inadvertently consuming more of the directory than we can
 118          * pass back to the Linux app, we hand the kernel a smaller buffer
 119          * than the app handed us.
 120          */
 121         sbufsz = (lbufsz / 32) * 24;
 122 
 123         sbuf = SAFE_ALLOCA(sbufsz);
 124         lbuf = SAFE_ALLOCA(lbufsz);
 125         if (sbuf == NULL || lbuf == NULL)
 126                 return (-ENOMEM);
 127 
 128         if ((bytes = getdents(fd, sbuf, sbufsz)) < 0)
 129                 return (-errno);
 130 
 131         /* munge the Solaris buffer to a linux buffer. */
 132         sd = (struct dirent *)sbuf;
 133         ld = (struct lx_dirent64 *)lbuf;
 134         rc = 0;
 135         while (bytes > 0) {
 136                 namelen = strlen(sd->d_name);
 137                 if (namelen >= LX_NAMEMAX)
 138                         namelen = LX_NAMEMAX - 1;
 139                 ld->d_ino = (uint64_t)sd->d_ino;
 140                 ld->d_off = (int64_t)sd->d_off;
 141                 ld->d_type = 0;
 142 
 143                 (void) strncpy(ld->d_name, sd->d_name, namelen);
 144                 ld->d_name[namelen] = 0;
 145                 ld->d_reclen = (ushort_t)LX_RECLEN(namelen);
 146 
 147                 bytes -= (int)sd->d_reclen;
 148                 rc += (int)ld->d_reclen;
 149 
 150                 sd = (struct dirent *)(void *)((caddr_t)sd + sd->d_reclen);
 151                 ld = (struct lx_dirent64 *)(void *)((caddr_t)ld + ld->d_reclen);
 152         }
 153 
 154         /* now copy the lbuf to the userland buffer */
 155         assert(rc <= lbufsz);
 156         if (uucopy(lbuf, buf, rc) != 0)
 157                 return (-EFAULT);
 158 
 159         return (rc);
 160 }