6253 F_GETLK doesn't always return lock owner
[illumos-gate.git] / usr / src / uts / common / syscall / rlimit.c
blobeac35847645c12f4f7330fcce0a7b803f2f8c06d
1 /*
2 * CDDL HEADER START
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
7 * with the License.
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
20 * CDDL HEADER END
23 * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
28 /* All Rights Reserved */
31 #pragma ident "%Z%%M% %I% %E% SMI"
33 #include <sys/param.h>
34 #include <sys/types.h>
35 #include <sys/inttypes.h>
36 #include <sys/sysmacros.h>
37 #include <sys/systm.h>
38 #include <sys/tuneable.h>
39 #include <sys/user.h>
40 #include <sys/errno.h>
41 #include <sys/vnode.h>
42 #include <sys/file.h>
43 #include <sys/proc.h>
44 #include <sys/resource.h>
45 #include <sys/ulimit.h>
46 #include <sys/debug.h>
47 #include <sys/rctl.h>
49 #include <vm/as.h>
52 * Perhaps ulimit could be moved into a user library, as calls to
53 * getrlimit and setrlimit, were it not for binary compatibility
54 * restrictions.
56 long
57 ulimit(int cmd, long arg)
59 proc_t *p = curproc;
60 long retval;
62 switch (cmd) {
64 case UL_GFILLIM: /* Return current file size limit. */
66 rlim64_t filesize;
68 mutex_enter(&p->p_lock);
69 filesize = rctl_enforced_value(rctlproc_legacy[RLIMIT_FSIZE],
70 p->p_rctls, p);
71 mutex_exit(&p->p_lock);
73 if (get_udatamodel() == DATAMODEL_ILP32) {
75 * File size is returned in blocks for ulimit.
76 * This function is deprecated and therefore LFS API
77 * didn't define the behaviour of ulimit.
78 * Here we return maximum value of file size possible
79 * so that applications that do not check errors
80 * continue to work.
82 if (filesize > MAXOFF32_T)
83 filesize = MAXOFF32_T;
84 retval = ((int)filesize >> SCTRSHFT);
85 } else
86 retval = filesize >> SCTRSHFT;
87 break;
90 case UL_SFILLIM: /* Set new file size limit. */
92 int error = 0;
93 rlim64_t lim = (rlim64_t)arg;
94 struct rlimit64 rl64;
95 rctl_alloc_gp_t *gp = rctl_rlimit_set_prealloc(1);
97 if (lim >= (((rlim64_t)MAXOFFSET_T) >> SCTRSHFT))
98 lim = (rlim64_t)RLIM64_INFINITY;
99 else
100 lim <<= SCTRSHFT;
102 rl64.rlim_max = rl64.rlim_cur = lim;
103 mutex_enter(&p->p_lock);
104 if (error = rctl_rlimit_set(rctlproc_legacy[RLIMIT_FSIZE], p,
105 &rl64, gp, RCTL_LOCAL_DENY | RCTL_LOCAL_SIGNAL, SIGXFSZ,
106 CRED())) {
107 mutex_exit(&p->p_lock);
108 rctl_prealloc_destroy(gp);
109 return (set_errno(error));
111 mutex_exit(&p->p_lock);
112 rctl_prealloc_destroy(gp);
113 retval = arg;
114 break;
117 case UL_GMEMLIM: /* Return maximum possible break value. */
119 struct seg *seg;
120 struct seg *nextseg;
121 struct as *as = p->p_as;
122 caddr_t brkend;
123 caddr_t brkbase;
124 size_t size;
125 rlim64_t size_ctl;
126 rlim64_t vmem_ctl;
129 * Find the segment with a virtual address
130 * greater than the end of the current break.
132 nextseg = NULL;
133 mutex_enter(&p->p_lock);
134 brkbase = (caddr_t)p->p_brkbase;
135 brkend = (caddr_t)p->p_brkbase + p->p_brksize;
136 mutex_exit(&p->p_lock);
139 * Since we can't return less than the current break,
140 * initialize the return value to the current break
142 retval = (long)brkend;
144 AS_LOCK_ENTER(as, &as->a_lock, RW_READER);
145 for (seg = as_findseg(as, brkend, 0); seg != NULL;
146 seg = AS_SEGNEXT(as, seg)) {
147 if (seg->s_base >= brkend) {
148 nextseg = seg;
149 break;
153 mutex_enter(&p->p_lock);
154 size_ctl = rctl_enforced_value(rctlproc_legacy[RLIMIT_DATA],
155 p->p_rctls, p);
156 vmem_ctl = rctl_enforced_value(rctlproc_legacy[RLIMIT_VMEM],
157 p->p_rctls, p);
158 mutex_exit(&p->p_lock);
161 * First, calculate the maximum break value based on
162 * the user's RLIMIT_DATA, but also taking into account
163 * that this value cannot be greater than as->a_userlimit.
164 * We also take care to make sure that we don't overflow
165 * in the calculation.
168 * Since we are casting the RLIMIT_DATA value to a
169 * ulong (a 32-bit value in the 32-bit kernel) we have
170 * to pass this assertion.
172 ASSERT32((size_t)size_ctl <= UINT32_MAX);
174 size = (size_t)size_ctl;
175 if (as->a_userlimit - brkbase > size)
176 retval = MAX((size_t)retval, (size_t)(brkbase + size));
177 /* don't return less than current */
178 else
179 retval = (long)as->a_userlimit;
182 * The max break cannot extend into the next segment
184 if (nextseg != NULL)
185 retval = MIN((uintptr_t)retval,
186 (uintptr_t)nextseg->s_base);
189 * Handle the case where there is an limit on RLIMIT_VMEM
191 if (vmem_ctl < UINT64_MAX) {
192 /* calculate brkend based on the end of page */
193 caddr_t brkendpg = (caddr_t)roundup((uintptr_t)brkend,
194 PAGESIZE);
196 * Large Files: The following assertion has to pass
197 * through to ensure the correctness of the cast.
199 ASSERT32(vmem_ctl <= UINT32_MAX);
201 size = (size_t)(vmem_ctl & PAGEMASK);
203 if (as->a_size < size)
204 size -= as->a_size;
205 else
206 size = 0;
208 * Take care to not overflow the calculation
210 if (as->a_userlimit - brkendpg > size)
211 retval = MIN((size_t)retval,
212 (size_t)(brkendpg + size));
215 AS_LOCK_EXIT(as, &as->a_lock);
217 /* truncate to same boundary as sbrk */
219 switch (get_udatamodel()) {
220 default:
221 case DATAMODEL_ILP32:
222 retval = retval & ~(8-1);
223 break;
224 case DATAMODEL_LP64:
225 retval = retval & ~(16-1);
226 break;
228 break;
231 case UL_GDESLIM: /* Return approximate number of open files */
233 rlim64_t fdno_ctl;
235 mutex_enter(&curproc->p_lock);
236 fdno_ctl = rctl_enforced_value(rctlproc_legacy[RLIMIT_NOFILE],
237 curproc->p_rctls, curproc);
238 ASSERT(fdno_ctl <= INT_MAX);
239 retval = (rlim_t)fdno_ctl;
240 mutex_exit(&curproc->p_lock);
241 break;
244 default:
245 return (set_errno(EINVAL));
248 return (retval);
251 #ifdef _SYSCALL32_IMPL
254 ulimit32(int cmd, int arg)
256 return ((int)ulimit(cmd, (long)arg));
259 #endif /* _SYSCALL32_IMPL */
261 #if defined(_ILP32) || defined(_SYSCALL32_IMPL)
264 * Large Files: getrlimit returns RLIM_SAVED_CUR or RLIM_SAVED_MAX when
265 * rlim_cur or rlim_max is not representable in 32-bit rlim_t. These
266 * values are just tokens which will be used in setrlimit to set the
267 * correct limits. The current limits are saved in the saved_rlimit members
268 * in user structures when the token is returned. setrlimit restores
269 * the limit values to these saved values when the token is passed.
270 * Consider the following common scenario of the apps:
272 * limit = getrlimit();
273 * savedlimit = limit;
274 * limit = limit1;
275 * setrlimit(limit)
276 * // execute all processes in the new rlimit state.
277 * setrlimit(savedlimit) // restore the old values.
279 * Most apps don't check error returns from getrlimit or setrlimit
280 * and this is why we return tokens when the correct value
281 * cannot be represented in rlim_t. For more discussion refer to
282 * the LFS API document.
284 * In the 64-bit kernel, all existing resource limits are treated in this
285 * manner. In the 32-bit kernel, CPU time is treated equivalently to the
286 * file size limit above; the VM-related limits are not. The macro,
287 * RLIM_SAVED(x), returns true if the resource limit should be handled in
288 * this way on the current kernel.
291 getrlimit32(int resource, struct rlimit32 *rlp)
293 struct rlimit32 rlim32;
294 struct rlimit64 rlim64;
295 struct proc *p = curproc;
296 struct user *up = PTOU(p);
297 int savecur = 0;
298 int savemax = 0;
300 if (resource < 0 || resource >= RLIM_NLIMITS)
301 return (set_errno(EINVAL));
303 mutex_enter(&p->p_lock);
304 (void) rctl_rlimit_get(rctlproc_legacy[resource], p, &rlim64);
305 mutex_exit(&p->p_lock);
307 if (rlim64.rlim_max > (rlim64_t)UINT32_MAX) {
309 if (rlim64.rlim_max == RLIM64_INFINITY)
310 rlim32.rlim_max = RLIM32_INFINITY;
311 else {
312 savemax = 1;
313 rlim32.rlim_max = RLIM32_SAVED_MAX;
314 /*CONSTCOND*/
315 ASSERT(RLIM_SAVED(resource));
318 if (rlim64.rlim_cur == RLIM64_INFINITY)
319 rlim32.rlim_cur = RLIM32_INFINITY;
320 else if (rlim64.rlim_cur == rlim64.rlim_max) {
321 savecur = 1;
322 rlim32.rlim_cur = RLIM32_SAVED_MAX;
323 /*CONSTCOND*/
324 ASSERT(RLIM_SAVED(resource));
325 } else if (rlim64.rlim_cur > (rlim64_t)UINT32_MAX) {
326 savecur = 1;
327 rlim32.rlim_cur = RLIM32_SAVED_CUR;
328 /*CONSTCOND*/
329 ASSERT(RLIM_SAVED(resource));
330 } else
331 rlim32.rlim_cur = rlim64.rlim_cur;
334 * save the current limits in user structure.
336 /*CONSTCOND*/
337 if (RLIM_SAVED(resource)) {
338 mutex_enter(&p->p_lock);
339 if (savemax)
340 up->u_saved_rlimit[resource].rlim_max =
341 rlim64.rlim_max;
342 if (savecur)
343 up->u_saved_rlimit[resource].rlim_cur =
344 rlim64.rlim_cur;
345 mutex_exit(&p->p_lock);
347 } else {
348 ASSERT(rlim64.rlim_cur <= (rlim64_t)UINT32_MAX);
349 rlim32.rlim_max = rlim64.rlim_max;
350 rlim32.rlim_cur = rlim64.rlim_cur;
353 if (copyout(&rlim32, rlp, sizeof (rlim32)))
354 return (set_errno(EFAULT));
356 return (0);
360 * See comments above getrlimit32(). When the tokens are passed in the
361 * rlimit structure the values are considered equal to the values
362 * stored in saved_rlimit members of user structure.
363 * When the user passes RLIM_INFINITY to set the resource limit to
364 * unlimited internally understand this value as RLIM64_INFINITY and
365 * let rlimit() do the job.
368 setrlimit32(int resource, struct rlimit32 *rlp)
370 struct rlimit32 rlim32;
371 struct rlimit64 rlim64;
372 struct rlimit64 saved_rlim;
373 int error;
374 struct proc *p = ttoproc(curthread);
375 struct user *up = PTOU(p);
376 rctl_alloc_gp_t *gp;
378 if (resource < 0 || resource >= RLIM_NLIMITS)
379 return (set_errno(EINVAL));
380 if (copyin(rlp, &rlim32, sizeof (rlim32)))
381 return (set_errno(EFAULT));
383 gp = rctl_rlimit_set_prealloc(1);
386 * Disallow resource limit tunnelling
388 /*CONSTCOND*/
389 if (RLIM_SAVED(resource)) {
390 mutex_enter(&p->p_lock);
391 saved_rlim = up->u_saved_rlimit[resource];
392 mutex_exit(&p->p_lock);
393 } else {
394 saved_rlim.rlim_max = (rlim64_t)rlim32.rlim_max;
395 saved_rlim.rlim_cur = (rlim64_t)rlim32.rlim_cur;
398 switch (rlim32.rlim_cur) {
399 case RLIM32_INFINITY:
400 rlim64.rlim_cur = RLIM64_INFINITY;
401 break;
402 case RLIM32_SAVED_CUR:
403 rlim64.rlim_cur = saved_rlim.rlim_cur;
404 break;
405 case RLIM32_SAVED_MAX:
406 rlim64.rlim_cur = saved_rlim.rlim_max;
407 break;
408 default:
409 rlim64.rlim_cur = (rlim64_t)rlim32.rlim_cur;
410 break;
413 switch (rlim32.rlim_max) {
414 case RLIM32_INFINITY:
415 rlim64.rlim_max = RLIM64_INFINITY;
416 break;
417 case RLIM32_SAVED_MAX:
418 rlim64.rlim_max = saved_rlim.rlim_max;
419 break;
420 case RLIM32_SAVED_CUR:
421 rlim64.rlim_max = saved_rlim.rlim_cur;
422 break;
423 default:
424 rlim64.rlim_max = (rlim64_t)rlim32.rlim_max;
425 break;
428 mutex_enter(&p->p_lock);
429 if (error = rctl_rlimit_set(rctlproc_legacy[resource], p, &rlim64, gp,
430 rctlproc_flags[resource], rctlproc_signals[resource], CRED())) {
431 mutex_exit(&p->p_lock);
432 rctl_prealloc_destroy(gp);
433 return (set_errno(error));
435 mutex_exit(&p->p_lock);
436 rctl_prealloc_destroy(gp);
438 return (0);
441 #endif /* _ILP32 && _SYSCALL32_IMPL */
444 getrlimit64(int resource, struct rlimit64 *rlp)
446 struct rlimit64 rlim64;
447 struct proc *p = ttoproc(curthread);
449 if (resource < 0 || resource >= RLIM_NLIMITS)
450 return (set_errno(EINVAL));
452 mutex_enter(&p->p_lock);
453 (void) rctl_rlimit_get(rctlproc_legacy[resource], p, &rlim64);
454 mutex_exit(&p->p_lock);
456 if (copyout(&rlim64, rlp, sizeof (rlim64)))
457 return (set_errno(EFAULT));
458 return (0);
462 setrlimit64(int resource, struct rlimit64 *rlp)
464 struct rlimit64 rlim64;
465 struct proc *p = ttoproc(curthread);
466 int error;
467 rctl_alloc_gp_t *gp;
469 if (resource < 0 || resource >= RLIM_NLIMITS)
470 return (set_errno(EINVAL));
471 if (copyin(rlp, &rlim64, sizeof (rlim64)))
472 return (set_errno(EFAULT));
474 gp = rctl_rlimit_set_prealloc(1);
476 mutex_enter(&p->p_lock);
477 if (error = rctl_rlimit_set(rctlproc_legacy[resource], p, &rlim64, gp,
478 rctlproc_flags[resource], rctlproc_signals[resource], CRED())) {
479 mutex_exit(&p->p_lock);
480 rctl_prealloc_destroy(gp);
481 return (set_errno(error));
483 mutex_exit(&p->p_lock);
484 rctl_prealloc_destroy(gp);
485 return (0);