Commit | Line | Data |
---|---|---|
da7c5cc6 | 1 | /* |
8429d022 MK |
2 | * Copyright (c) 1982, 1986, 1989, 1990, 1991 Regents of the University |
3 | * of California. All rights reserved. | |
da7c5cc6 | 4 | * |
dbf0c423 | 5 | * %sccs.include.redist.c% |
88a39492 | 6 | * |
ff63251b | 7 | * @(#)kern_prot.c 7.21 (Berkeley) %G% |
da7c5cc6 | 8 | */ |
a05af100 BJ |
9 | |
10 | /* | |
4147b3f6 | 11 | * System calls related to processes and protection |
a05af100 BJ |
12 | */ |
13 | ||
94368568 | 14 | #include "param.h" |
88a39492 | 15 | #include "acct.h" |
94368568 | 16 | #include "systm.h" |
8429d022 | 17 | #include "ucred.h" |
94368568 JB |
18 | #include "proc.h" |
19 | #include "timeb.h" | |
20 | #include "times.h" | |
88a39492 | 21 | #include "malloc.h" |
a05af100 | 22 | |
d5dc47bf MK |
23 | /* ARGSUSED */ |
24 | getpid(p, uap, retval) | |
25 | struct proc *p; | |
26 | void *uap; | |
27 | int *retval; | |
4147b3f6 BJ |
28 | { |
29 | ||
d5dc47bf MK |
30 | *retval = p->p_pid; |
31 | #ifdef COMPAT_43 | |
8429d022 | 32 | retval[1] = p->p_pptr->p_pid; |
d5dc47bf | 33 | #endif |
d9c2f47f | 34 | return (0); |
4147b3f6 BJ |
35 | } |
36 | ||
d5dc47bf MK |
37 | /* ARGSUSED */ |
38 | getppid(p, uap, retval) | |
39 | struct proc *p; | |
40 | void *uap; | |
41 | int *retval; | |
4147b3f6 | 42 | { |
d5dc47bf | 43 | |
8429d022 | 44 | *retval = p->p_pptr->p_pid; |
d9c2f47f | 45 | return (0); |
d5dc47bf MK |
46 | } |
47 | ||
8429d022 | 48 | /* Get process group ID; note that POSIX getpgrp takes no parameter */ |
d5dc47bf MK |
49 | getpgrp(p, uap, retval) |
50 | struct proc *p; | |
8429d022 | 51 | void *uap; |
d5dc47bf MK |
52 | int *retval; |
53 | { | |
4147b3f6 | 54 | |
d5dc47bf | 55 | *retval = p->p_pgrp->pg_id; |
d9c2f47f | 56 | return (0); |
4147b3f6 BJ |
57 | } |
58 | ||
d5dc47bf MK |
59 | /* ARGSUSED */ |
60 | getuid(p, uap, retval) | |
61 | struct proc *p; | |
62 | void *uap; | |
63 | int *retval; | |
a05af100 BJ |
64 | { |
65 | ||
8429d022 | 66 | *retval = p->p_cred->p_ruid; |
d5dc47bf | 67 | #ifdef COMPAT_43 |
8429d022 | 68 | retval[1] = p->p_ucred->cr_uid; |
d5dc47bf | 69 | #endif |
d9c2f47f | 70 | return (0); |
a05af100 BJ |
71 | } |
72 | ||
d5dc47bf MK |
73 | /* ARGSUSED */ |
74 | geteuid(p, uap, retval) | |
75 | struct proc *p; | |
76 | void *uap; | |
77 | int *retval; | |
4147b3f6 BJ |
78 | { |
79 | ||
8429d022 | 80 | *retval = p->p_ucred->cr_uid; |
d9c2f47f | 81 | return (0); |
4147b3f6 BJ |
82 | } |
83 | ||
d5dc47bf MK |
84 | /* ARGSUSED */ |
85 | getgid(p, uap, retval) | |
86 | struct proc *p; | |
87 | void *uap; | |
88 | int *retval; | |
89 | { | |
90 | ||
8429d022 | 91 | *retval = p->p_cred->p_rgid; |
d5dc47bf | 92 | #ifdef COMPAT_43 |
8429d022 | 93 | retval[1] = p->p_ucred->cr_groups[0]; |
d5dc47bf | 94 | #endif |
d9c2f47f | 95 | return (0); |
d5dc47bf MK |
96 | } |
97 | ||
98 | /* | |
0712db82 KB |
99 | * Get effective group ID. The "egid" is groups[0], and could be obtained |
100 | * via getgroups. This syscall exists because it is somewhat painful to do | |
101 | * correctly in a library function. | |
d5dc47bf MK |
102 | */ |
103 | /* ARGSUSED */ | |
104 | getegid(p, uap, retval) | |
105 | struct proc *p; | |
106 | void *uap; | |
107 | int *retval; | |
4147b3f6 | 108 | { |
8429d022 MK |
109 | |
110 | *retval = p->p_ucred->cr_groups[0]; | |
d9c2f47f | 111 | return (0); |
d5dc47bf MK |
112 | } |
113 | ||
114 | getgroups(p, uap, retval) | |
115 | struct proc *p; | |
116 | register struct arg { | |
b32450f4 | 117 | u_int gidsetsize; |
d5dc47bf MK |
118 | int *gidset; /* XXX not yet POSIX */ |
119 | } *uap; | |
120 | int *retval; | |
121 | { | |
8429d022 | 122 | register struct pcred *pc = p->p_cred; |
38b6104c MK |
123 | register gid_t *gp; |
124 | register int *lp; | |
fca91c15 | 125 | register u_int ngrp; |
38b6104c | 126 | int groups[NGROUPS]; |
d5dc47bf | 127 | int error; |
4147b3f6 | 128 | |
fca91c15 | 129 | if ((ngrp = uap->gidsetsize) == 0) { |
8429d022 | 130 | *retval = pc->pc_ucred->cr_ngroups; |
d9c2f47f | 131 | return (0); |
197da11b | 132 | } |
8429d022 | 133 | if (ngrp < pc->pc_ucred->cr_ngroups) |
d9c2f47f | 134 | return (EINVAL); |
8429d022 MK |
135 | ngrp = pc->pc_ucred->cr_ngroups; |
136 | for (gp = pc->pc_ucred->cr_groups, lp = groups; lp < &groups[ngrp]; ) | |
38b6104c | 137 | *lp++ = *gp++; |
d5dc47bf | 138 | if (error = copyout((caddr_t)groups, (caddr_t)uap->gidset, |
fca91c15 | 139 | ngrp * sizeof (groups[0]))) |
d9c2f47f | 140 | return (error); |
fca91c15 | 141 | *retval = ngrp; |
d9c2f47f | 142 | return (0); |
4147b3f6 BJ |
143 | } |
144 | ||
d5dc47bf MK |
145 | /* ARGSUSED */ |
146 | setsid(p, uap, retval) | |
8429d022 | 147 | register struct proc *p; |
d5dc47bf MK |
148 | void *uap; |
149 | int *retval; | |
8fe87cbb | 150 | { |
8fe87cbb | 151 | |
d5dc47bf | 152 | if (p->p_pgid == p->p_pid || pgfind(p->p_pid)) { |
d9c2f47f | 153 | return (EPERM); |
d5dc47bf | 154 | } else { |
8429d022 | 155 | enterpgrp(p, p->p_pid, 1); |
d5dc47bf | 156 | *retval = p->p_pid; |
d9c2f47f | 157 | return (0); |
8fe87cbb | 158 | } |
8fe87cbb MT |
159 | } |
160 | ||
161 | /* | |
8429d022 | 162 | * set process group (setpgid/old setpgrp) |
8fe87cbb | 163 | * |
049b1521 | 164 | * caller does setpgid(targpid, targpgid) |
164c0f54 MK |
165 | * |
166 | * pid must be caller or child of caller (ESRCH) | |
167 | * if a child | |
168 | * pid must be in same session (EPERM) | |
169 | * pid can't have done an exec (EACCES) | |
170 | * if pgid != pid | |
171 | * there must exist some pid in same session having pgid (EPERM) | |
172 | * pid must not be session leader (EPERM) | |
8fe87cbb | 173 | */ |
d5dc47bf | 174 | /* ARGSUSED */ |
049b1521 MT |
175 | setpgid(curp, uap, retval) |
176 | struct proc *curp; | |
d5dc47bf | 177 | register struct args { |
ff63251b MK |
178 | int pid; /* target process id */ |
179 | int pgid; /* target pgrp id */ | |
d5dc47bf MK |
180 | } *uap; |
181 | int *retval; | |
182 | { | |
049b1521 | 183 | register struct proc *targp; /* target process */ |
ff63251b | 184 | register struct pgrp *pgrp; /* target pgrp */ |
4147b3f6 | 185 | |
ff63251b MK |
186 | if (uap->pid != 0 && uap->pid != curp->p_pid) { |
187 | if ((targp = pfind(uap->pid)) == 0 || !inferior(targp)) | |
d9c2f47f | 188 | return (ESRCH); |
049b1521 | 189 | if (targp->p_session != curp->p_session) |
d9c2f47f | 190 | return (EPERM); |
049b1521 | 191 | if (targp->p_flag&SEXEC) |
d9c2f47f | 192 | return (EACCES); |
d5dc47bf | 193 | } else |
049b1521 MT |
194 | targp = curp; |
195 | if (SESS_LEADER(targp)) | |
d9c2f47f | 196 | return (EPERM); |
ff63251b MK |
197 | if (uap->pgid == 0) |
198 | uap->pgid = targp->p_pid; | |
199 | else if (uap->pgid != targp->p_pid) | |
200 | if ((pgrp = pgfind(uap->pgid)) == 0 || | |
201 | pgrp->pg_session != curp->p_session) | |
0712db82 | 202 | return (EPERM); |
ff63251b | 203 | enterpgrp(targp, uap->pgid, 0); |
d9c2f47f | 204 | return (0); |
4147b3f6 BJ |
205 | } |
206 | ||
d5dc47bf MK |
207 | /* ARGSUSED */ |
208 | setuid(p, uap, retval) | |
8429d022 | 209 | struct proc *p; |
d5dc47bf MK |
210 | struct args { |
211 | int uid; | |
212 | } *uap; | |
213 | int *retval; | |
4f083fd7 | 214 | { |
8429d022 | 215 | register struct pcred *pc = p->p_cred; |
d5dc47bf MK |
216 | register uid_t uid; |
217 | int error; | |
218 | ||
219 | uid = uap->uid; | |
8429d022 MK |
220 | if (uid != pc->p_ruid && |
221 | (error = suser(pc->pc_ucred, &p->p_acflag))) | |
d9c2f47f | 222 | return (error); |
d5dc47bf | 223 | /* |
0712db82 KB |
224 | * Everything's okay, do it. Copy credentials so other references do |
225 | * not see our changes. | |
d5dc47bf | 226 | */ |
8429d022 MK |
227 | pc->pc_ucred = crcopy(pc->pc_ucred); |
228 | pc->pc_ucred->cr_uid = uid; | |
229 | pc->p_ruid = uid; | |
230 | pc->p_svuid = uid; | |
d9c2f47f | 231 | return (0); |
d5dc47bf MK |
232 | } |
233 | ||
234 | /* ARGSUSED */ | |
235 | seteuid(p, uap, retval) | |
8429d022 | 236 | struct proc *p; |
d5dc47bf | 237 | struct args { |
4f083fd7 SL |
238 | int euid; |
239 | } *uap; | |
d5dc47bf MK |
240 | int *retval; |
241 | { | |
8429d022 | 242 | register struct pcred *pc = p->p_cred; |
d5dc47bf MK |
243 | register uid_t euid; |
244 | int error; | |
4f083fd7 | 245 | |
4f083fd7 | 246 | euid = uap->euid; |
8429d022 MK |
247 | if (euid != pc->p_ruid && euid != pc->p_svuid && |
248 | (error = suser(pc->pc_ucred, &p->p_acflag))) | |
d9c2f47f | 249 | return (error); |
4f083fd7 | 250 | /* |
0712db82 KB |
251 | * Everything's okay, do it. Copy credentials so other references do |
252 | * not see our changes. | |
4f083fd7 | 253 | */ |
8429d022 MK |
254 | pc->pc_ucred = crcopy(pc->pc_ucred); |
255 | pc->pc_ucred->cr_uid = euid; | |
d9c2f47f | 256 | return (0); |
4f083fd7 SL |
257 | } |
258 | ||
d5dc47bf MK |
259 | /* ARGSUSED */ |
260 | setgid(p, uap, retval) | |
261 | struct proc *p; | |
262 | struct args { | |
263 | int gid; | |
264 | } *uap; | |
265 | int *retval; | |
4f083fd7 | 266 | { |
8429d022 | 267 | register struct pcred *pc = p->p_cred; |
d5dc47bf MK |
268 | register gid_t gid; |
269 | int error; | |
270 | ||
271 | gid = uap->gid; | |
8429d022 | 272 | if (gid != pc->p_rgid && (error = suser(pc->pc_ucred, &p->p_acflag))) |
d9c2f47f | 273 | return (error); |
8429d022 MK |
274 | pc->pc_ucred = crcopy(pc->pc_ucred); |
275 | pc->pc_ucred->cr_groups[0] = gid; | |
276 | pc->p_rgid = gid; | |
277 | pc->p_svgid = gid; /* ??? */ | |
d9c2f47f | 278 | return (0); |
d5dc47bf MK |
279 | } |
280 | ||
281 | /* ARGSUSED */ | |
282 | setegid(p, uap, retval) | |
283 | struct proc *p; | |
284 | struct args { | |
4f083fd7 SL |
285 | int egid; |
286 | } *uap; | |
d5dc47bf MK |
287 | int *retval; |
288 | { | |
8429d022 | 289 | register struct pcred *pc = p->p_cred; |
d5dc47bf MK |
290 | register gid_t egid; |
291 | int error; | |
4f083fd7 | 292 | |
4f083fd7 | 293 | egid = uap->egid; |
8429d022 MK |
294 | if (egid != pc->p_rgid && egid != pc->p_svgid && |
295 | (error = suser(pc->pc_ucred, &p->p_acflag))) | |
d9c2f47f | 296 | return (error); |
8429d022 MK |
297 | pc->pc_ucred = crcopy(pc->pc_ucred); |
298 | pc->pc_ucred->cr_groups[0] = egid; | |
d9c2f47f | 299 | return (0); |
d5dc47bf MK |
300 | } |
301 | ||
302 | #ifdef COMPAT_43 | |
303 | /* ARGSUSED */ | |
304 | osetreuid(p, uap, retval) | |
305 | register struct proc *p; | |
306 | struct args { | |
307 | int ruid; | |
308 | int euid; | |
309 | } *uap; | |
310 | int *retval; | |
311 | { | |
8429d022 | 312 | register struct pcred *pc = p->p_cred; |
d5dc47bf MK |
313 | register uid_t ruid, euid; |
314 | int error; | |
315 | ||
316 | if (uap->ruid == -1) | |
8429d022 MK |
317 | if (ruid != pc->p_ruid && ruid != pc->pc_ucred->cr_uid /* XXX */ && |
318 | (error = suser(pc->pc_ucred, &p->p_acflag))) | |
d9c2f47f | 319 | return (error); |
d5dc47bf | 320 | if (uap->euid == -1) |
8429d022 MK |
321 | if (euid != pc->pc_ucred->cr_uid && euid != pc->p_ruid && |
322 | euid != pc->p_svuid && (error = suser(pc->pc_ucred, &p->p_acflag))) | |
d9c2f47f | 323 | return (error); |
d5dc47bf | 324 | /* |
0712db82 KB |
325 | * Everything's okay, do it. Copy credentials so other references do |
326 | * not see our changes. | |
d5dc47bf | 327 | */ |
8429d022 MK |
328 | pc->pc_ucred = crcopy(pc->pc_ucred); |
329 | pc->pc_ucred->cr_uid = euid; | |
330 | pc->p_ruid = ruid; | |
d9c2f47f | 331 | return (0); |
4f083fd7 | 332 | } |
a05af100 | 333 | |
d5dc47bf MK |
334 | /* ARGSUSED */ |
335 | osetregid(p, uap, retval) | |
8429d022 | 336 | register struct proc *p; |
d5dc47bf MK |
337 | struct args { |
338 | int rgid; | |
339 | int egid; | |
340 | } *uap; | |
341 | int *retval; | |
4147b3f6 | 342 | { |
8429d022 | 343 | register struct pcred *pc = p->p_cred; |
d5dc47bf MK |
344 | register gid_t rgid, egid; |
345 | int error; | |
346 | ||
347 | if (uap->rgid == -1) | |
8429d022 MK |
348 | if (rgid != pc->p_rgid && rgid != pc->pc_ucred->cr_groups[0] /* XXX */ && |
349 | (error = suser(pc->pc_ucred, &p->p_acflag))) | |
d9c2f47f | 350 | return (error); |
d5dc47bf | 351 | if (uap->egid == -1) |
8429d022 MK |
352 | if (egid != pc->pc_ucred->cr_groups[0] && egid != pc->p_rgid && |
353 | egid != pc->p_svgid && (error = suser(pc->pc_ucred, &p->p_acflag))) | |
d9c2f47f | 354 | return (error); |
8429d022 MK |
355 | pc->pc_ucred = crcopy(pc->pc_ucred); |
356 | pc->pc_ucred->cr_groups[0] = egid; | |
357 | pc->p_rgid = rgid; | |
d9c2f47f | 358 | return (0); |
d5dc47bf MK |
359 | } |
360 | #endif | |
361 | ||
362 | /* ARGSUSED */ | |
363 | setgroups(p, uap, retval) | |
364 | struct proc *p; | |
365 | struct args { | |
b32450f4 | 366 | u_int gidsetsize; |
4147b3f6 | 367 | int *gidset; |
d5dc47bf MK |
368 | } *uap; |
369 | int *retval; | |
370 | { | |
8429d022 | 371 | register struct pcred *pc = p->p_cred; |
38b6104c | 372 | register gid_t *gp; |
fca91c15 | 373 | register u_int ngrp; |
38b6104c | 374 | register int *lp; |
446a921a | 375 | int error, groups[NGROUPS]; |
4147b3f6 | 376 | |
8429d022 | 377 | if (error = suser(pc->pc_ucred, &p->p_acflag)) |
d9c2f47f | 378 | return (error); |
fca91c15 | 379 | if ((ngrp = uap->gidsetsize) > NGROUPS) |
d9c2f47f | 380 | return (EINVAL); |
446a921a | 381 | if (error = copyin((caddr_t)uap->gidset, (caddr_t)groups, |
fca91c15 | 382 | ngrp * sizeof (groups[0]))) |
d9c2f47f | 383 | return (error); |
8429d022 MK |
384 | pc->pc_ucred = crcopy(pc->pc_ucred); |
385 | pc->pc_ucred->cr_ngroups = ngrp; | |
446a921a | 386 | /* convert from int's to gid_t's */ |
8429d022 | 387 | for (gp = pc->pc_ucred->cr_groups, lp = groups; ngrp--; ) |
fca91c15 | 388 | *gp++ = *lp++; |
d9c2f47f | 389 | return (0); |
4147b3f6 BJ |
390 | } |
391 | ||
f926daf9 | 392 | /* |
88a39492 | 393 | * Check if gid is a member of the group set. |
f926daf9 | 394 | */ |
88a39492 | 395 | groupmember(gid, cred) |
90731a34 | 396 | gid_t gid; |
88a39492 | 397 | register struct ucred *cred; |
197da11b | 398 | { |
38b6104c | 399 | register gid_t *gp; |
88a39492 | 400 | gid_t *egp; |
197da11b | 401 | |
88a39492 KM |
402 | egp = &(cred->cr_groups[cred->cr_ngroups]); |
403 | for (gp = cred->cr_groups; gp < egp; gp++) | |
197da11b | 404 | if (*gp == gid) |
88a39492 KM |
405 | return (1); |
406 | return (0); | |
197da11b BJ |
407 | } |
408 | ||
f926daf9 | 409 | /* |
f242743a MK |
410 | * Test whether the specified credentials imply "super-user" |
411 | * privilege; if so, and we have accounting info, set the flag | |
412 | * indicating use of super-powers. | |
413 | * Returns 0 or error. | |
f926daf9 | 414 | */ |
88a39492 KM |
415 | suser(cred, acflag) |
416 | struct ucred *cred; | |
417 | short *acflag; | |
197da11b | 418 | { |
88a39492 KM |
419 | if (cred->cr_uid == 0) { |
420 | if (acflag) | |
421 | *acflag |= ASU; | |
422 | return (0); | |
38b6104c | 423 | } |
88a39492 | 424 | return (EPERM); |
197da11b | 425 | } |
f926daf9 SL |
426 | |
427 | /* | |
88a39492 | 428 | * Allocate a zeroed cred structure. |
f926daf9 | 429 | */ |
88a39492 KM |
430 | struct ucred * |
431 | crget() | |
f926daf9 | 432 | { |
88a39492 | 433 | register struct ucred *cr; |
f926daf9 | 434 | |
88a39492 KM |
435 | MALLOC(cr, struct ucred *, sizeof(*cr), M_CRED, M_WAITOK); |
436 | bzero((caddr_t)cr, sizeof(*cr)); | |
437 | cr->cr_ref = 1; | |
d5dc47bf | 438 | return (cr); |
88a39492 KM |
439 | } |
440 | ||
441 | /* | |
442 | * Free a cred structure. | |
443 | * Throws away space when ref count gets to 0. | |
444 | */ | |
445 | crfree(cr) | |
446 | struct ucred *cr; | |
447 | { | |
8429d022 | 448 | int s = splimp(); /* ??? */ |
88a39492 KM |
449 | |
450 | if (--cr->cr_ref != 0) { | |
451 | (void) splx(s); | |
452 | return; | |
453 | } | |
454 | FREE((caddr_t)cr, M_CRED); | |
455 | (void) splx(s); | |
456 | } | |
457 | ||
458 | /* | |
459 | * Copy cred structure to a new one and free the old one. | |
460 | */ | |
461 | struct ucred * | |
462 | crcopy(cr) | |
463 | struct ucred *cr; | |
464 | { | |
465 | struct ucred *newcr; | |
466 | ||
0712db82 KB |
467 | if (cr->cr_ref == 1) |
468 | return (cr); | |
88a39492 KM |
469 | newcr = crget(); |
470 | *newcr = *cr; | |
471 | crfree(cr); | |
472 | newcr->cr_ref = 1; | |
d5dc47bf | 473 | return (newcr); |
88a39492 KM |
474 | } |
475 | ||
476 | /* | |
477 | * Dup cred struct to a new held one. | |
478 | */ | |
479 | struct ucred * | |
480 | crdup(cr) | |
481 | struct ucred *cr; | |
482 | { | |
483 | struct ucred *newcr; | |
484 | ||
485 | newcr = crget(); | |
486 | *newcr = *cr; | |
487 | newcr->cr_ref = 1; | |
d5dc47bf | 488 | return (newcr); |
f926daf9 | 489 | } |
5b6ee178 KF |
490 | |
491 | /* | |
164c0f54 | 492 | * Get login name, if available. |
5b6ee178 | 493 | */ |
d5dc47bf MK |
494 | /* ARGSUSED */ |
495 | getlogin(p, uap, retval) | |
496 | struct proc *p; | |
497 | struct args { | |
5b6ee178 KF |
498 | char *namebuf; |
499 | u_int namelen; | |
d5dc47bf MK |
500 | } *uap; |
501 | int *retval; | |
502 | { | |
5b6ee178 | 503 | |
8429d022 MK |
504 | if (uap->namelen > sizeof (p->p_pgrp->pg_session->s_login)) |
505 | uap->namelen = sizeof (p->p_pgrp->pg_session->s_login); | |
506 | return (copyout((caddr_t) p->p_pgrp->pg_session->s_login, | |
507 | (caddr_t) uap->namebuf, uap->namelen)); | |
5b6ee178 KF |
508 | } |
509 | ||
510 | /* | |
164c0f54 | 511 | * Set login name. |
5b6ee178 | 512 | */ |
d5dc47bf MK |
513 | /* ARGSUSED */ |
514 | setlogin(p, uap, retval) | |
515 | struct proc *p; | |
516 | struct args { | |
5b6ee178 | 517 | char *namebuf; |
d5dc47bf MK |
518 | } *uap; |
519 | int *retval; | |
520 | { | |
164c0f54 | 521 | int error; |
5b6ee178 | 522 | |
8429d022 | 523 | if (error = suser(p->p_ucred, &p->p_acflag)) |
d9c2f47f | 524 | return (error); |
8429d022 MK |
525 | error = copyinstr((caddr_t) uap->namebuf, |
526 | (caddr_t) p->p_pgrp->pg_session->s_login, | |
527 | sizeof (p->p_pgrp->pg_session->s_login) - 1, (u_int *)0); | |
528 | if (error == ENAMETOOLONG) | |
164c0f54 | 529 | error = EINVAL; |
d9c2f47f | 530 | return (error); |
5b6ee178 | 531 | } |