int lx_sendfile64(uintptr_t p1, uintptr_t p2, uintptr_t p3, uintptr_t p4) { sysret_t rval; off64_t off = 0; off64_t *offp = (off64_t *)p3; size_t sz = (size_t)p4; int error; struct sendfilevec64 sfv; size_t xferred; if (sz > 0 && uucopy(offp, &off, sizeof (off)) != 0) return (-errno); sfv.sfv_fd = p2; sfv.sfv_flag = 0; sfv.sfv_off = off; sfv.sfv_len = sz; error = __systemcall(&rval, SYS_sendfilev, SENDFILEV64, p1, &sfv, 1, &xferred); if (error == 0 && xferred > 0) { off += xferred; error = uucopy(&off, offp, sizeof (off)); } return (error ? -error : (int)rval.sys_rval1); }
static int lx_accept(ulong_t *args) { int sockfd = (int)args[0]; struct sockaddr *name = (struct sockaddr *)args[1]; socklen_t namelen = 0; int r; lx_debug("\taccept(%d, 0x%p, 0x%p", sockfd, args[1], args[2]); /* * The Linux man page says that -1 is returned and errno is set to * EFAULT if the "name" address is bad, but it is silent on what to * set errno to if the "namelen" address is bad. Experimentation * shows that Linux (at least the 2.4.21 kernel in CentOS) actually * sets errno to EINVAL in both cases. * * Note that we must first check the name pointer, as the Linux * docs state nothing is copied out if the "name" pointer is NULL. * If it is NULL, we don't care about the namelen pointer's value * or about dereferencing it. * * Happily, Solaris' accept(3SOCKET) treats NULL name pointers and * zero namelens the same way. */ if ((name != NULL) && (uucopy((void *)args[2], &namelen, sizeof (socklen_t)) != 0)) return ((errno == EFAULT) ? -EINVAL : -errno); lx_debug("\taccept namelen = %d", namelen); if ((r = accept(sockfd, name, &namelen)) < 0) return ((errno == EFAULT) ? -EINVAL : -errno); lx_debug("\taccept namelen returned %d bytes", namelen); /* * In Linux, accept()ed sockets do not inherit anything set by * fcntl(), so filter those out. */ if (fcntl(r, F_SETFL, 0) < 0) return (-errno); /* * Once again, a bad "namelen" address sets errno to EINVAL, not * EFAULT. If namelen was zero, there's no need to copy a zero back * out. * * Logic might dictate that we should check if we can write to * the namelen pointer earlier so we don't accept a pending connection * only to fail the call because we can't write the namelen value back * out. However, testing shows Linux does indeed fail the call after * accepting the connection so we must behave in a compatible manner. */ if ((name != NULL) && (namelen != 0) && (uucopy(&namelen, (void *)args[2], sizeof (socklen_t)) != 0)) return ((errno == EFAULT) ? -EINVAL : -errno); return (r); }
static int lx_getpeername(ulong_t *args) { int sockfd = (int)args[0]; struct sockaddr *name; socklen_t namelen; if (uucopy((void *)args[2], &namelen, sizeof (socklen_t)) != 0) return (-errno); lx_debug("\tgetpeername(%d, 0x%p, 0x%p (=%d))", sockfd, args[1], args[2], namelen); /* * Linux returns EFAULT in this case, even if the namelen parameter * is 0. This check will not catch other illegal addresses, but * the benefit catching a non-null illegal address here is not * worth the cost of another system call. */ if ((void *)args[1] == NULL) return (-EFAULT); if ((name = SAFE_ALLOCA(namelen)) == NULL) return (-EINVAL); if ((getpeername(sockfd, name, &namelen)) < 0) return (-errno); if (uucopy(name, (void *)args[1], namelen) != 0) return (-errno); if (uucopy(&namelen, (void *)args[2], sizeof (socklen_t)) != 0) return (-errno); return (0); }
long lx_fcntl64(uintptr_t p1, uintptr_t p2, uintptr_t p3) { int fd = (int)p1; int cmd = (int)p2; struct lx_flock lxflk; struct lx_flock64 lxflk64; struct flock fl; struct flock64 fl64; int rc; if (cmd == LX_F_SETSIG || cmd == LX_F_GETSIG || cmd == LX_F_SETLEASE || cmd == LX_F_GETLEASE) { lx_unsupported("unsupported fcntl64 command: %d", cmd); return (-ENOTSUP); } if (cmd == LX_F_GETLK || cmd == LX_F_SETLK || cmd == LX_F_SETLKW) { if (uucopy((void *)p3, (void *)&lxflk, sizeof (struct lx_flock)) != 0) return (-errno); ltos_flock(&lxflk, &fl); rc = lx_fcntl_com(fd, cmd, (ulong_t)&fl); if (rc >= 0) { stol_flock(&fl, &lxflk); if (uucopy((void *)&lxflk, (void *)p3, sizeof (struct lx_flock)) != 0) return (-errno); } } else if (cmd == LX_F_GETLK64 || cmd == LX_F_SETLKW64 || \ cmd == LX_F_SETLK64) { if (uucopy((void *)p3, (void *)&lxflk64, sizeof (struct lx_flock64)) != 0) return (-errno); ltos_flock64(&lxflk64, &fl64); rc = lx_fcntl_com(fd, cmd, (ulong_t)&fl64); if (rc >= 0) { stol_flock64(&fl64, &lxflk64); if (uucopy((void *)&lxflk64, (void *)p3, sizeof (struct lx_flock64)) != 0) return (-errno); } } else { rc = lx_fcntl_com(fd, cmd, (ulong_t)p3); } return (rc); }
/* * From the man page: * The Linux-specific prlimit() system call combines and extends the * functionality of setrlimit() and getrlimit(). It can be used to both set * and get the resource limits of an arbitrary process. * * If pid is 0, then the call applies to the calling process. */ int lx_prlimit64(uintptr_t p1, uintptr_t p2, uintptr_t p3, uintptr_t p4) { pid_t pid = (pid_t)p1; int resource = (int)p2; lx_rlimit64_t *nrlp = (lx_rlimit64_t *)p3; lx_rlimit64_t *orlp = (lx_rlimit64_t *)p4; int rv = 0; uint64_t rlim_cur, rlim_max; lx_rlimit64_t nrl, orl; if (pid != 0) { /* XXX TBD if needed */ lx_unsupported("setting prlimit %d for another process\n", resource); return (-ENOTSUP); } if (orlp != NULL) { /* we first get the current limits */ rv = getrlimit_common(resource, &rlim_cur, &rlim_max); if (rv != 0) return (rv); } if (nrlp != NULL) { if (uucopy((void *)p3, &nrl, sizeof (nrl)) != 0) return (-errno); if ((nrl.rlim_max != LX_RLIM64_INFINITY && nrl.rlim_cur == LX_RLIM64_INFINITY) || nrl.rlim_cur > nrl.rlim_max) return (-EINVAL); rv = setrlimit_common(resource, nrl.rlim_cur, nrl.rlim_max); } if (rv == 0 && orlp != NULL) { /* now return the original limits, if necessary */ orl.rlim_cur = rlim_cur; orl.rlim_max = rlim_max; if ((uucopy(&orl, orlp, sizeof (orl))) != 0) rv = -errno; } return (rv); }
static int lx_semctl_ipcstat(int semid, void *buf) { struct lx_semid_ds semds; struct semid_ds sol_semds; if (semctl(semid, 0, IPC_STAT, &sol_semds) != 0) return (-errno); bzero(&semds, sizeof (semds)); semds.sem_perm.key = sol_semds.sem_perm.key; semds.sem_perm.seq = sol_semds.sem_perm.seq; semds.sem_perm.uid = sol_semds.sem_perm.uid; semds.sem_perm.gid = sol_semds.sem_perm.gid; semds.sem_perm.cuid = sol_semds.sem_perm.cuid; semds.sem_perm.cgid = sol_semds.sem_perm.cgid; /* Linux only uses the bottom 9 bits */ semds.sem_perm.mode = sol_semds.sem_perm.mode & S_IAMB; semds.sem_otime = sol_semds.sem_otime; semds.sem_ctime = sol_semds.sem_ctime; semds.sem_nsems = sol_semds.sem_nsems; if (uucopy(&semds, buf, sizeof (semds))) return (-errno); return (0); }
/* * For the SETALL operation, we have to examine each of the semaphore * values to be sure it is legal. */ static int lx_semctl_setall(int semid, ushort_t *arg) { struct semid_ds semds; ushort_t *vals; int i, sz, r; /* * Find out how many semaphores are involved, reserve enough * memory for an internal copy of the array, and then copy it in * from the process. */ if (semctl(semid, 0, IPC_STAT, &semds) != 0) return (-errno); sz = semds.sem_nsems * sizeof (ushort_t); if ((vals = SAFE_ALLOCA(sz)) == NULL) return (-ENOMEM); if (uucopy(arg, vals, sz)) return (-errno); /* Validate each of the values. */ for (i = 0; i < semds.sem_nsems; i++) if (vals[i] > LX_SEMVMX) return (-ERANGE); r = semctl(semid, 0, SETALL, arg); return ((r < 0) ? -errno : r); }
long lx_sched_setparam(uintptr_t pid, uintptr_t param) { int err, policy; pid_t s_pid; lwpid_t s_tid; struct lx_sched_param lp; struct sched_param sp; if (((pid_t)pid < 0) || (param == NULL)) return (-EINVAL); if (lx_lpid_to_spair((pid_t)pid, &s_pid, &s_tid) < 0) return (-ESRCH); if (s_pid == getpid()) { struct sched_param dummy; if ((err = pthread_getschedparam(s_tid, &policy, &dummy)) != 0) return (-err); } else if ((policy = sched_getscheduler(s_pid)) < 0) return (-errno); lx_debug("sched_setparam(): current policy %d", policy); if (uucopy((void *)param, &lp, sizeof (lp)) != 0) return (-errno); /* * In Linux, the only valid SCHED_OTHER scheduler priority is 0 */ if ((policy == SCHED_OTHER) && (lp.lx_sched_prio != 0)) return (-EINVAL); if ((err = ltos_sparam(policy, (struct lx_sched_param *)&lp, &sp)) != 0) return (err); /* * Check if we're allowed to change the scheduler for the process. * * If we're operating on a thread, we can't just call * pthread_setschedparam() because as all threads reside within a * single Solaris process, Solaris will allow the modification * * If we're operating on a process, we can't just call sched_setparam() * because Solaris will allow the call to succeed if the scheduler * parameters do not differ from those being installed, but Linux wants * the call to fail. */ if ((err = check_schedperms(s_pid)) != 0) return (err); if (s_pid == getpid()) return (((err = pthread_setschedparam(s_tid, policy, &sp)) != 0) ? -err : 0); return ((sched_setparam(s_pid, &sp) == -1) ? -errno : 0); }
static int stol_sparam(int policy, struct sched_param *sp, struct lx_sched_param *lsp) { struct lx_sched_param ls; int smin = sched_get_priority_min(policy); int smax = sched_get_priority_max(policy); if (policy == SCHED_OTHER) { /* * In Linux, the only valid SCHED_OTHER scheduler priority is 0 */ ls.lx_sched_prio = 0; } else { /* * Convert Solaris's dynamic, inverted priority range to the * fixed Linux range of 1 - 99. * * The formula is (see above): * * (smax - s + 2smin) * 99 * l = ----------------------- * smax - smin */ ls.lx_sched_prio = ((smax - sp->sched_priority + 2*smin) * LX_PRI_MAX) / (smax - smin); } lx_debug("stol_sparam: policy %d: Solaris prio %d = linux prio %d " "(Solaris range %d,%d)\n", policy, sp->sched_priority, ls.lx_sched_prio, smin, smax); return ((uucopy(&ls, lsp, sizeof (struct lx_sched_param)) != 0) ? -errno : 0); }
static int lx_shmctl_ipcstat(int shmid, void *buf) { struct lx_shmid_ds shmds; struct shmid_ds sol_shmds; if (shmctl(shmid, IPC_STAT, &sol_shmds) != 0) return (-errno); bzero(&shmds, sizeof (shmds)); shmds.shm_perm.key = sol_shmds.shm_perm.key; shmds.shm_perm.seq = sol_shmds.shm_perm.seq; shmds.shm_perm.uid = sol_shmds.shm_perm.uid; shmds.shm_perm.gid = sol_shmds.shm_perm.gid; shmds.shm_perm.cuid = sol_shmds.shm_perm.cuid; shmds.shm_perm.cgid = sol_shmds.shm_perm.cgid; shmds.shm_perm.mode = sol_shmds.shm_perm.mode & S_IAMB; if (sol_shmds.shm_lkcnt > 0) shmds.shm_perm.mode |= LX_SHM_LOCKED; shmds.shm_segsz = sol_shmds.shm_segsz; shmds.shm_atime = sol_shmds.shm_atime; shmds.shm_dtime = sol_shmds.shm_dtime; shmds.shm_ctime = sol_shmds.shm_ctime; shmds.shm_cpid = sol_shmds.shm_cpid; shmds.shm_lpid = sol_shmds.shm_lpid; shmds.shm_nattch = (ushort_t)sol_shmds.shm_nattch; if (uucopy(&shmds, buf, sizeof (shmds))) return (-errno); return (0); }
int lx_setrlimit(uintptr_t p1, uintptr_t p2) { int resource = (int)p1; lx_rlimit_t rl; uint64_t rlim_cur, rlim_max; if (uucopy((void *)p2, &rl, sizeof (rl)) != 0) return (-errno); if ((rl.rlim_max != LX_RLIM_INFINITY_N && rl.rlim_cur == LX_RLIM_INFINITY_N) || rl.rlim_cur > rl.rlim_max) return (-EINVAL); if (rl.rlim_cur == LX_RLIM_INFINITY_N) rlim_cur = LX_RLIM64_INFINITY; else rlim_cur = rl.rlim_cur; if (rl.rlim_max == LX_RLIM_INFINITY_N) rlim_max = LX_RLIM64_INFINITY; else rlim_max = rl.rlim_max; return (setrlimit_common(resource, rlim_cur, rlim_max)); }
/* * We may need a different size socket address vs. the one passed in. */ static int calc_addr_size(struct sockaddr *a, int in_len, lx_addr_type_t *type) { struct sockaddr name; boolean_t abst_sock; int nlen; if (uucopy(a, &name, sizeof (struct sockaddr)) != 0) return (-errno); /* * Handle Linux abstract sockets, which are UNIX sockets whose path * begins with a NULL character. */ abst_sock = (name.sa_family == AF_UNIX) && (name.sa_data[0] == '\0'); /* * Convert_sockaddr will expand the socket path if it is abstract, so * we need to allocate extra memory for it. */ nlen = in_len; if (abst_sock) { nlen += ABST_PRFX_LEN; *type = lxa_abstract; } else { *type = lxa_none; } return (nlen); }
static int lx_socketpair(ulong_t *args) { int domain; int type; int options; int protocol = (int)args[2]; int *sv = (int *)args[3]; int fds[2]; int r; r = convert_sock_args((int)args[0], (int)args[1], protocol, &domain, &type, &options); if (r != 0) return (r); lx_debug("\tsocketpair(%d, %d, %d, 0x%p)", domain, type, protocol, sv); r = socketpair(domain, type | options, protocol, fds); if (r == 0) { if (uucopy(fds, sv, sizeof (fds)) != 0) { r = errno; (void) close(fds[0]); (void) close(fds[1]); return (-r); } return (0); } if (errno == EPROTONOSUPPORT) return (-ESOCKTNOSUPPORT); return (-errno); }
/* * setitimer() - the Linux implementation can handle tv_usec values greater * than 1,000,000 where Illumos would return EINVAL. * * There's still an issue here where Linux can handle a * tv_sec value greater than 100,000,000 but Illumos cannot, * but that would also mean setting an interval timer to fire * over _three years_ in the future so it's unlikely anything * other than Linux test suites will trip over it. */ long lx_setitimer(uintptr_t p1, uintptr_t p2, uintptr_t p3) { struct itimerval itv; struct itimerval *itp = (struct itimerval *)p2; if (itp != NULL) { if (uucopy(itp, &itv, sizeof (itv)) != 0) return (-errno); /* * Adjust any tv_usec fields >= 1,000,000 by adding any whole * seconds so indicated to tv_sec and leaving tv_usec as the * remainder. */ if (itv.it_interval.tv_usec >= MICROSEC) { itv.it_interval.tv_sec += itv.it_interval.tv_usec / MICROSEC; itv.it_interval.tv_usec %= MICROSEC; } if (itv.it_value.tv_usec >= MICROSEC) { itv.it_value.tv_sec += itv.it_value.tv_usec / MICROSEC; itv.it_value.tv_usec %= MICROSEC; } itp = &itv; } return ((setitimer((int)p1, itp, (struct itimerval *)p3) != 0) ? -errno : 0); }
/* * This is the 'old' getrlimit, variously called getrlimit or old_getrlimit * in Linux headers and code. The only difference between this and the new * getrlimit (variously called getrlimit or ugetrlimit) is the value of * RLIM_INFINITY, which is smaller for the older version. */ int lx_oldgetrlimit(uintptr_t p1, uintptr_t p2) { int resource = (int)p1; lx_rlimit_t *rlp = (lx_rlimit_t *)p2; int rv; lx_rlimit_t rl; uint64_t rlim_cur, rlim_max; rv = getrlimit_common(resource, &rlim_cur, &rlim_max); if (rv != 0) return (rv); if (rlim_cur == LX_RLIM64_INFINITY) rl.rlim_cur = LX_RLIM_INFINITY_O; else if (rlim_cur > BIG_INFINITY_O) rl.rlim_cur = LX_RLIM_INFINITY_O; else rl.rlim_cur = (ulong_t)rlim_cur; if (rlim_max == LX_RLIM64_INFINITY) rl.rlim_max = LX_RLIM_INFINITY_O; else if (rlim_max > BIG_INFINITY_O) rl.rlim_max = LX_RLIM_INFINITY_O; else rl.rlim_max = (ulong_t)rlim_max; if ((uucopy(&rl, rlp, sizeof (rl))) != 0) return (-errno); return (0); }
/* * copyoutseg() * Copy out segments to user address space * * Copies out to the greater of what's in the sysmsg and what's * specified by the user message. Returns number of bytes actually * copied, or -1 on error. */ copyoutsegs(struct sysmsg *sm) { uint cnt, total = 0; struct msg *m = &sm->sm_msg; int nsm_segs = sm->sm_nseg, nm_segs = m->m_nseg; struct seg **sm_pp = &(sm->sm_seg[0]), *sm_segs = *sm_pp++; seg_t *m_segs = m->m_seg; do { /* * Calculate how much we can move this time */ cnt = MIN(sm_segs->s_len, m_segs->s_buflen); /* * If non-zero, move the next batch */ if (cnt > 0) { char *from, *to; /* * Copy the memory */ from = sm_segs->s_pview.p_vaddr; from += sm_segs->s_off; to = m_segs->s_buf; if (uucopy(to, from, cnt)) { return(err(EFAULT)); } total += cnt; /* * Advance the sysmsg */ if ((sm_segs->s_len -= cnt) == 0) { sm_segs = *sm_pp++; if ((--nsm_segs) == 0) { return(total); } } else { sm_segs->s_off += cnt; } /* * Advance the user message */ if ((m_segs->s_buflen -= cnt) == 0) { ++m_segs; if ((--nm_segs) == 0) { return(total); } } else { m_segs->s_buf = ((char *)m_segs->s_buf) + cnt; } } } while (cnt > 0); return(total); }
long lx_fcntl(uintptr_t p1, uintptr_t p2, uintptr_t p3) { int fd = (int)p1; int cmd = (int)p2; ulong_t arg = (ulong_t)p3; struct lx_flock lxflk; struct flock fl; int lk = 0; int rc; /* * The 64-bit fcntl commands must go through fcntl64(). */ if (cmd == LX_F_GETLK64 || cmd == LX_F_SETLK64 || cmd == LX_F_SETLKW64) return (-EINVAL); if (cmd == LX_F_SETSIG || cmd == LX_F_GETSIG || cmd == LX_F_SETLEASE || cmd == LX_F_GETLEASE) { lx_unsupported("unsupported fcntl command: %d", cmd); return (-ENOTSUP); } if (cmd == LX_F_GETLK || cmd == LX_F_SETLK || cmd == LX_F_SETLKW) { if (uucopy((void *)p3, (void *)&lxflk, sizeof (struct lx_flock)) != 0) return (-errno); lk = 1; ltos_flock(&lxflk, &fl); arg = (ulong_t)&fl; } rc = lx_fcntl_com(fd, cmd, arg); if (lk && rc >= 0) { stol_flock(&fl, &lxflk); if (uucopy((void *)&lxflk, (void *)p3, sizeof (struct lx_flock)) != 0) return (-errno); } return (rc); }
/* * time() - This cannot be passthrough because on Linux a bad buffer will * set errno to EFAULT, and on Illumos the failure mode is documented * as "undefined." * * (At present, Illumos' time(2) will segmentation fault, as the call * is simply a libc wrapper atop the time() syscall that will * dereference the passed pointer if it is non-zero.) */ long lx_time(uintptr_t p1) { time_t ret = time((time_t *)0); if ((ret == (time_t)-1) || ((p1 != 0) && (uucopy(&ret, (time_t *)p1, sizeof (ret)) != 0))) return (-errno); return (ret); }
/* ARGSUSED */ long lx_sched_getaffinity(uintptr_t pid, uintptr_t len, uintptr_t maskp) { int sz; ulong_t *lmask, *zmask; int i; sz = syscall(SYS_brand, B_GET_AFFINITY_MASK, pid, len, maskp); if (sz == -1) return (-errno); /* * If the target LWP hasn't ever had an affinity mask set, the kernel * will return a mask of all 0's. If that is the case we must build a * default mask that has all valid bits turned on. */ lmask = SAFE_ALLOCA(sz); zmask = SAFE_ALLOCA(sz); if (lmask == NULL || zmask == NULL) return (-ENOMEM); bzero(zmask, sz); if (uucopy((void *)maskp, lmask, sz) != 0) return (-EFAULT); if (bcmp(lmask, zmask, sz) != 0) return (sz); for (i = 0; i < sz * 8; i++) { if (p_online(i, P_STATUS) != -1) { lmask[BITINDEX(i)] |= BITSHIFT(i); } } if (uucopy(lmask, (void *)maskp, sz) != 0) return (-EFAULT); return (sz); }
/* * times() - The Linux implementation avoids writing to NULL, while Illumos * returns EFAULT. */ long lx_times(uintptr_t p1) { clock_t ret; struct tms buf, *tp = (struct tms *)p1; ret = times(&buf); if ((ret == -1) || ((tp != NULL) && uucopy((void *)&buf, tp, sizeof (buf)) != 0)) return (-errno); return ((ret == -1) ? -errno : ret); }
static int lx_semctl_ipcinfo(void *buf) { struct lx_seminfo i; rctlblk_t *rblk; int rblksz; uint_t nids; int idbuf; int err; uint64_t val; rblksz = rctlblk_size(); if ((rblk = (rctlblk_t *)SAFE_ALLOCA(rblksz)) == NULL) return (-ENOMEM); bzero(&i, sizeof (i)); err = get_rctlval(rblk, "project.max-sem-ids", (ulong_t)MAXINT, &val); if (err < 0) return (err); i.semmni = (int)val; err = get_rctlval(rblk, "process.max-sem-nsems", (ulong_t)MAXINT, &val); if (err < 0) return (err); i.semmsl = (int)val; err = get_rctlval(rblk, "process.max-sem-ops", (ulong_t)MAXINT, &val); if (err < 0) return (err); i.semopm = (int)val; /* * We don't have corresponding rctls for these fields. The values * are taken from the formulas used to derive the defaults listed * in the Linux header file. We're lying, but trying to be * coherent about it. */ i.semmap = i.semmni; i.semmns = i.semmni * i.semmsl; i.semmnu = INT_MAX; i.semume = INT_MAX; i.semvmx = LX_SEMVMX; if (semids(&idbuf, 0, &nids) < 0) return (-errno); i.semusz = nids; i.semaem = INT_MAX; if (uucopy(&i, buf, sizeof (i)) != 0) return (-errno); return (nids); }
/* * Based on the lx_accept code with the addition of the flags handling. * See internal comments in that function for more explanation. */ static int lx_accept4(ulong_t *args) { int sockfd = (int)args[0]; struct sockaddr *name = (struct sockaddr *)args[1]; socklen_t namelen = 0; int lx_flags, flags = 0; int r; lx_flags = (int)args[3]; lx_debug("\taccept4(%d, 0x%p, 0x%p 0x%x", sockfd, args[1], args[2], lx_flags); if ((name != NULL) && (uucopy((void *)args[2], &namelen, sizeof (socklen_t)) != 0)) return ((errno == EFAULT) ? -EINVAL : -errno); lx_debug("\taccept4 namelen = %d", namelen); if (lx_flags & LX_SOCK_NONBLOCK) flags |= SOCK_NONBLOCK; if (lx_flags & LX_SOCK_CLOEXEC) flags |= SOCK_CLOEXEC; if ((r = accept4(sockfd, name, &namelen, flags)) < 0) return ((errno == EFAULT) ? -EINVAL : -errno); lx_debug("\taccept4 namelen returned %d bytes", namelen); if ((name != NULL) && (namelen != 0) && (uucopy(&namelen, (void *)args[2], sizeof (socklen_t)) != 0)) return ((errno == EFAULT) ? -EINVAL : -errno); return (r); }
static int lx_getsockname(ulong_t *args) { int sockfd = (int)args[0]; struct sockaddr *name = NULL; socklen_t namelen, namelen_orig; if (uucopy((void *)args[2], &namelen, sizeof (socklen_t)) != 0) return (-errno); namelen_orig = namelen; lx_debug("\tgetsockname(%d, 0x%p, 0x%p (=%d))", sockfd, args[1], args[2], namelen); if (namelen > 0) { if ((name = SAFE_ALLOCA(namelen)) == NULL) return (-EINVAL); bzero(name, namelen); } if (getsockname(sockfd, name, &namelen) < 0) return (-errno); /* * If the name that getsockname() wants to return is larger * than namelen, getsockname() will copy out the maximum amount * of data possible and then update namelen to indicate the * actually size of all the data that it wanted to copy out. */ if (uucopy(name, (void *)args[1], namelen_orig) != 0) return (-errno); if (uucopy(&namelen, (void *)args[2], sizeof (socklen_t)) != 0) return (-errno); return (0); }
long lx_settimeofday(uintptr_t p1, uintptr_t p2) { struct timeval tv; struct lx_timezone tz; if ((p1 != NULL) && (uucopy((struct timeval *)p1, &tv, sizeof (tv)) < 0)) return (-errno); /* * The Linux man page states use of the second parameter is obsolete, * but settimeofday(2) should still return EFAULT if it is set * to a bad non-NULL pointer (sigh...) */ if ((p2 != NULL) && (uucopy((struct lx_timezone *)p2, &tz, sizeof (tz)) < 0)) return (-errno); if ((p1 != NULL) && (settimeofday(&tv, NULL) < 0)) return (-errno); return (0); }
/* * Build and return a shm_info structure. We only return the bare * essentials required by ipcs. The rest of the info is not readily * available. */ static int lx_shmctl_shminfo(void *buf) { struct lx_shm_info shminfo; uint_t nids; int idbuf; bzero(&shminfo, sizeof (shminfo)); if (shmids(&idbuf, 0, &nids) < 0) return (-errno); shminfo.used_ids = nids; if (uucopy(&shminfo, buf, sizeof (shminfo)) != 0) return (-errno); return (nids); }
static int lx_shmctl_ipcset(int shmid, void *buf) { struct lx_shmid_ds shmds; struct shmid_ds sol_shmds; int r; if (uucopy(buf, &shmds, sizeof (shmds))) return (-errno); bzero(&sol_shmds, sizeof (sol_shmds)); sol_shmds.shm_perm.uid = shmds.shm_perm.uid; sol_shmds.shm_perm.gid = shmds.shm_perm.gid; sol_shmds.shm_perm.mode = shmds.shm_perm.mode & S_IAMB; r = shmctl(shmid, IPC_SET, &sol_shmds); return (r < 0 ? -errno : r); }
long lx_sched_rr_get_interval(uintptr_t pid, uintptr_t timespec) { struct timespec ts; pid_t s_pid; if ((pid_t)pid < 0) return (-EINVAL); if (lx_lpid_to_spid((pid_t)pid, &s_pid) < 0) return (-ESRCH); if (uucopy((struct timespec *)timespec, &ts, sizeof (struct timespec)) != 0) return (-errno); return ((sched_rr_get_interval(s_pid, &ts) == -1) ? -errno : 0); }
static int lx_semctl_ipcset(int semid, void *buf) { struct lx_semid_ds semds; struct semid_ds sol_semds; int r; if (uucopy(buf, &semds, sizeof (semds))) return (-errno); bzero(&sol_semds, sizeof (sol_semds)); sol_semds.sem_perm.uid = semds.sem_perm.uid; sol_semds.sem_perm.gid = semds.sem_perm.gid; sol_semds.sem_perm.mode = semds.sem_perm.mode; r = semctl(semid, 0, IPC_SET, &sol_semds); return ((r < 0) ? -errno : r); }
static int ltos_sparam(int policy, struct lx_sched_param *lsp, struct sched_param *sp) { struct lx_sched_param ls; int smin = sched_get_priority_min(policy); int smax = sched_get_priority_max(policy); if (uucopy(lsp, &ls, sizeof (struct lx_sched_param)) != 0) return (-errno); bzero(sp, sizeof (struct sched_param)); /* * Linux has a fixed priority range, 0 - 99, which we need to convert to * Solaris's dynamic range. Linux considers lower numbers to be * higher priority, so we'll invert the priority within Solaris's range. * * The formula to convert between ranges is: * * L * (smax - smin) * S = ----------------- + smin * (lmax - lmin) * * where S is the Solaris equivalent of the linux priority L. * * To invert the priority, we use: * S' = smax - S + smin * * Together, these two formulas become: * * L * (smax - smin) * S = smax - ----------------- + 2smin * 99 */ sp->sched_priority = smax - ((ls.lx_sched_prio * (smax - smin)) / LX_PRI_MAX) + 2*smin; lx_debug("ltos_sparam: linux prio %d = Solaris prio %d " "(Solaris range %d,%d)\n", ls.lx_sched_prio, sp->sched_priority, smin, smax); return (0); }
static int lx_msgctl_ipcset(int msgid, void *buf) { struct lx_msqid_ds msgids; struct msqid_ds sol_msgids; int r; if (uucopy(buf, &msgids, sizeof (msgids))) return (-errno); bzero(&sol_msgids, sizeof (sol_msgids)); sol_msgids.msg_perm.uid = LX_UID16_TO_UID32(msgids.msg_perm.uid); sol_msgids.msg_perm.gid = LX_UID16_TO_UID32(msgids.msg_perm.gid); /* Linux only uses the bottom 9 bits */ sol_msgids.msg_perm.mode = msgids.msg_perm.mode & S_IAMB; sol_msgids.msg_qbytes = msgids.msg_qbytes; r = msgctl(msgid, IPC_SET, &sol_msgids); return (r < 0 ? -errno : r); }