#include <sys/param.h>
#include <sys/types.h>
#include <sys/inttypes.h>
#include <sys/sysmacros.h>
#include <sys/systm.h>
#include <sys/tuneable.h>
#include <sys/user.h>
#include <sys/errno.h>
#include <sys/vnode.h>
#include <sys/file.h>
#include <sys/proc.h>
#include <sys/resource.h>
#include <sys/ulimit.h>
#include <sys/debug.h>
#include <sys/rctl.h>
#include <vm/as.h>
long
ulimit(int cmd, long arg)
{
proc_t *p = curproc;
long retval;
switch (cmd) {
case UL_GFILLIM:
{
rlim64_t filesize;
mutex_enter(&p->p_lock);
filesize = rctl_enforced_value(rctlproc_legacy[RLIMIT_FSIZE],
p->p_rctls, p);
mutex_exit(&p->p_lock);
if (get_udatamodel() == DATAMODEL_ILP32) {
if (filesize > MAXOFF32_T)
filesize = MAXOFF32_T;
retval = ((int)filesize >> SCTRSHFT);
} else
retval = filesize >> SCTRSHFT;
break;
}
case UL_SFILLIM:
{
int error = 0;
rlim64_t lim = (rlim64_t)arg;
struct rlimit64 rl64;
rctl_alloc_gp_t *gp = rctl_rlimit_set_prealloc(1);
if (lim >= (((rlim64_t)MAXOFFSET_T) >> SCTRSHFT))
lim = (rlim64_t)RLIM64_INFINITY;
else
lim <<= SCTRSHFT;
rl64.rlim_max = rl64.rlim_cur = lim;
mutex_enter(&p->p_lock);
if (error = rctl_rlimit_set(rctlproc_legacy[RLIMIT_FSIZE], p,
&rl64, gp, RCTL_LOCAL_DENY | RCTL_LOCAL_SIGNAL, SIGXFSZ,
CRED())) {
mutex_exit(&p->p_lock);
rctl_prealloc_destroy(gp);
return (set_errno(error));
}
mutex_exit(&p->p_lock);
rctl_prealloc_destroy(gp);
retval = arg;
break;
}
case UL_GMEMLIM:
{
struct seg *seg;
struct seg *nextseg;
struct as *as = p->p_as;
caddr_t brkend;
caddr_t brkbase;
size_t size;
rlim64_t size_ctl;
rlim64_t vmem_ctl;
nextseg = NULL;
mutex_enter(&p->p_lock);
brkbase = (caddr_t)p->p_brkbase;
brkend = (caddr_t)p->p_brkbase + p->p_brksize;
mutex_exit(&p->p_lock);
retval = (long)brkend;
AS_LOCK_ENTER(as, RW_READER);
for (seg = as_findseg(as, brkend, 0); seg != NULL;
seg = AS_SEGNEXT(as, seg)) {
if (seg->s_base >= brkend) {
nextseg = seg;
break;
}
}
mutex_enter(&p->p_lock);
size_ctl = rctl_enforced_value(rctlproc_legacy[RLIMIT_DATA],
p->p_rctls, p);
vmem_ctl = rctl_enforced_value(rctlproc_legacy[RLIMIT_VMEM],
p->p_rctls, p);
mutex_exit(&p->p_lock);
ASSERT32((size_t)size_ctl <= UINT32_MAX);
size = (size_t)size_ctl;
if (as->a_userlimit - brkbase > size)
retval = MAX((size_t)retval, (size_t)(brkbase + size));
else
retval = (long)as->a_userlimit;
if (nextseg != NULL)
retval = MIN((uintptr_t)retval,
(uintptr_t)nextseg->s_base);
if (vmem_ctl < UINT64_MAX) {
caddr_t brkendpg = (caddr_t)roundup((uintptr_t)brkend,
PAGESIZE);
ASSERT32(vmem_ctl <= UINT32_MAX);
size = (size_t)(vmem_ctl & PAGEMASK);
if (as->a_size < size)
size -= as->a_size;
else
size = 0;
if (as->a_userlimit - brkendpg > size)
retval = MIN((size_t)retval,
(size_t)(brkendpg + size));
}
AS_LOCK_EXIT(as);
switch (get_udatamodel()) {
default:
case DATAMODEL_ILP32:
retval = retval & ~(8-1);
break;
case DATAMODEL_LP64:
retval = retval & ~(16-1);
break;
}
break;
}
case UL_GDESLIM:
{
rlim64_t fdno_ctl;
mutex_enter(&curproc->p_lock);
fdno_ctl = rctl_enforced_value(rctlproc_legacy[RLIMIT_NOFILE],
curproc->p_rctls, curproc);
ASSERT(fdno_ctl <= INT_MAX);
retval = (rlim_t)fdno_ctl;
mutex_exit(&curproc->p_lock);
break;
}
default:
return (set_errno(EINVAL));
}
return (retval);
}
#ifdef _SYSCALL32_IMPL
int
ulimit32(int cmd, int arg)
{
return ((int)ulimit(cmd, (long)arg));
}
#endif
#if defined(_ILP32) || defined(_SYSCALL32_IMPL)
int
getrlimit32(int resource, struct rlimit32 *rlp)
{
struct rlimit32 rlim32;
struct rlimit64 rlim64;
struct proc *p = curproc;
struct user *up = PTOU(p);
int savecur = 0;
int savemax = 0;
if (resource < 0 || resource >= RLIM_NLIMITS)
return (set_errno(EINVAL));
mutex_enter(&p->p_lock);
(void) rctl_rlimit_get(rctlproc_legacy[resource], p, &rlim64);
mutex_exit(&p->p_lock);
if (rlim64.rlim_max > (rlim64_t)UINT32_MAX) {
if (rlim64.rlim_max == RLIM64_INFINITY)
rlim32.rlim_max = RLIM32_INFINITY;
else {
savemax = 1;
rlim32.rlim_max = RLIM32_SAVED_MAX;
ASSERT(RLIM_SAVED(resource));
}
if (rlim64.rlim_cur == RLIM64_INFINITY)
rlim32.rlim_cur = RLIM32_INFINITY;
else if (rlim64.rlim_cur == rlim64.rlim_max) {
savecur = 1;
rlim32.rlim_cur = RLIM32_SAVED_MAX;
ASSERT(RLIM_SAVED(resource));
} else if (rlim64.rlim_cur > (rlim64_t)UINT32_MAX) {
savecur = 1;
rlim32.rlim_cur = RLIM32_SAVED_CUR;
ASSERT(RLIM_SAVED(resource));
} else
rlim32.rlim_cur = rlim64.rlim_cur;
if (RLIM_SAVED(resource)) {
mutex_enter(&p->p_lock);
if (savemax)
up->u_saved_rlimit[resource].rlim_max =
rlim64.rlim_max;
if (savecur)
up->u_saved_rlimit[resource].rlim_cur =
rlim64.rlim_cur;
mutex_exit(&p->p_lock);
}
} else {
ASSERT(rlim64.rlim_cur <= (rlim64_t)UINT32_MAX);
rlim32.rlim_max = rlim64.rlim_max;
rlim32.rlim_cur = rlim64.rlim_cur;
}
if (copyout(&rlim32, rlp, sizeof (rlim32)))
return (set_errno(EFAULT));
return (0);
}
int
setrlimit32(int resource, struct rlimit32 *rlp)
{
struct rlimit32 rlim32;
struct rlimit64 rlim64;
struct rlimit64 saved_rlim;
int error;
struct proc *p = ttoproc(curthread);
struct user *up = PTOU(p);
rctl_alloc_gp_t *gp;
if (resource < 0 || resource >= RLIM_NLIMITS)
return (set_errno(EINVAL));
if (copyin(rlp, &rlim32, sizeof (rlim32)))
return (set_errno(EFAULT));
gp = rctl_rlimit_set_prealloc(1);
if (RLIM_SAVED(resource)) {
mutex_enter(&p->p_lock);
saved_rlim = up->u_saved_rlimit[resource];
mutex_exit(&p->p_lock);
} else {
saved_rlim.rlim_max = (rlim64_t)rlim32.rlim_max;
saved_rlim.rlim_cur = (rlim64_t)rlim32.rlim_cur;
}
switch (rlim32.rlim_cur) {
case RLIM32_INFINITY:
rlim64.rlim_cur = RLIM64_INFINITY;
break;
case RLIM32_SAVED_CUR:
rlim64.rlim_cur = saved_rlim.rlim_cur;
break;
case RLIM32_SAVED_MAX:
rlim64.rlim_cur = saved_rlim.rlim_max;
break;
default:
rlim64.rlim_cur = (rlim64_t)rlim32.rlim_cur;
break;
}
switch (rlim32.rlim_max) {
case RLIM32_INFINITY:
rlim64.rlim_max = RLIM64_INFINITY;
break;
case RLIM32_SAVED_MAX:
rlim64.rlim_max = saved_rlim.rlim_max;
break;
case RLIM32_SAVED_CUR:
rlim64.rlim_max = saved_rlim.rlim_cur;
break;
default:
rlim64.rlim_max = (rlim64_t)rlim32.rlim_max;
break;
}
mutex_enter(&p->p_lock);
if (error = rctl_rlimit_set(rctlproc_legacy[resource], p, &rlim64, gp,
rctlproc_flags[resource], rctlproc_signals[resource], CRED())) {
mutex_exit(&p->p_lock);
rctl_prealloc_destroy(gp);
return (set_errno(error));
}
mutex_exit(&p->p_lock);
rctl_prealloc_destroy(gp);
return (0);
}
#endif
int
getrlimit64(int resource, struct rlimit64 *rlp)
{
struct rlimit64 rlim64;
struct proc *p = ttoproc(curthread);
if (resource < 0 || resource >= RLIM_NLIMITS)
return (set_errno(EINVAL));
mutex_enter(&p->p_lock);
(void) rctl_rlimit_get(rctlproc_legacy[resource], p, &rlim64);
mutex_exit(&p->p_lock);
if (copyout(&rlim64, rlp, sizeof (rlim64)))
return (set_errno(EFAULT));
return (0);
}
int
setrlimit64(int resource, struct rlimit64 *rlp)
{
struct rlimit64 rlim64;
struct proc *p = ttoproc(curthread);
int error;
rctl_alloc_gp_t *gp;
if (resource < 0 || resource >= RLIM_NLIMITS)
return (set_errno(EINVAL));
if (copyin(rlp, &rlim64, sizeof (rlim64)))
return (set_errno(EFAULT));
gp = rctl_rlimit_set_prealloc(1);
mutex_enter(&p->p_lock);
if (error = rctl_rlimit_set(rctlproc_legacy[resource], p, &rlim64, gp,
rctlproc_flags[resource], rctlproc_signals[resource], CRED())) {
mutex_exit(&p->p_lock);
rctl_prealloc_destroy(gp);
return (set_errno(error));
}
mutex_exit(&p->p_lock);
rctl_prealloc_destroy(gp);
return (0);
}