xref: /titanic_52/usr/src/uts/common/fs/ufs/quota_ufs.c (revision fa9e4066f08beec538e775443c5be79dd423fcab)
1 /*
2  * CDDL HEADER START
3  *
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.
8  *
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.
13  *
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]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*	Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T	*/
28 /*	  All Rights Reserved  	*/
29 
30 /*
31  * University Copyright- Copyright (c) 1982, 1986, 1988
32  * The Regents of the University of California
33  * All Rights Reserved
34  *
35  * University Acknowledgment- Portions of this document are derived from
36  * software developed by the University of California, Berkeley, and its
37  * contributors.
38  */
39 
40 
41 #pragma ident	"%Z%%M%	%I%	%E% SMI"
42 
43 /*
44  * Routines used in checking limits on file system usage.
45  */
46 
47 #include <sys/types.h>
48 #include <sys/t_lock.h>
49 #include <sys/param.h>
50 #include <sys/time.h>
51 #include <sys/systm.h>
52 #include <sys/kmem.h>
53 #include <sys/signal.h>
54 #include <sys/cred.h>
55 #include <sys/proc.h>
56 #include <sys/user.h>
57 #include <sys/proc.h>
58 #include <sys/vfs.h>
59 #include <sys/vnode.h>
60 #include <sys/buf.h>
61 #include <sys/uio.h>
62 #include <sys/fs/ufs_inode.h>
63 #include <sys/fs/ufs_fs.h>
64 #include <sys/fs/ufs_quota.h>
65 #include <sys/errno.h>
66 #include <sys/cmn_err.h>
67 #include <sys/session.h>
68 #include <sys/debug.h>
69 
70 /*
71  * Find the dquot structure that should
72  * be used in checking i/o on inode ip.
73  */
74 struct dquot *
75 getinoquota(struct inode *ip)
76 {
77 	struct dquot *dqp, *xdqp;
78 	struct ufsvfs *ufsvfsp = ip->i_ufsvfs;
79 
80 	ASSERT(RW_LOCK_HELD(&ufsvfsp->vfs_dqrwlock));
81 	ASSERT(RW_WRITE_HELD(&ip->i_contents));
82 	/*
83 	 * Check for quotas enabled.
84 	 */
85 	if ((ufsvfsp->vfs_qflags & MQ_ENABLED) == 0) {
86 		return (NULL);
87 	}
88 
89 	/*
90 	 * Check for someone doing I/O to quota file.
91 	 */
92 	if (ip == ufsvfsp->vfs_qinod) {
93 		return (NULL);
94 	}
95 
96 	/*
97 	 * Check for a legal inode, e.g. not a shadow inode,
98 	 * not a extended attribute directory inode and a valid mode.
99 	 */
100 	ASSERT((ip->i_mode & IFMT) != IFSHAD);
101 	ASSERT((ip->i_mode & IFMT) != IFATTRDIR);
102 	ASSERT(ip->i_mode);
103 
104 	if (getdiskquota((uid_t)ip->i_uid, ufsvfsp, 0, &xdqp)) {
105 		return (NULL);
106 	}
107 	dqp = xdqp;
108 	mutex_enter(&dqp->dq_lock);
109 	ASSERT(ip->i_uid == dqp->dq_uid);
110 
111 	if (dqp->dq_fhardlimit == 0 && dqp->dq_fsoftlimit == 0 &&
112 	    dqp->dq_bhardlimit == 0 && dqp->dq_bsoftlimit == 0) {
113 		dqput(dqp);
114 		mutex_exit(&dqp->dq_lock);
115 		dqp = NULL;
116 	} else {
117 		mutex_exit(&dqp->dq_lock);
118 	}
119 	return (dqp);
120 }
121 
122 /*
123  * Update disk usage, and take corrective action.
124  */
125 int
126 chkdq(struct inode *ip, long change, int force, struct cred *cr,
127 	char **uerrp, size_t *lenp)
128 {
129 	struct dquot *dqp;
130 	uint64_t ncurblocks;
131 	struct ufsvfs *ufsvfsp = ip->i_ufsvfs;
132 	int error = 0;
133 	long abs_change;
134 	char *msg1 =
135 "!quota_ufs: over hard disk limit (pid %d, uid %d, inum %d, fs %s)\n";
136 	char *msg2 =
137 "!quota_ufs: Warning: over disk limit (pid %d, uid %d, inum %d, fs %s)\n";
138 	char *msg3 =
139 "!quota_ufs: over disk and time limit (pid %d, uid %d, inum %d, fs %s)\n";
140 	char *msg4 =
141 "!quota_ufs: Warning: quota overflow (pid %d, uid %d, inum %d, fs %s)\n";
142 	char *errmsg = NULL;
143 	time_t now;
144 
145 	/*
146 	 * Shadow inodes do not need to hold the vfs_dqrwlock lock.
147 	 */
148 	ASSERT((ip->i_mode & IFMT) == IFSHAD ||
149 		RW_LOCK_HELD(&ufsvfsp->vfs_dqrwlock));
150 	ASSERT(RW_WRITE_HELD(&ip->i_contents));
151 
152 	if (change == 0)
153 		return (0);
154 	dqp = ip->i_dquot;
155 
156 	/*
157 	 * Make sure the quota info record matches the owner.
158 	 */
159 	ASSERT(dqp == NULL || ip->i_uid == dqp->dq_uid);
160 
161 #ifdef DEBUG
162 	/*
163 	 * Shadow inodes and extended attribute directories
164 	 * should not have quota info records.
165 	 */
166 	if ((ip->i_mode & IFMT) == IFSHAD || (ip->i_mode & IFMT) == IFATTRDIR) {
167 		ASSERT(dqp == NULL);
168 	}
169 	/*
170 	 * Paranoia for verifying that quotas are okay.
171 	 */
172 	else {
173 		struct dquot *expect_dq;
174 		int mismatch_ok = 0;
175 
176 		/* Get current quota information */
177 		expect_dq = getinoquota(ip);
178 		/*
179 		 * We got NULL back from getinoquota(), but there is
180 		 * no error code return from that interface and some
181 		 * errors are "ok" because we may be testing via error
182 		 * injection.  If this is not the quota inode then we
183 		 * use getdiskquota() to see if there is an error and
184 		 * if the error is ok.
185 		 */
186 		if (expect_dq == NULL && ip != ufsvfsp->vfs_qinod) {
187 			int error;
188 			struct dquot *xdqp;
189 
190 			error = getdiskquota((uid_t)ip->i_uid, ufsvfsp, 0,
191 						&xdqp);
192 			switch (error) {
193 			/*
194 			 * Either the error was transient or the quota
195 			 * info record has no limits which gets optimized
196 			 * out by getinoquota().
197 			 */
198 			case 0:
199 				if (xdqp->dq_fhardlimit == 0 &&
200 				    xdqp->dq_fsoftlimit == 0 &&
201 				    xdqp->dq_bhardlimit == 0 &&
202 				    xdqp->dq_bsoftlimit == 0) {
203 					mutex_enter(&xdqp->dq_lock);
204 					dqput(xdqp);
205 					mutex_exit(&xdqp->dq_lock);
206 				} else {
207 					expect_dq = xdqp;
208 				}
209 				break;
210 
211 			case ESRCH:	/* quotas are not enabled */
212 			case EINVAL:	/* error flag set on cached record */
213 			case EUSERS:	/* quota table is full */
214 			case EIO:	/* I/O error */
215 				mismatch_ok = 1;
216 				break;
217 			}
218 		}
219 
220 		/*
221 		 * Make sure dqp and the current quota info agree.
222 		 * The first part of the #ifndef is the quick way to
223 		 * do the check and should be part of the standard
224 		 * DEBUG code. The #else part is useful if you are
225 		 * actually chasing an inconsistency and don't want
226 		 * to have to look at stack frames to figure which
227 		 * variable has what value.
228 		 */
229 #ifndef CHASE_QUOTA
230 		ASSERT(mismatch_ok || dqp == expect_dq);
231 #else /* CHASE_QUOTA */
232 		if (expect_dq == NULL) {
233 			/*
234 			 * If you hit this ASSERT() you know that quota
235 			 * subsystem does not expect quota info for this
236 			 * inode, but the inode has it.
237 			 */
238 			ASSERT(mismatch_ok || dqp == NULL);
239 		} else {
240 			/*
241 			 * If you hit this ASSERT() you know that quota
242 			 * subsystem expects quota info for this inode,
243 			 * but the inode does not have it.
244 			 */
245 			ASSERT(dqp);
246 			/*
247 			 * If you hit this ASSERT() you know that quota
248 			 * subsystem expects quota info for this inode
249 			 * and the inode has quota info, but the two
250 			 * quota info pointers are not the same.
251 			 */
252 			ASSERT(dqp == expect_dq);
253 		}
254 #endif /* !CHASE_QUOTA */
255 		/*
256 		 * Release for getinoquota() above or getdiskquota()
257 		 * call when error is transient.
258 		 */
259 		if (expect_dq) {
260 			mutex_enter(&expect_dq->dq_lock);
261 			dqput(expect_dq);
262 			mutex_exit(&expect_dq->dq_lock);
263 		}
264 	}
265 #endif /* DEBUG */
266 
267 	/*
268 	 * Shadow inodes and extended attribute directories
269 	 * do not have quota info records.
270 	 */
271 	if (dqp == NULL)
272 		return (0);
273 	/*
274 	 * Quotas are not enabled on this file system so there is nothing
275 	 * more to do.
276 	 */
277 	if ((ufsvfsp->vfs_qflags & MQ_ENABLED) == 0) {
278 		return (0);
279 	}
280 	mutex_enter(&dqp->dq_lock);
281 	if (change < 0) {
282 		dqp->dq_flags |= DQ_MOD;
283 		abs_change = -change;	/* abs_change must be positive */
284 		if (dqp->dq_curblocks < abs_change)
285 			dqp->dq_curblocks = 0;
286 		else
287 			dqp->dq_curblocks += change;
288 		if (dqp->dq_curblocks < dqp->dq_bsoftlimit)
289 			dqp->dq_btimelimit = 0;
290 		dqp->dq_flags &= ~DQ_BLKS;
291 		TRANS_QUOTA(dqp);
292 		mutex_exit(&dqp->dq_lock);
293 		return (0);
294 	}
295 
296 	/*
297 	 * Adding 'change' to dq_curblocks could cause an overflow.
298 	 * So store the result in a 64-bit variable and check for
299 	 * overflow below.
300 	 */
301 	ncurblocks = (uint64_t)dqp->dq_curblocks + change;
302 
303 	/*
304 	 * Allocation. Check hard and soft limits.
305 	 * Skip checks for uid 0 owned files.
306 	 * This check used to require both euid and ip->i_uid
307 	 * to be 0; but there are no quotas for uid 0 so
308 	 * it really doesn't matter who is writing to the
309 	 * root owned file.  And even root cannot write
310 	 * past a user's quota limit.
311 	 */
312 	if (ip->i_uid == 0)
313 		goto out;
314 
315 	/*
316 	 * Disallow allocation if it would bring the current usage over
317 	 * the hard limit or if the user is over his soft limit and his time
318 	 * has run out.
319 	 */
320 	if (dqp->dq_bhardlimit && ncurblocks >= (uint64_t)dqp->dq_bhardlimit &&
321 	    !force) {
322 		/* If the user was not informed yet and the caller	*/
323 		/* is the owner of the file				*/
324 		if ((dqp->dq_flags & DQ_BLKS) == 0 &&
325 			ip->i_uid == crgetruid(cr)) {
326 			errmsg = msg1;
327 			dqp->dq_flags |= DQ_BLKS;
328 		}
329 		error = EDQUOT;
330 		goto out;
331 	}
332 	if (dqp->dq_bsoftlimit && ncurblocks >= (uint64_t)dqp->dq_bsoftlimit) {
333 		now = gethrestime_sec();
334 		if (dqp->dq_curblocks < dqp->dq_bsoftlimit ||
335 		    dqp->dq_btimelimit == 0) {
336 			dqp->dq_flags |= DQ_MOD;
337 			dqp->dq_btimelimit = now +
338 			    ((struct ufsvfs *)ITOV(ip)->v_vfsp->vfs_data)
339 				->vfs_btimelimit;
340 			if (ip->i_uid == crgetruid(cr)) {
341 				errmsg = msg2;
342 			}
343 		} else if (now > dqp->dq_btimelimit && !force) {
344 			/* If the user was not informed yet and the	*/
345 			/* caller is the owner of the file		*/
346 			if ((dqp->dq_flags & DQ_BLKS) == 0 &&
347 				ip->i_uid == crgetruid(cr)) {
348 				errmsg = msg3;
349 				dqp->dq_flags |= DQ_BLKS;
350 			}
351 			error = EDQUOT;
352 		}
353 	}
354 out:
355 	if (error == 0) {
356 		dqp->dq_flags |= DQ_MOD;
357 		/*
358 		 * ncurblocks can be bigger than the maximum
359 		 * number that can be represented in 32-bits.
360 		 * When copying ncurblocks to dq_curblocks
361 		 * (an unsigned 32-bit quantity), make sure there
362 		 * is no overflow.  The only way this can happen
363 		 * is if "force" is set.  Otherwise, this allocation
364 		 * would have exceeded the hard limit check above
365 		 * (since the hard limit is a 32-bit quantity).
366 		 */
367 		if (ncurblocks > 0xffffffffLL) {
368 			dqp->dq_curblocks = 0xffffffff;
369 			errmsg = msg4;
370 		} else {
371 			dqp->dq_curblocks = ncurblocks;
372 		}
373 	}
374 
375 	if (dqp->dq_flags & DQ_MOD)
376 		TRANS_QUOTA(dqp);
377 
378 	mutex_exit(&dqp->dq_lock);
379 	/*
380 	 * Check for any error messages to be sent
381 	 */
382 	if (errmsg != NULL) {
383 		/*
384 		 * Send message to the error log.
385 		 */
386 		if (uerrp != NULL) {
387 			/*
388 			 * Set up message caller should send to user;
389 			 * gets copied to the message buffer as a side-
390 			 * effect of the caller's uprintf().
391 			 */
392 			*lenp = strlen(errmsg) + 20 + 20 +
393 				strlen(ip->i_fs->fs_fsmnt) + 1;
394 			*uerrp = (char *)kmem_alloc(*lenp, KM_NOSLEEP);
395 			if (*uerrp != NULL) {
396 				/* errmsg+1 => skip leading ! */
397 				(void) sprintf(*uerrp, errmsg+1,
398 					(int)ttoproc(curthread)->p_pid,
399 					(int)ip->i_uid, (int)ip->i_number,
400 					ip->i_fs->fs_fsmnt);
401 			}
402 		} else {
403 			/*
404 			 * Caller doesn't care, so just copy to the
405 			 * message buffer.
406 			 */
407 			cmn_err(CE_NOTE, errmsg,
408 				(int)ttoproc(curthread)->p_pid,
409 				(int)ip->i_uid, (int)ip->i_number,
410 				ip->i_fs->fs_fsmnt);
411 		}
412 	}
413 	return (error);
414 }
415 
416 /*
417  * Check the inode limit, applying corrective action.
418  */
419 int
420 chkiq(struct ufsvfs *ufsvfsp, int change, struct inode *ip, uid_t uid,
421 	int force, struct cred *cr, char **uerrp, size_t *lenp)
422 {
423 	struct dquot *dqp, *xdqp;
424 	unsigned int ncurfiles;
425 	char *errmsg = NULL;
426 	char *err1 =
427 "!quota_ufs: over file hard limit (pid %d, uid %d, fs %s)\n";
428 	char *err2 =
429 "!quota_ufs: Warning: too many files (pid %d, uid %d, fs %s)\n";
430 	char *err3 =
431 "!quota_ufs: over file and time limit (pid %d, uid %d, fs %s)\n";
432 	int error = 0;
433 	time_t now;
434 
435 	ASSERT(RW_READ_HELD(&ufsvfsp->vfs_dqrwlock));
436 	/*
437 	 * Change must be either a single increment or decrement.
438 	 * If change is an increment, then ip must be NULL.
439 	 */
440 	ASSERT(change == 1 || change == -1);
441 	ASSERT(change != 1 || ip == NULL);
442 
443 	/*
444 	 * Quotas are not enabled so bail out now.
445 	 */
446 	if ((ufsvfsp->vfs_qflags & MQ_ENABLED) == 0) {
447 		return (0);
448 	}
449 
450 	/*
451 	 * Free a specific inode.
452 	 */
453 	if (change == -1 && ip) {
454 		dqp = ip->i_dquot;
455 		/*
456 		 * Shadow inodes and extended attribute directories
457 		 * do not have quota info records.
458 		 */
459 		if (dqp == NULL)
460 			return (0);
461 		mutex_enter(&dqp->dq_lock);
462 		if (dqp->dq_curfiles) {
463 			dqp->dq_curfiles--;
464 			dqp->dq_flags |= DQ_MOD;
465 		}
466 		if (dqp->dq_curfiles < dqp->dq_fsoftlimit) {
467 			dqp->dq_ftimelimit = 0;
468 			dqp->dq_flags |= DQ_MOD;
469 		}
470 		dqp->dq_flags &= ~DQ_FILES;
471 		if (dqp->dq_flags & DQ_MOD)
472 			TRANS_QUOTA(dqp);
473 		mutex_exit(&dqp->dq_lock);
474 		return (0);
475 	}
476 
477 	/*
478 	 * Allocation or deallocation without a specific inode.
479 	 * Get dquot for for uid, fs.
480 	 */
481 	if (getdiskquota(uid, ufsvfsp, 0, &xdqp)) {
482 		return (0);
483 	}
484 	dqp = xdqp;
485 	mutex_enter(&dqp->dq_lock);
486 	if (dqp->dq_fsoftlimit == 0 && dqp->dq_fhardlimit == 0) {
487 		dqput(dqp);
488 		mutex_exit(&dqp->dq_lock);
489 		return (0);
490 	}
491 
492 	/*
493 	 * Skip checks for uid 0 owned files.
494 	 * This check used to require both euid and uid
495 	 * to be 0; but there are no quotas for uid 0 so
496 	 * it really doesn't matter who is writing to the
497 	 * root owned file.  And even root can not write
498 	 * past the user's quota limit.
499 	 */
500 	if (uid == 0)
501 		goto out;
502 
503 	/*
504 	 * Theoretically, this could overflow, but in practice, it
505 	 * won't.  Multi-terabyte file systems are required to have an
506 	 * nbpi value of at least 1MB.  In order to overflow this
507 	 * field, there would have to be 2^32 inodes in the file.
508 	 * That would imply a file system of 2^32 * 1MB, which is
509 	 * 2^(32 + 20), which is 4096 terabytes, which is not
510 	 * contemplated for ufs any time soon.
511 	 */
512 	ncurfiles = dqp->dq_curfiles + change;
513 
514 	/*
515 	 * Dissallow allocation if it would bring the current usage over
516 	 * the hard limit or if the user is over his soft limit and his time
517 	 * has run out.
518 	 */
519 	if (change == 1 && ncurfiles >= dqp->dq_fhardlimit &&
520 	    dqp->dq_fhardlimit && !force) {
521 		/* If the user was not informed yet and the caller	*/
522 		/* is the owner of the file 				*/
523 		if ((dqp->dq_flags & DQ_FILES) == 0 && uid == crgetruid(cr)) {
524 			errmsg = err1;
525 			dqp->dq_flags |= DQ_FILES;
526 		}
527 		error = EDQUOT;
528 	} else if (change == 1 && ncurfiles >= dqp->dq_fsoftlimit &&
529 			dqp->dq_fsoftlimit) {
530 		now = gethrestime_sec();
531 		if (ncurfiles == dqp->dq_fsoftlimit ||
532 		    dqp->dq_ftimelimit == 0) {
533 			dqp->dq_flags |= DQ_MOD;
534 			dqp->dq_ftimelimit = now + ufsvfsp->vfs_ftimelimit;
535 			/* If the caller owns the file */
536 			if (uid == crgetruid(cr))
537 				errmsg = err2;
538 		} else if (now > dqp->dq_ftimelimit && !force) {
539 			/* If the user was not informed yet and the	*/
540 			/* caller is the owner of the file 		*/
541 			if ((dqp->dq_flags & DQ_FILES) == 0 &&
542 				uid == crgetruid(cr)) {
543 				errmsg = err3;
544 				dqp->dq_flags |= DQ_FILES;
545 			}
546 			error = EDQUOT;
547 		}
548 	}
549 out:
550 	if (error == 0) {
551 		dqp->dq_flags |= DQ_MOD;
552 		dqp->dq_curfiles += change;
553 	}
554 	if (dqp->dq_flags & DQ_MOD)
555 		TRANS_QUOTA(dqp);
556 	dqput(dqp);
557 	mutex_exit(&dqp->dq_lock);
558 	/*
559 	 * Check for any error messages to be sent
560 	 */
561 	if (errmsg != NULL) {
562 		/*
563 		 * Send message to the error log.
564 		 */
565 		if (uerrp != NULL) {
566 			/*
567 			 * Set up message caller should send to user;
568 			 * gets copied to the message buffer as a side-
569 			 * effect of the caller's uprintf().
570 			 */
571 			*lenp = strlen(errmsg) + 20 + 20 +
572 				strlen(ufsvfsp->vfs_fs->fs_fsmnt) + 1;
573 			*uerrp = (char *)kmem_alloc(*lenp, KM_NOSLEEP);
574 			if (*uerrp != NULL) {
575 				/* errmsg+1 => skip leading ! */
576 				(void) sprintf(*uerrp, errmsg+1,
577 					(int)ttoproc(curthread)->p_pid,
578 					(int)uid, ufsvfsp->vfs_fs->fs_fsmnt);
579 			}
580 		} else {
581 			/*
582 			 * Caller doesn't care, so just copy to the
583 			 * message buffer.
584 			 */
585 			cmn_err(CE_NOTE, errmsg,
586 				(int)ttoproc(curthread)->p_pid,
587 				(int)uid, ufsvfsp->vfs_fs->fs_fsmnt);
588 		}
589 	}
590 	return (error);
591 }
592 
593 /*
594  * Release a dquot.
595  */
596 void
597 dqrele(struct dquot *dqp)
598 {
599 	/*
600 	 * Shadow inodes and extended attribute directories
601 	 * do not have quota info records.
602 	 */
603 	if (dqp != NULL) {
604 		mutex_enter(&dqp->dq_lock);
605 		if (dqp->dq_cnt == 1 && dqp->dq_flags & DQ_MOD)
606 			dqupdate(dqp);
607 		dqput(dqp);
608 		mutex_exit(&dqp->dq_lock);
609 	}
610 }
611