diff options
author | Jiri Slaby <jslaby@suse.cz> | 2010-03-24 11:11:29 -0400 |
---|---|---|
committer | Jiri Slaby <jirislaby@gmail.com> | 2010-07-16 03:48:48 -0400 |
commit | 5b41535aac0c07135ff6a4c5c2ae115d1c20c0bc (patch) | |
tree | f8db7ec9e07d33aa2af061b49895a0baebafd4cc /kernel/sys.c | |
parent | 6a1d5e2c85d06da35cdfd93f1a27675bfdc3ad8c (diff) |
rlimits: redo do_setrlimit to more generic do_prlimit
It now allows also reading of limits. I.e. all read and writes will
later use this function.
It takes two parameters, new and old limits which can be both NULL.
If new is non-NULL, the value in it is set to rlimits.
If old is non-NULL, current rlimits are stored there.
If both are non-NULL, old are stored prior to setting the new ones,
atomically.
(Similar to sigaction.)
Signed-off-by: Jiri Slaby <jslaby@suse.cz>
Diffstat (limited to 'kernel/sys.c')
-rw-r--r-- | kernel/sys.c | 71 |
1 files changed, 37 insertions, 34 deletions
diff --git a/kernel/sys.c b/kernel/sys.c index c762eebdebf7..bc7d1be0960e 100644 --- a/kernel/sys.c +++ b/kernel/sys.c | |||
@@ -1273,18 +1273,21 @@ SYSCALL_DEFINE2(old_getrlimit, unsigned int, resource, | |||
1273 | #endif | 1273 | #endif |
1274 | 1274 | ||
1275 | /* make sure you are allowed to change @tsk limits before calling this */ | 1275 | /* make sure you are allowed to change @tsk limits before calling this */ |
1276 | int do_setrlimit(struct task_struct *tsk, unsigned int resource, | 1276 | int do_prlimit(struct task_struct *tsk, unsigned int resource, |
1277 | struct rlimit *new_rlim) | 1277 | struct rlimit *new_rlim, struct rlimit *old_rlim) |
1278 | { | 1278 | { |
1279 | struct rlimit *old_rlim; | 1279 | struct rlimit *rlim; |
1280 | int retval = 0; | 1280 | int retval = 0; |
1281 | 1281 | ||
1282 | if (resource >= RLIM_NLIMITS) | 1282 | if (resource >= RLIM_NLIMITS) |
1283 | return -EINVAL; | 1283 | return -EINVAL; |
1284 | if (new_rlim->rlim_cur > new_rlim->rlim_max) | 1284 | if (new_rlim) { |
1285 | return -EINVAL; | 1285 | if (new_rlim->rlim_cur > new_rlim->rlim_max) |
1286 | if (resource == RLIMIT_NOFILE && new_rlim->rlim_max > sysctl_nr_open) | 1286 | return -EINVAL; |
1287 | return -EPERM; | 1287 | if (resource == RLIMIT_NOFILE && |
1288 | new_rlim->rlim_max > sysctl_nr_open) | ||
1289 | return -EPERM; | ||
1290 | } | ||
1288 | 1291 | ||
1289 | /* protect tsk->signal and tsk->sighand from disappearing */ | 1292 | /* protect tsk->signal and tsk->sighand from disappearing */ |
1290 | read_lock(&tasklist_lock); | 1293 | read_lock(&tasklist_lock); |
@@ -1293,42 +1296,42 @@ int do_setrlimit(struct task_struct *tsk, unsigned int resource, | |||
1293 | goto out; | 1296 | goto out; |
1294 | } | 1297 | } |
1295 | 1298 | ||
1296 | old_rlim = tsk->signal->rlim + resource; | 1299 | rlim = tsk->signal->rlim + resource; |
1297 | task_lock(tsk->group_leader); | 1300 | task_lock(tsk->group_leader); |
1298 | if (new_rlim->rlim_max > old_rlim->rlim_max && | 1301 | if (new_rlim) { |
1299 | !capable(CAP_SYS_RESOURCE)) | 1302 | if (new_rlim->rlim_max > rlim->rlim_max && |
1300 | retval = -EPERM; | 1303 | !capable(CAP_SYS_RESOURCE)) |
1301 | if (!retval) | 1304 | retval = -EPERM; |
1302 | retval = security_task_setrlimit(tsk->group_leader, resource, | 1305 | if (!retval) |
1303 | new_rlim); | 1306 | retval = security_task_setrlimit(tsk->group_leader, |
1304 | 1307 | resource, new_rlim); | |
1305 | if (resource == RLIMIT_CPU && new_rlim->rlim_cur == 0) { | 1308 | if (resource == RLIMIT_CPU && new_rlim->rlim_cur == 0) { |
1306 | /* | 1309 | /* |
1307 | * The caller is asking for an immediate RLIMIT_CPU | 1310 | * The caller is asking for an immediate RLIMIT_CPU |
1308 | * expiry. But we use the zero value to mean "it was | 1311 | * expiry. But we use the zero value to mean "it was |
1309 | * never set". So let's cheat and make it one second | 1312 | * never set". So let's cheat and make it one second |
1310 | * instead | 1313 | * instead |
1311 | */ | 1314 | */ |
1312 | new_rlim->rlim_cur = 1; | 1315 | new_rlim->rlim_cur = 1; |
1316 | } | ||
1317 | } | ||
1318 | if (!retval) { | ||
1319 | if (old_rlim) | ||
1320 | *old_rlim = *rlim; | ||
1321 | if (new_rlim) | ||
1322 | *rlim = *new_rlim; | ||
1313 | } | 1323 | } |
1314 | |||
1315 | if (!retval) | ||
1316 | *old_rlim = *new_rlim; | ||
1317 | task_unlock(tsk->group_leader); | 1324 | task_unlock(tsk->group_leader); |
1318 | 1325 | ||
1319 | if (retval || resource != RLIMIT_CPU) | ||
1320 | goto out; | ||
1321 | |||
1322 | /* | 1326 | /* |
1323 | * RLIMIT_CPU handling. Note that the kernel fails to return an error | 1327 | * RLIMIT_CPU handling. Note that the kernel fails to return an error |
1324 | * code if it rejected the user's attempt to set RLIMIT_CPU. This is a | 1328 | * code if it rejected the user's attempt to set RLIMIT_CPU. This is a |
1325 | * very long-standing error, and fixing it now risks breakage of | 1329 | * very long-standing error, and fixing it now risks breakage of |
1326 | * applications, so we live with it | 1330 | * applications, so we live with it |
1327 | */ | 1331 | */ |
1328 | if (new_rlim->rlim_cur == RLIM_INFINITY) | 1332 | if (!retval && new_rlim && resource == RLIMIT_CPU && |
1329 | goto out; | 1333 | new_rlim->rlim_cur != RLIM_INFINITY) |
1330 | 1334 | update_rlimit_cpu(tsk, new_rlim->rlim_cur); | |
1331 | update_rlimit_cpu(tsk, new_rlim->rlim_cur); | ||
1332 | out: | 1335 | out: |
1333 | read_unlock(&tasklist_lock); | 1336 | read_unlock(&tasklist_lock); |
1334 | return retval; | 1337 | return retval; |
@@ -1340,7 +1343,7 @@ SYSCALL_DEFINE2(setrlimit, unsigned int, resource, struct rlimit __user *, rlim) | |||
1340 | 1343 | ||
1341 | if (copy_from_user(&new_rlim, rlim, sizeof(*rlim))) | 1344 | if (copy_from_user(&new_rlim, rlim, sizeof(*rlim))) |
1342 | return -EFAULT; | 1345 | return -EFAULT; |
1343 | return do_setrlimit(current, resource, &new_rlim); | 1346 | return do_prlimit(current, resource, &new_rlim, NULL); |
1344 | } | 1347 | } |
1345 | 1348 | ||
1346 | /* | 1349 | /* |