17c478bd9Sstevel@tonic-gate /* 27c478bd9Sstevel@tonic-gate * CDDL HEADER START 37c478bd9Sstevel@tonic-gate * 47c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the 580d34432Sfrankho * Common Development and Distribution License (the "License"). 680d34432Sfrankho * You may not use this file except in compliance with the License. 77c478bd9Sstevel@tonic-gate * 87c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 97c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 107c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions 117c478bd9Sstevel@tonic-gate * and limitations under the License. 127c478bd9Sstevel@tonic-gate * 137c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 147c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 157c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 167c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 177c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 187c478bd9Sstevel@tonic-gate * 197c478bd9Sstevel@tonic-gate * CDDL HEADER END 207c478bd9Sstevel@tonic-gate */ 217c478bd9Sstevel@tonic-gate /* 2280d34432Sfrankho * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 237c478bd9Sstevel@tonic-gate * Use is subject to license terms. 247c478bd9Sstevel@tonic-gate */ 257c478bd9Sstevel@tonic-gate 267c478bd9Sstevel@tonic-gate /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ 277c478bd9Sstevel@tonic-gate /* All Rights Reserved */ 287c478bd9Sstevel@tonic-gate 297c478bd9Sstevel@tonic-gate /* 307c478bd9Sstevel@tonic-gate * University Copyright- Copyright (c) 1982, 1986, 1988 317c478bd9Sstevel@tonic-gate * The Regents of the University of California 327c478bd9Sstevel@tonic-gate * All Rights Reserved 337c478bd9Sstevel@tonic-gate * 347c478bd9Sstevel@tonic-gate * University Acknowledgment- Portions of this document are derived from 357c478bd9Sstevel@tonic-gate * software developed by the University of California, Berkeley, and its 367c478bd9Sstevel@tonic-gate * contributors. 377c478bd9Sstevel@tonic-gate */ 387c478bd9Sstevel@tonic-gate 397c478bd9Sstevel@tonic-gate 407c478bd9Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 417c478bd9Sstevel@tonic-gate 427c478bd9Sstevel@tonic-gate /* 437c478bd9Sstevel@tonic-gate * Quota system calls. 447c478bd9Sstevel@tonic-gate */ 457c478bd9Sstevel@tonic-gate #include <sys/types.h> 467c478bd9Sstevel@tonic-gate #include <sys/t_lock.h> 477c478bd9Sstevel@tonic-gate #include <sys/param.h> 487c478bd9Sstevel@tonic-gate #include <sys/time.h> 497c478bd9Sstevel@tonic-gate #include <sys/systm.h> 507c478bd9Sstevel@tonic-gate #include <sys/signal.h> 517c478bd9Sstevel@tonic-gate #include <sys/cred.h> 527c478bd9Sstevel@tonic-gate #include <sys/proc.h> 537c478bd9Sstevel@tonic-gate #include <sys/user.h> 547c478bd9Sstevel@tonic-gate #include <sys/proc.h> 557c478bd9Sstevel@tonic-gate #include <sys/vfs.h> 567c478bd9Sstevel@tonic-gate #include <sys/vnode.h> 577c478bd9Sstevel@tonic-gate #include <sys/uio.h> 587c478bd9Sstevel@tonic-gate #include <sys/buf.h> 597c478bd9Sstevel@tonic-gate #include <sys/file.h> 607c478bd9Sstevel@tonic-gate #include <sys/fs/ufs_inode.h> 617c478bd9Sstevel@tonic-gate #include <sys/fs/ufs_fs.h> 627c478bd9Sstevel@tonic-gate #include <sys/fs/ufs_quota.h> 637c478bd9Sstevel@tonic-gate #include <sys/errno.h> 647c478bd9Sstevel@tonic-gate #include <sys/debug.h> 657c478bd9Sstevel@tonic-gate #include <sys/cmn_err.h> 667c478bd9Sstevel@tonic-gate #include <sys/pathname.h> 677c478bd9Sstevel@tonic-gate #include <sys/mntent.h> 687c478bd9Sstevel@tonic-gate #include <sys/policy.h> 697c478bd9Sstevel@tonic-gate 707c478bd9Sstevel@tonic-gate static int opendq(); 717c478bd9Sstevel@tonic-gate static int setquota(); 727c478bd9Sstevel@tonic-gate static int getquota(); 737c478bd9Sstevel@tonic-gate static int quotasync(); 747c478bd9Sstevel@tonic-gate 757c478bd9Sstevel@tonic-gate /* 767c478bd9Sstevel@tonic-gate * Quota sub-system init flag. 777c478bd9Sstevel@tonic-gate */ 787c478bd9Sstevel@tonic-gate int quotas_initialized = 0; 797c478bd9Sstevel@tonic-gate 807c478bd9Sstevel@tonic-gate /* 817c478bd9Sstevel@tonic-gate * Sys call to allow users to find out 827c478bd9Sstevel@tonic-gate * their current position wrt quota's 837c478bd9Sstevel@tonic-gate * and to allow privileged users to alter it. 847c478bd9Sstevel@tonic-gate */ 857c478bd9Sstevel@tonic-gate 867c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 877c478bd9Sstevel@tonic-gate int 887c478bd9Sstevel@tonic-gate quotactl(struct vnode *vp, intptr_t arg, int flag, struct cred *cr) 897c478bd9Sstevel@tonic-gate { 907c478bd9Sstevel@tonic-gate struct quotctl quot; 917c478bd9Sstevel@tonic-gate struct ufsvfs *ufsvfsp; 927c478bd9Sstevel@tonic-gate int error = 0; 937c478bd9Sstevel@tonic-gate 947c478bd9Sstevel@tonic-gate if ((flag & DATAMODEL_MASK) == DATAMODEL_NATIVE) { 957c478bd9Sstevel@tonic-gate if (copyin((caddr_t)arg, ", sizeof (struct quotctl))) 967c478bd9Sstevel@tonic-gate return (EFAULT); 977c478bd9Sstevel@tonic-gate } 987c478bd9Sstevel@tonic-gate #ifdef _SYSCALL32_IMPL 997c478bd9Sstevel@tonic-gate else { 1007c478bd9Sstevel@tonic-gate /* quotctl struct from ILP32 callers */ 1017c478bd9Sstevel@tonic-gate struct quotctl32 quot32; 1027c478bd9Sstevel@tonic-gate if (copyin((caddr_t)arg, "32, sizeof (struct quotctl32))) 1037c478bd9Sstevel@tonic-gate return (EFAULT); 1047c478bd9Sstevel@tonic-gate quot.op = quot32.op; 1057c478bd9Sstevel@tonic-gate quot.uid = quot32.uid; 1067c478bd9Sstevel@tonic-gate quot.addr = (caddr_t)(uintptr_t)quot32.addr; 1077c478bd9Sstevel@tonic-gate } 1087c478bd9Sstevel@tonic-gate #endif /* _SYSCALL32_IMPL */ 1097c478bd9Sstevel@tonic-gate 1107c478bd9Sstevel@tonic-gate if (quot.uid < 0) 1117c478bd9Sstevel@tonic-gate quot.uid = crgetruid(cr); 1127c478bd9Sstevel@tonic-gate if (quot.op == Q_SYNC && vp == NULL) { 1137c478bd9Sstevel@tonic-gate ufsvfsp = NULL; 1147c478bd9Sstevel@tonic-gate } else if (quot.op != Q_ALLSYNC) { 1157c478bd9Sstevel@tonic-gate ufsvfsp = (struct ufsvfs *)(vp->v_vfsp->vfs_data); 1167c478bd9Sstevel@tonic-gate } 1177c478bd9Sstevel@tonic-gate switch (quot.op) { 1187c478bd9Sstevel@tonic-gate 1197c478bd9Sstevel@tonic-gate case Q_QUOTAON: 1207c478bd9Sstevel@tonic-gate rw_enter(&dq_rwlock, RW_WRITER); 1217c478bd9Sstevel@tonic-gate if (quotas_initialized == 0) { 1227c478bd9Sstevel@tonic-gate qtinit2(); 1237c478bd9Sstevel@tonic-gate quotas_initialized = 1; 1247c478bd9Sstevel@tonic-gate } 1257c478bd9Sstevel@tonic-gate rw_exit(&dq_rwlock); 1267c478bd9Sstevel@tonic-gate error = opendq(ufsvfsp, vp, cr); 1277c478bd9Sstevel@tonic-gate break; 1287c478bd9Sstevel@tonic-gate 1297c478bd9Sstevel@tonic-gate case Q_QUOTAOFF: 1307c478bd9Sstevel@tonic-gate error = closedq(ufsvfsp, cr); 1317c478bd9Sstevel@tonic-gate if (!error) { 1327c478bd9Sstevel@tonic-gate invalidatedq(ufsvfsp); 1337c478bd9Sstevel@tonic-gate } 1347c478bd9Sstevel@tonic-gate break; 1357c478bd9Sstevel@tonic-gate 1367c478bd9Sstevel@tonic-gate case Q_SETQUOTA: 1377c478bd9Sstevel@tonic-gate case Q_SETQLIM: 1387c478bd9Sstevel@tonic-gate error = setquota(quot.op, (uid_t)quot.uid, ufsvfsp, 1397c478bd9Sstevel@tonic-gate quot.addr, cr); 1407c478bd9Sstevel@tonic-gate break; 1417c478bd9Sstevel@tonic-gate 1427c478bd9Sstevel@tonic-gate case Q_GETQUOTA: 1437c478bd9Sstevel@tonic-gate error = getquota((uid_t)quot.uid, ufsvfsp, (caddr_t)quot.addr, 1447c478bd9Sstevel@tonic-gate cr); 1457c478bd9Sstevel@tonic-gate break; 1467c478bd9Sstevel@tonic-gate 1477c478bd9Sstevel@tonic-gate case Q_SYNC: 1487c478bd9Sstevel@tonic-gate error = qsync(ufsvfsp); 1497c478bd9Sstevel@tonic-gate break; 1507c478bd9Sstevel@tonic-gate 1517c478bd9Sstevel@tonic-gate case Q_ALLSYNC: 1527c478bd9Sstevel@tonic-gate (void) qsync(NULL); 1537c478bd9Sstevel@tonic-gate break; 1547c478bd9Sstevel@tonic-gate 1557c478bd9Sstevel@tonic-gate default: 1567c478bd9Sstevel@tonic-gate error = EINVAL; 1577c478bd9Sstevel@tonic-gate break; 1587c478bd9Sstevel@tonic-gate } 1597c478bd9Sstevel@tonic-gate return (error); 1607c478bd9Sstevel@tonic-gate } 1617c478bd9Sstevel@tonic-gate 1627c478bd9Sstevel@tonic-gate static int 1637c478bd9Sstevel@tonic-gate opendq_scan_inode(struct inode *ip, void *arg) 1647c478bd9Sstevel@tonic-gate { 1657c478bd9Sstevel@tonic-gate struct ufsvfs *ufsvfsp = ip->i_ufsvfs; 1667c478bd9Sstevel@tonic-gate 1677c478bd9Sstevel@tonic-gate /* 1687c478bd9Sstevel@tonic-gate * wrong file system or this is the quota inode; keep looking 1697c478bd9Sstevel@tonic-gate */ 1707c478bd9Sstevel@tonic-gate if (ufsvfsp != (struct ufsvfs *)arg || ip == ip->i_ufsvfs->vfs_qinod) { 1717c478bd9Sstevel@tonic-gate return (0); 1727c478bd9Sstevel@tonic-gate } 1737c478bd9Sstevel@tonic-gate 1747c478bd9Sstevel@tonic-gate ASSERT(RW_WRITE_HELD(&ufsvfsp->vfs_dqrwlock)); 1757c478bd9Sstevel@tonic-gate rw_enter(&ip->i_contents, RW_WRITER); 1767c478bd9Sstevel@tonic-gate /* 1777c478bd9Sstevel@tonic-gate * This inode is in the cache (by definition), is still valid, 1787c478bd9Sstevel@tonic-gate * and is not a shadow inode or extended attribute directory inode, 1797c478bd9Sstevel@tonic-gate * but does not have a quota so get the quota information. 1807c478bd9Sstevel@tonic-gate */ 1817c478bd9Sstevel@tonic-gate if (ip->i_mode && (ip->i_mode & IFMT) != IFSHAD && 1827c478bd9Sstevel@tonic-gate (ip->i_mode & IFMT) != IFATTRDIR && ip->i_dquot == NULL) { 1837c478bd9Sstevel@tonic-gate ip->i_dquot = getinoquota(ip); 1847c478bd9Sstevel@tonic-gate } 1857c478bd9Sstevel@tonic-gate rw_exit(&ip->i_contents); 1867c478bd9Sstevel@tonic-gate 1877c478bd9Sstevel@tonic-gate return (0); 1887c478bd9Sstevel@tonic-gate } 1897c478bd9Sstevel@tonic-gate 1907c478bd9Sstevel@tonic-gate /* 1917c478bd9Sstevel@tonic-gate * Set the quota file up for a particular file system. 1927c478bd9Sstevel@tonic-gate * Called as the result of a quotaon (Q_QUOTAON) ioctl. 1937c478bd9Sstevel@tonic-gate */ 1947c478bd9Sstevel@tonic-gate static int 1957c478bd9Sstevel@tonic-gate opendq( 1967c478bd9Sstevel@tonic-gate struct ufsvfs *ufsvfsp, 1977c478bd9Sstevel@tonic-gate struct vnode *vp, /* quota file */ 1987c478bd9Sstevel@tonic-gate struct cred *cr) 1997c478bd9Sstevel@tonic-gate { 2007c478bd9Sstevel@tonic-gate struct inode *qip; 2017c478bd9Sstevel@tonic-gate struct dquot *dqp; 2027c478bd9Sstevel@tonic-gate int error; 2037c478bd9Sstevel@tonic-gate int quotaon = 0; 2047c478bd9Sstevel@tonic-gate 2057c478bd9Sstevel@tonic-gate if (secpolicy_fs_quota(cr, ufsvfsp->vfs_vfs) != 0) 2067c478bd9Sstevel@tonic-gate return (EPERM); 2077c478bd9Sstevel@tonic-gate 2087c478bd9Sstevel@tonic-gate VN_HOLD(vp); 2097c478bd9Sstevel@tonic-gate 2107c478bd9Sstevel@tonic-gate /* 2117c478bd9Sstevel@tonic-gate * Check to be sure its a regular file. 2127c478bd9Sstevel@tonic-gate */ 2137c478bd9Sstevel@tonic-gate if (vp->v_type != VREG) { 2147c478bd9Sstevel@tonic-gate VN_RELE(vp); 2157c478bd9Sstevel@tonic-gate return (EACCES); 2167c478bd9Sstevel@tonic-gate } 2177c478bd9Sstevel@tonic-gate 2187c478bd9Sstevel@tonic-gate rw_enter(&ufsvfsp->vfs_dqrwlock, RW_WRITER); 2197c478bd9Sstevel@tonic-gate 2207c478bd9Sstevel@tonic-gate /* 2217c478bd9Sstevel@tonic-gate * We have vfs_dqrwlock as writer, so if quotas are disabled, 2227c478bd9Sstevel@tonic-gate * then vfs_qinod should be NULL or we have a race somewhere. 2237c478bd9Sstevel@tonic-gate */ 2247c478bd9Sstevel@tonic-gate ASSERT((ufsvfsp->vfs_qflags & MQ_ENABLED) || (ufsvfsp->vfs_qinod == 0)); 2257c478bd9Sstevel@tonic-gate 2267c478bd9Sstevel@tonic-gate if ((ufsvfsp->vfs_qflags & MQ_ENABLED) != 0) { 2277c478bd9Sstevel@tonic-gate /* 2287c478bd9Sstevel@tonic-gate * Quotas are already enabled on this file system. 2297c478bd9Sstevel@tonic-gate * 2307c478bd9Sstevel@tonic-gate * If the "quotas" file was replaced (different inode) 2317c478bd9Sstevel@tonic-gate * while quotas were enabled we don't want to re-enable 2327c478bd9Sstevel@tonic-gate * them with a new "quotas" file. Simply print a warning 2337c478bd9Sstevel@tonic-gate * message to the console, release the new vnode, and 2347c478bd9Sstevel@tonic-gate * return. 2357c478bd9Sstevel@tonic-gate * XXX - The right way to fix this is to return EBUSY 2367c478bd9Sstevel@tonic-gate * for the ioctl() issued by 'quotaon'. 2377c478bd9Sstevel@tonic-gate */ 2387c478bd9Sstevel@tonic-gate if (VTOI(vp) != ufsvfsp->vfs_qinod) { 2397c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "Previous quota file still in use." 2407c478bd9Sstevel@tonic-gate " Disable quotas on %s before enabling.\n", 2417c478bd9Sstevel@tonic-gate VTOI(vp)->i_fs->fs_fsmnt); 2427c478bd9Sstevel@tonic-gate VN_RELE(vp); 2437c478bd9Sstevel@tonic-gate rw_exit(&ufsvfsp->vfs_dqrwlock); 2447c478bd9Sstevel@tonic-gate return (0); 2457c478bd9Sstevel@tonic-gate } 2467c478bd9Sstevel@tonic-gate (void) quotasync(ufsvfsp, /* do_lock */ 0); 2477c478bd9Sstevel@tonic-gate /* remove extra hold on quota file */ 2487c478bd9Sstevel@tonic-gate VN_RELE(vp); 2497c478bd9Sstevel@tonic-gate quotaon++; 2507c478bd9Sstevel@tonic-gate qip = ufsvfsp->vfs_qinod; 2517c478bd9Sstevel@tonic-gate } else { 2527c478bd9Sstevel@tonic-gate int qlen; 2537c478bd9Sstevel@tonic-gate 2547c478bd9Sstevel@tonic-gate ufsvfsp->vfs_qinod = VTOI(vp); 2557c478bd9Sstevel@tonic-gate qip = ufsvfsp->vfs_qinod; 2567c478bd9Sstevel@tonic-gate /* 2577c478bd9Sstevel@tonic-gate * Force the file to have no partially allocated blocks 2587c478bd9Sstevel@tonic-gate * to prevent a realloc from changing the location of 2597c478bd9Sstevel@tonic-gate * the data. We must do this even if not logging in 2607c478bd9Sstevel@tonic-gate * case we later remount to logging. 2617c478bd9Sstevel@tonic-gate */ 2627c478bd9Sstevel@tonic-gate qlen = qip->i_fs->fs_bsize * NDADDR; 2637c478bd9Sstevel@tonic-gate 2647c478bd9Sstevel@tonic-gate /* 2657c478bd9Sstevel@tonic-gate * Largefiles: i_size needs to be atomically accessed now. 2667c478bd9Sstevel@tonic-gate */ 2677c478bd9Sstevel@tonic-gate rw_enter(&qip->i_contents, RW_WRITER); 2687c478bd9Sstevel@tonic-gate if (qip->i_size < qlen) { 2697c478bd9Sstevel@tonic-gate if (ufs_itrunc(qip, (u_offset_t)qlen, (int)0, cr) != 0) 2707c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "opendq failed to remove frags" 2717c478bd9Sstevel@tonic-gate " from quota file\n"); 2727c478bd9Sstevel@tonic-gate rw_exit(&qip->i_contents); 2737c478bd9Sstevel@tonic-gate (void) VOP_PUTPAGE(vp, (offset_t)0, (size_t)qip->i_size, 274*da6c28aaSamw B_INVAL, kcred, NULL); 2757c478bd9Sstevel@tonic-gate } else { 2767c478bd9Sstevel@tonic-gate rw_exit(&qip->i_contents); 2777c478bd9Sstevel@tonic-gate } 2787c478bd9Sstevel@tonic-gate TRANS_MATA_IGET(ufsvfsp, qip); 2797c478bd9Sstevel@tonic-gate } 2807c478bd9Sstevel@tonic-gate 2817c478bd9Sstevel@tonic-gate /* 2827c478bd9Sstevel@tonic-gate * The file system time limits are in the dquot for uid 0. 2837c478bd9Sstevel@tonic-gate * The time limits set the relative time the other users 2847c478bd9Sstevel@tonic-gate * can be over quota for this file system. 2857c478bd9Sstevel@tonic-gate * If it is zero a default is used (see quota.h). 2867c478bd9Sstevel@tonic-gate */ 2877c478bd9Sstevel@tonic-gate error = getdiskquota((uid_t)0, ufsvfsp, 1, &dqp); 2887c478bd9Sstevel@tonic-gate if (error == 0) { 2897c478bd9Sstevel@tonic-gate mutex_enter(&dqp->dq_lock); 2907c478bd9Sstevel@tonic-gate ufsvfsp->vfs_btimelimit = 2917c478bd9Sstevel@tonic-gate (dqp->dq_btimelimit? dqp->dq_btimelimit: DQ_BTIMELIMIT); 2927c478bd9Sstevel@tonic-gate ufsvfsp->vfs_ftimelimit = 2937c478bd9Sstevel@tonic-gate (dqp->dq_ftimelimit? dqp->dq_ftimelimit: DQ_FTIMELIMIT); 2947c478bd9Sstevel@tonic-gate 2957c478bd9Sstevel@tonic-gate ufsvfsp->vfs_qflags = MQ_ENABLED; /* enable quotas */ 2967c478bd9Sstevel@tonic-gate vfs_setmntopt(ufsvfsp->vfs_vfs, MNTOPT_QUOTA, NULL, 0); 2977c478bd9Sstevel@tonic-gate dqput(dqp); 2987c478bd9Sstevel@tonic-gate mutex_exit(&dqp->dq_lock); 2997c478bd9Sstevel@tonic-gate } else if (!quotaon) { 3007c478bd9Sstevel@tonic-gate /* 3017c478bd9Sstevel@tonic-gate * Some sort of I/O error on the quota file, and quotas were 3027c478bd9Sstevel@tonic-gate * not already on when we got here so clean up. 3037c478bd9Sstevel@tonic-gate */ 3047c478bd9Sstevel@tonic-gate ufsvfsp->vfs_qflags = 0; 3057c478bd9Sstevel@tonic-gate ufsvfsp->vfs_qinod = NULL; 3067c478bd9Sstevel@tonic-gate VN_RELE(ITOV(qip)); 3077c478bd9Sstevel@tonic-gate } 3087c478bd9Sstevel@tonic-gate 3097c478bd9Sstevel@tonic-gate /* 3107c478bd9Sstevel@tonic-gate * If quotas are enabled update all valid inodes in the 3117c478bd9Sstevel@tonic-gate * cache with quota information. 3127c478bd9Sstevel@tonic-gate */ 3137c478bd9Sstevel@tonic-gate if (ufsvfsp->vfs_qflags & MQ_ENABLED) { 3147c478bd9Sstevel@tonic-gate (void) ufs_scan_inodes(0, opendq_scan_inode, ufsvfsp, ufsvfsp); 3157c478bd9Sstevel@tonic-gate } 3167c478bd9Sstevel@tonic-gate 3177c478bd9Sstevel@tonic-gate rw_exit(&ufsvfsp->vfs_dqrwlock); 3187c478bd9Sstevel@tonic-gate return (error); 3197c478bd9Sstevel@tonic-gate } 3207c478bd9Sstevel@tonic-gate 3217c478bd9Sstevel@tonic-gate static int 3227c478bd9Sstevel@tonic-gate closedq_scan_inode(struct inode *ip, void *arg) 3237c478bd9Sstevel@tonic-gate { 3247c478bd9Sstevel@tonic-gate struct dquot *dqp; 3257c478bd9Sstevel@tonic-gate struct ufsvfs *ufsvfsp = ip->i_ufsvfs; 3267c478bd9Sstevel@tonic-gate 3277c478bd9Sstevel@tonic-gate /* 3287c478bd9Sstevel@tonic-gate * wrong file system; keep looking 3297c478bd9Sstevel@tonic-gate */ 3307c478bd9Sstevel@tonic-gate if (ufsvfsp != (struct ufsvfs *)arg) 3317c478bd9Sstevel@tonic-gate return (0); 3327c478bd9Sstevel@tonic-gate 3337c478bd9Sstevel@tonic-gate ASSERT(RW_WRITE_HELD(&ufsvfsp->vfs_dqrwlock)); 3347c478bd9Sstevel@tonic-gate rw_enter(&ip->i_contents, RW_WRITER); 3357c478bd9Sstevel@tonic-gate 3367c478bd9Sstevel@tonic-gate /* 3377c478bd9Sstevel@tonic-gate * Shadow inodes and extended attribute directories 3387c478bd9Sstevel@tonic-gate * do not have quota info records. 3397c478bd9Sstevel@tonic-gate */ 3407c478bd9Sstevel@tonic-gate if ((dqp = ip->i_dquot) != NULL) { 3417c478bd9Sstevel@tonic-gate ASSERT((ip->i_mode & IFMT) != IFSHAD); 3427c478bd9Sstevel@tonic-gate ASSERT((ip->i_mode & IFMT) != IFATTRDIR); 3437c478bd9Sstevel@tonic-gate ip->i_dquot = NULL; 3447c478bd9Sstevel@tonic-gate mutex_enter(&dqp->dq_lock); 3457c478bd9Sstevel@tonic-gate dqput(dqp); 3467c478bd9Sstevel@tonic-gate 3477c478bd9Sstevel@tonic-gate /* 3487c478bd9Sstevel@tonic-gate * If we have a pending logging file system quota 3497c478bd9Sstevel@tonic-gate * transaction, then cancel it. Clear the flag to 3507c478bd9Sstevel@tonic-gate * prevent ufs_trans_push_quota() from trying to 3517c478bd9Sstevel@tonic-gate * deal with this transaction just in case it is 3527c478bd9Sstevel@tonic-gate * waiting for the mutex. We decrement the counter 3537c478bd9Sstevel@tonic-gate * since the transaction won't be needing the quota 3547c478bd9Sstevel@tonic-gate * info record anymore. 3557c478bd9Sstevel@tonic-gate */ 3567c478bd9Sstevel@tonic-gate if (dqp->dq_flags & DQ_TRANS) { 3577c478bd9Sstevel@tonic-gate dqp->dq_flags &= ~DQ_TRANS; 3587c478bd9Sstevel@tonic-gate dqput(dqp); 3597c478bd9Sstevel@tonic-gate } 3607c478bd9Sstevel@tonic-gate mutex_exit(&dqp->dq_lock); 3617c478bd9Sstevel@tonic-gate } 3627c478bd9Sstevel@tonic-gate rw_exit(&ip->i_contents); 3637c478bd9Sstevel@tonic-gate 3647c478bd9Sstevel@tonic-gate return (0); 3657c478bd9Sstevel@tonic-gate } 3667c478bd9Sstevel@tonic-gate 3677c478bd9Sstevel@tonic-gate /* 3687c478bd9Sstevel@tonic-gate * Close off disk quotas for a file system. 3697c478bd9Sstevel@tonic-gate */ 3707c478bd9Sstevel@tonic-gate int 3717c478bd9Sstevel@tonic-gate closedq(struct ufsvfs *ufsvfsp, struct cred *cr) 3727c478bd9Sstevel@tonic-gate { 3737c478bd9Sstevel@tonic-gate struct inode *qip; 3747c478bd9Sstevel@tonic-gate 3757c478bd9Sstevel@tonic-gate if (secpolicy_fs_quota(cr, ufsvfsp->vfs_vfs) != 0) 3767c478bd9Sstevel@tonic-gate return (EPERM); 3777c478bd9Sstevel@tonic-gate 3787c478bd9Sstevel@tonic-gate rw_enter(&ufsvfsp->vfs_dqrwlock, RW_WRITER); 3797c478bd9Sstevel@tonic-gate 3807c478bd9Sstevel@tonic-gate /* 3817c478bd9Sstevel@tonic-gate * Quotas are not enabled on this file system so there is 3827c478bd9Sstevel@tonic-gate * nothing more to do. 3837c478bd9Sstevel@tonic-gate */ 3847c478bd9Sstevel@tonic-gate if ((ufsvfsp->vfs_qflags & MQ_ENABLED) == 0) { 3857c478bd9Sstevel@tonic-gate rw_exit(&ufsvfsp->vfs_dqrwlock); 3867c478bd9Sstevel@tonic-gate return (0); 3877c478bd9Sstevel@tonic-gate } 3887c478bd9Sstevel@tonic-gate 3897c478bd9Sstevel@tonic-gate /* 3907c478bd9Sstevel@tonic-gate * At this point, the quota subsystem is quiescent on this file 3917c478bd9Sstevel@tonic-gate * system so we can do all the work necessary to dismantle the 3927c478bd9Sstevel@tonic-gate * quota stuff. 3937c478bd9Sstevel@tonic-gate */ 3947c478bd9Sstevel@tonic-gate qip = ufsvfsp->vfs_qinod; 3957c478bd9Sstevel@tonic-gate if (!qip) 3967c478bd9Sstevel@tonic-gate return (ufs_fault(ufsvfsp->vfs_root, "closedq: NULL qip")); 3977c478bd9Sstevel@tonic-gate 3987c478bd9Sstevel@tonic-gate ufsvfsp->vfs_qflags = 0; /* disable quotas */ 3997c478bd9Sstevel@tonic-gate vfs_setmntopt(ufsvfsp->vfs_vfs, MNTOPT_NOQUOTA, NULL, 0); 4007c478bd9Sstevel@tonic-gate 4017c478bd9Sstevel@tonic-gate /* 4027c478bd9Sstevel@tonic-gate * ufs_scan_inodes() depends on vfs_qinod, so we can't 4037c478bd9Sstevel@tonic-gate * clear it until afterwards. 4047c478bd9Sstevel@tonic-gate */ 4057c478bd9Sstevel@tonic-gate (void) ufs_scan_inodes(0, closedq_scan_inode, ufsvfsp, ufsvfsp); 4067c478bd9Sstevel@tonic-gate 4077c478bd9Sstevel@tonic-gate ufsvfsp->vfs_qinod = NULL; 4087c478bd9Sstevel@tonic-gate rw_exit(&ufsvfsp->vfs_dqrwlock); 4097c478bd9Sstevel@tonic-gate 4107c478bd9Sstevel@tonic-gate /* 4117c478bd9Sstevel@tonic-gate * Sync and release the quota file inode. Since we have a private 4127c478bd9Sstevel@tonic-gate * pointer to the quota inode and vfs_qinod is now clear we do not 4137c478bd9Sstevel@tonic-gate * need to hold vfs_dqrwlock. 4147c478bd9Sstevel@tonic-gate */ 4157c478bd9Sstevel@tonic-gate (void) TRANS_SYNCIP(qip, 0, I_SYNC, TOP_SYNCIP_CLOSEDQ); 4167c478bd9Sstevel@tonic-gate VN_RELE(ITOV(qip)); 4177c478bd9Sstevel@tonic-gate return (0); 4187c478bd9Sstevel@tonic-gate } 4197c478bd9Sstevel@tonic-gate 4207c478bd9Sstevel@tonic-gate /* 4217c478bd9Sstevel@tonic-gate * Private data between setquota() and setquota_scan_inode(). 4227c478bd9Sstevel@tonic-gate */ 4237c478bd9Sstevel@tonic-gate struct setquota_data { 4247c478bd9Sstevel@tonic-gate #define SQD_TYPE_NONE 0 4257c478bd9Sstevel@tonic-gate #define SQD_TYPE_LIMIT 1 4267c478bd9Sstevel@tonic-gate #define SQD_TYPE_NO_LIMIT 2 4277c478bd9Sstevel@tonic-gate int sqd_type; 4287c478bd9Sstevel@tonic-gate struct ufsvfs *sqd_ufsvfsp; 4297c478bd9Sstevel@tonic-gate uid_t sqd_uid; 4307c478bd9Sstevel@tonic-gate }; 4317c478bd9Sstevel@tonic-gate 4327c478bd9Sstevel@tonic-gate static int 4337c478bd9Sstevel@tonic-gate setquota_scan_inode(struct inode *ip, void *arg) 4347c478bd9Sstevel@tonic-gate { 4357c478bd9Sstevel@tonic-gate struct setquota_data *sqdp = (struct setquota_data *)arg; 4367c478bd9Sstevel@tonic-gate struct ufsvfs *ufsvfsp = ip->i_ufsvfs; 4377c478bd9Sstevel@tonic-gate 4387c478bd9Sstevel@tonic-gate /* 4397c478bd9Sstevel@tonic-gate * wrong file system; keep looking 4407c478bd9Sstevel@tonic-gate */ 4417c478bd9Sstevel@tonic-gate if (ufsvfsp != sqdp->sqd_ufsvfsp) 4427c478bd9Sstevel@tonic-gate return (0); 4437c478bd9Sstevel@tonic-gate 4447c478bd9Sstevel@tonic-gate ASSERT(RW_WRITE_HELD(&ufsvfsp->vfs_dqrwlock)); 4457c478bd9Sstevel@tonic-gate 4467c478bd9Sstevel@tonic-gate /* 4477c478bd9Sstevel@tonic-gate * The file system does not have quotas enabled or this is the 4487c478bd9Sstevel@tonic-gate * file system's quota inode; keep looking. 4497c478bd9Sstevel@tonic-gate */ 4507c478bd9Sstevel@tonic-gate if ((ufsvfsp->vfs_qflags & MQ_ENABLED) == 0 || 4517c478bd9Sstevel@tonic-gate ip == ufsvfsp->vfs_qinod) { 4527c478bd9Sstevel@tonic-gate return (0); 4537c478bd9Sstevel@tonic-gate } 4547c478bd9Sstevel@tonic-gate 4557c478bd9Sstevel@tonic-gate rw_enter(&ip->i_contents, RW_WRITER); 4567c478bd9Sstevel@tonic-gate /* 4577c478bd9Sstevel@tonic-gate * This inode is in the cache (by definition), is still valid, 4587c478bd9Sstevel@tonic-gate * is not a shadow inode or extended attribute directory inode 4597c478bd9Sstevel@tonic-gate * and has the right uid. 4607c478bd9Sstevel@tonic-gate */ 4617c478bd9Sstevel@tonic-gate if (ip->i_mode && (ip->i_mode & IFMT) != IFSHAD && 4627c478bd9Sstevel@tonic-gate (ip->i_mode & IFMT) != IFATTRDIR && ip->i_uid == sqdp->sqd_uid) { 4637c478bd9Sstevel@tonic-gate /* 4647c478bd9Sstevel@tonic-gate * Transition is "no limit" to "at least one limit": 4657c478bd9Sstevel@tonic-gate */ 4667c478bd9Sstevel@tonic-gate if (sqdp->sqd_type == SQD_TYPE_LIMIT && 4677c478bd9Sstevel@tonic-gate ip->i_dquot == NULL) { 4687c478bd9Sstevel@tonic-gate ip->i_dquot = getinoquota(ip); 4697c478bd9Sstevel@tonic-gate } 4707c478bd9Sstevel@tonic-gate /* 4717c478bd9Sstevel@tonic-gate * Transition is "at least one limit" to "no limit": 4727c478bd9Sstevel@tonic-gate */ 4737c478bd9Sstevel@tonic-gate else if (sqdp->sqd_type == SQD_TYPE_NO_LIMIT && ip->i_dquot) { 4747c478bd9Sstevel@tonic-gate mutex_enter(&ip->i_dquot->dq_lock); 4757c478bd9Sstevel@tonic-gate dqput(ip->i_dquot); 4767c478bd9Sstevel@tonic-gate mutex_exit(&ip->i_dquot->dq_lock); 4777c478bd9Sstevel@tonic-gate ip->i_dquot = NULL; 4787c478bd9Sstevel@tonic-gate } 4797c478bd9Sstevel@tonic-gate } 4807c478bd9Sstevel@tonic-gate rw_exit(&ip->i_contents); 4817c478bd9Sstevel@tonic-gate 4827c478bd9Sstevel@tonic-gate return (0); 4837c478bd9Sstevel@tonic-gate } 4847c478bd9Sstevel@tonic-gate 4857c478bd9Sstevel@tonic-gate /* 4867c478bd9Sstevel@tonic-gate * Set various fields of the dqblk according to the command. 4877c478bd9Sstevel@tonic-gate * Q_SETQUOTA - assign an entire dqblk structure. 4887c478bd9Sstevel@tonic-gate * Q_SETQLIM - assign a dqblk structure except for the usage. 4897c478bd9Sstevel@tonic-gate */ 4907c478bd9Sstevel@tonic-gate static int 4917c478bd9Sstevel@tonic-gate setquota(int cmd, uid_t uid, struct ufsvfs *ufsvfsp, 4927c478bd9Sstevel@tonic-gate caddr_t addr, struct cred *cr) 4937c478bd9Sstevel@tonic-gate { 4947c478bd9Sstevel@tonic-gate struct dquot *dqp; 4957c478bd9Sstevel@tonic-gate struct inode *qip; 4967c478bd9Sstevel@tonic-gate struct dquot *xdqp; 4977c478bd9Sstevel@tonic-gate struct dqblk newlim; 4987c478bd9Sstevel@tonic-gate int error; 4997c478bd9Sstevel@tonic-gate int scan_type = SQD_TYPE_NONE; 5007c478bd9Sstevel@tonic-gate daddr_t bn; 5017c478bd9Sstevel@tonic-gate int contig; 5027c478bd9Sstevel@tonic-gate 5037c478bd9Sstevel@tonic-gate if (secpolicy_fs_quota(cr, ufsvfsp->vfs_vfs) != 0) 5047c478bd9Sstevel@tonic-gate return (EPERM); 5057c478bd9Sstevel@tonic-gate 5067c478bd9Sstevel@tonic-gate rw_enter(&ufsvfsp->vfs_dqrwlock, RW_WRITER); 5077c478bd9Sstevel@tonic-gate 5087c478bd9Sstevel@tonic-gate /* 5097c478bd9Sstevel@tonic-gate * Quotas are not enabled on this file system so there is 5107c478bd9Sstevel@tonic-gate * nothing more to do. 5117c478bd9Sstevel@tonic-gate */ 5127c478bd9Sstevel@tonic-gate if ((ufsvfsp->vfs_qflags & MQ_ENABLED) == 0) { 5137c478bd9Sstevel@tonic-gate rw_exit(&ufsvfsp->vfs_dqrwlock); 5147c478bd9Sstevel@tonic-gate return (ESRCH); 5157c478bd9Sstevel@tonic-gate } 5167c478bd9Sstevel@tonic-gate 5177c478bd9Sstevel@tonic-gate /* 5187c478bd9Sstevel@tonic-gate * At this point, the quota subsystem is quiescent on this file 5197c478bd9Sstevel@tonic-gate * system so we can do all the work necessary to modify the quota 5207c478bd9Sstevel@tonic-gate * information for this user. 5217c478bd9Sstevel@tonic-gate */ 5227c478bd9Sstevel@tonic-gate 5237c478bd9Sstevel@tonic-gate if (copyin(addr, (caddr_t)&newlim, sizeof (struct dqblk)) != 0) { 5247c478bd9Sstevel@tonic-gate rw_exit(&ufsvfsp->vfs_dqrwlock); 5257c478bd9Sstevel@tonic-gate return (EFAULT); 5267c478bd9Sstevel@tonic-gate } 5277c478bd9Sstevel@tonic-gate error = getdiskquota(uid, ufsvfsp, 0, &xdqp); 5287c478bd9Sstevel@tonic-gate if (error) { 5297c478bd9Sstevel@tonic-gate rw_exit(&ufsvfsp->vfs_dqrwlock); 5307c478bd9Sstevel@tonic-gate return (error); 5317c478bd9Sstevel@tonic-gate } 5327c478bd9Sstevel@tonic-gate dqp = xdqp; 5337c478bd9Sstevel@tonic-gate /* 5347c478bd9Sstevel@tonic-gate * Don't change disk usage on Q_SETQLIM 5357c478bd9Sstevel@tonic-gate */ 5367c478bd9Sstevel@tonic-gate mutex_enter(&dqp->dq_lock); 5377c478bd9Sstevel@tonic-gate if (cmd == Q_SETQLIM) { 5387c478bd9Sstevel@tonic-gate newlim.dqb_curblocks = dqp->dq_curblocks; 5397c478bd9Sstevel@tonic-gate newlim.dqb_curfiles = dqp->dq_curfiles; 5407c478bd9Sstevel@tonic-gate } 5417c478bd9Sstevel@tonic-gate if (uid == 0) { 5427c478bd9Sstevel@tonic-gate /* 5437c478bd9Sstevel@tonic-gate * Timelimits for uid 0 set the relative time 5447c478bd9Sstevel@tonic-gate * the other users can be over quota for this file system. 5457c478bd9Sstevel@tonic-gate * If it is zero a default is used (see quota.h). 5467c478bd9Sstevel@tonic-gate */ 5477c478bd9Sstevel@tonic-gate ufsvfsp->vfs_btimelimit = 5487c478bd9Sstevel@tonic-gate newlim.dqb_btimelimit? newlim.dqb_btimelimit: DQ_BTIMELIMIT; 5497c478bd9Sstevel@tonic-gate ufsvfsp->vfs_ftimelimit = 5507c478bd9Sstevel@tonic-gate newlim.dqb_ftimelimit? newlim.dqb_ftimelimit: DQ_FTIMELIMIT; 5517c478bd9Sstevel@tonic-gate } else { 5527c478bd9Sstevel@tonic-gate if (newlim.dqb_bsoftlimit && 5537c478bd9Sstevel@tonic-gate newlim.dqb_curblocks >= newlim.dqb_bsoftlimit) { 5547c478bd9Sstevel@tonic-gate if (dqp->dq_bsoftlimit == 0 || 5557c478bd9Sstevel@tonic-gate dqp->dq_curblocks < dqp->dq_bsoftlimit) { 5567c478bd9Sstevel@tonic-gate /* If we're suddenly over the limit(s), */ 5577c478bd9Sstevel@tonic-gate /* start the timer(s) */ 5587c478bd9Sstevel@tonic-gate newlim.dqb_btimelimit = 5597c478bd9Sstevel@tonic-gate (uint32_t)gethrestime_sec() + 5607c478bd9Sstevel@tonic-gate ufsvfsp->vfs_btimelimit; 5617c478bd9Sstevel@tonic-gate dqp->dq_flags &= ~DQ_BLKS; 5627c478bd9Sstevel@tonic-gate } else { 5637c478bd9Sstevel@tonic-gate /* If we're currently over the soft */ 5647c478bd9Sstevel@tonic-gate /* limit and were previously over the */ 5657c478bd9Sstevel@tonic-gate /* soft limit then preserve the old */ 5667c478bd9Sstevel@tonic-gate /* time limit but make sure the DQ_BLKS */ 5677c478bd9Sstevel@tonic-gate /* flag is set since we must have been */ 5687c478bd9Sstevel@tonic-gate /* previously warned. */ 5697c478bd9Sstevel@tonic-gate newlim.dqb_btimelimit = dqp->dq_btimelimit; 5707c478bd9Sstevel@tonic-gate dqp->dq_flags |= DQ_BLKS; 5717c478bd9Sstevel@tonic-gate } 5727c478bd9Sstevel@tonic-gate } else { 5737c478bd9Sstevel@tonic-gate /* Either no quota or under quota, clear time limit */ 5747c478bd9Sstevel@tonic-gate newlim.dqb_btimelimit = 0; 5757c478bd9Sstevel@tonic-gate dqp->dq_flags &= ~DQ_BLKS; 5767c478bd9Sstevel@tonic-gate } 5777c478bd9Sstevel@tonic-gate 5787c478bd9Sstevel@tonic-gate if (newlim.dqb_fsoftlimit && 5797c478bd9Sstevel@tonic-gate newlim.dqb_curfiles >= newlim.dqb_fsoftlimit) { 5807c478bd9Sstevel@tonic-gate if (dqp->dq_fsoftlimit == 0 || 5817c478bd9Sstevel@tonic-gate dqp->dq_curfiles < dqp->dq_fsoftlimit) { 5827c478bd9Sstevel@tonic-gate /* If we're suddenly over the limit(s), */ 5837c478bd9Sstevel@tonic-gate /* start the timer(s) */ 5847c478bd9Sstevel@tonic-gate newlim.dqb_ftimelimit = 5857c478bd9Sstevel@tonic-gate (uint32_t)gethrestime_sec() + 5867c478bd9Sstevel@tonic-gate ufsvfsp->vfs_ftimelimit; 5877c478bd9Sstevel@tonic-gate dqp->dq_flags &= ~DQ_FILES; 5887c478bd9Sstevel@tonic-gate } else { 5897c478bd9Sstevel@tonic-gate /* If we're currently over the soft */ 5907c478bd9Sstevel@tonic-gate /* limit and were previously over the */ 5917c478bd9Sstevel@tonic-gate /* soft limit then preserve the old */ 5927c478bd9Sstevel@tonic-gate /* time limit but make sure the */ 5937c478bd9Sstevel@tonic-gate /* DQ_FILES flag is set since we must */ 5947c478bd9Sstevel@tonic-gate /* have been previously warned. */ 5957c478bd9Sstevel@tonic-gate newlim.dqb_ftimelimit = dqp->dq_ftimelimit; 5967c478bd9Sstevel@tonic-gate dqp->dq_flags |= DQ_FILES; 5977c478bd9Sstevel@tonic-gate } 5987c478bd9Sstevel@tonic-gate } else { 5997c478bd9Sstevel@tonic-gate /* Either no quota or under quota, clear time limit */ 6007c478bd9Sstevel@tonic-gate newlim.dqb_ftimelimit = 0; 6017c478bd9Sstevel@tonic-gate dqp->dq_flags &= ~DQ_FILES; 6027c478bd9Sstevel@tonic-gate } 6037c478bd9Sstevel@tonic-gate } 6047c478bd9Sstevel@tonic-gate 6057c478bd9Sstevel@tonic-gate /* 6067c478bd9Sstevel@tonic-gate * If there was previously no limit and there is now at least 6077c478bd9Sstevel@tonic-gate * one limit, then any inodes in the cache have NULL d_iquot 6087c478bd9Sstevel@tonic-gate * fields (getinoquota() returns NULL when there are no limits). 6097c478bd9Sstevel@tonic-gate */ 6107c478bd9Sstevel@tonic-gate if ((dqp->dq_fhardlimit == 0 && dqp->dq_fsoftlimit == 0 && 6117c478bd9Sstevel@tonic-gate dqp->dq_bhardlimit == 0 && dqp->dq_bsoftlimit == 0) && 6127c478bd9Sstevel@tonic-gate (newlim.dqb_fhardlimit || newlim.dqb_fsoftlimit || 6137c478bd9Sstevel@tonic-gate newlim.dqb_bhardlimit || newlim.dqb_bsoftlimit)) { 6147c478bd9Sstevel@tonic-gate scan_type = SQD_TYPE_LIMIT; 6157c478bd9Sstevel@tonic-gate } 6167c478bd9Sstevel@tonic-gate 6177c478bd9Sstevel@tonic-gate /* 6187c478bd9Sstevel@tonic-gate * If there was previously at least one limit and there is now 6197c478bd9Sstevel@tonic-gate * no limit, then any inodes in the cache have non-NULL d_iquot 6207c478bd9Sstevel@tonic-gate * fields need to be reset to NULL. 6217c478bd9Sstevel@tonic-gate */ 6227c478bd9Sstevel@tonic-gate else if ((dqp->dq_fhardlimit || dqp->dq_fsoftlimit || 6237c478bd9Sstevel@tonic-gate dqp->dq_bhardlimit || dqp->dq_bsoftlimit) && 6247c478bd9Sstevel@tonic-gate (newlim.dqb_fhardlimit == 0 && newlim.dqb_fsoftlimit == 0 && 6257c478bd9Sstevel@tonic-gate newlim.dqb_bhardlimit == 0 && newlim.dqb_bsoftlimit == 0)) { 6267c478bd9Sstevel@tonic-gate scan_type = SQD_TYPE_NO_LIMIT; 6277c478bd9Sstevel@tonic-gate } 6287c478bd9Sstevel@tonic-gate 6297c478bd9Sstevel@tonic-gate dqp->dq_dqb = newlim; 6307c478bd9Sstevel@tonic-gate dqp->dq_flags |= DQ_MOD; 6317c478bd9Sstevel@tonic-gate 6327c478bd9Sstevel@tonic-gate /* 6337c478bd9Sstevel@tonic-gate * push the new quota to disk now. If this is a trans device 6347c478bd9Sstevel@tonic-gate * then force the page out with ufs_putpage so it will be deltaed 6357c478bd9Sstevel@tonic-gate * by ufs_startio. 6367c478bd9Sstevel@tonic-gate */ 6377c478bd9Sstevel@tonic-gate qip = ufsvfsp->vfs_qinod; 6387c478bd9Sstevel@tonic-gate rw_enter(&qip->i_contents, RW_WRITER); 6397c478bd9Sstevel@tonic-gate (void) ufs_rdwri(UIO_WRITE, FWRITE | FSYNC, qip, (caddr_t)&dqp->dq_dqb, 6407c478bd9Sstevel@tonic-gate sizeof (struct dqblk), dqoff(uid), UIO_SYSSPACE, 6417c478bd9Sstevel@tonic-gate (int *)NULL, kcred); 6427c478bd9Sstevel@tonic-gate rw_exit(&qip->i_contents); 6437c478bd9Sstevel@tonic-gate 6447c478bd9Sstevel@tonic-gate (void) VOP_PUTPAGE(ITOV(qip), dqoff(dqp->dq_uid) & ~qip->i_fs->fs_bmask, 645*da6c28aaSamw qip->i_fs->fs_bsize, B_INVAL, kcred, NULL); 6467c478bd9Sstevel@tonic-gate 6477c478bd9Sstevel@tonic-gate /* 6487c478bd9Sstevel@tonic-gate * We must set the dq_mof even if not we are not logging in case 6497c478bd9Sstevel@tonic-gate * we are later remount to logging. 6507c478bd9Sstevel@tonic-gate */ 6517c478bd9Sstevel@tonic-gate contig = 0; 6527c478bd9Sstevel@tonic-gate rw_enter(&qip->i_contents, RW_WRITER); 6537c478bd9Sstevel@tonic-gate error = bmap_read(qip, dqoff(dqp->dq_uid), &bn, &contig); 6547c478bd9Sstevel@tonic-gate rw_exit(&qip->i_contents); 6557c478bd9Sstevel@tonic-gate if (error || (bn == UFS_HOLE)) { 6567c478bd9Sstevel@tonic-gate dqp->dq_mof = UFS_HOLE; 6577c478bd9Sstevel@tonic-gate } else { 6587c478bd9Sstevel@tonic-gate dqp->dq_mof = ldbtob(bn) + 6597c478bd9Sstevel@tonic-gate (offset_t)((dqoff(dqp->dq_uid)) & (DEV_BSIZE - 1)); 6607c478bd9Sstevel@tonic-gate } 6617c478bd9Sstevel@tonic-gate 6627c478bd9Sstevel@tonic-gate dqp->dq_flags &= ~DQ_MOD; 6637c478bd9Sstevel@tonic-gate dqput(dqp); 6647c478bd9Sstevel@tonic-gate mutex_exit(&dqp->dq_lock); 6657c478bd9Sstevel@tonic-gate if (scan_type) { 6667c478bd9Sstevel@tonic-gate struct setquota_data sqd; 6677c478bd9Sstevel@tonic-gate 6687c478bd9Sstevel@tonic-gate sqd.sqd_type = scan_type; 6697c478bd9Sstevel@tonic-gate sqd.sqd_ufsvfsp = ufsvfsp; 6707c478bd9Sstevel@tonic-gate sqd.sqd_uid = uid; 6717c478bd9Sstevel@tonic-gate (void) ufs_scan_inodes(0, setquota_scan_inode, &sqd, ufsvfsp); 6727c478bd9Sstevel@tonic-gate } 6737c478bd9Sstevel@tonic-gate rw_exit(&ufsvfsp->vfs_dqrwlock); 6747c478bd9Sstevel@tonic-gate return (0); 6757c478bd9Sstevel@tonic-gate } 6767c478bd9Sstevel@tonic-gate 6777c478bd9Sstevel@tonic-gate /* 6787c478bd9Sstevel@tonic-gate * Q_GETQUOTA - return current values in a dqblk structure. 6797c478bd9Sstevel@tonic-gate */ 6807c478bd9Sstevel@tonic-gate static int 6817c478bd9Sstevel@tonic-gate getquota(uid_t uid, struct ufsvfs *ufsvfsp, caddr_t addr, cred_t *cr) 6827c478bd9Sstevel@tonic-gate { 6837c478bd9Sstevel@tonic-gate struct dquot *dqp; 6847c478bd9Sstevel@tonic-gate struct dquot *xdqp; 6857c478bd9Sstevel@tonic-gate struct dqblk dqb; 6867c478bd9Sstevel@tonic-gate int error = 0; 6877c478bd9Sstevel@tonic-gate 6887c478bd9Sstevel@tonic-gate if (uid != crgetruid(cr) && 6897c478bd9Sstevel@tonic-gate secpolicy_fs_quota(cr, ufsvfsp->vfs_vfs) != 0) 6907c478bd9Sstevel@tonic-gate return (EPERM); 6917c478bd9Sstevel@tonic-gate rw_enter(&ufsvfsp->vfs_dqrwlock, RW_READER); 6927c478bd9Sstevel@tonic-gate if ((ufsvfsp->vfs_qflags & MQ_ENABLED) == 0) { 6937c478bd9Sstevel@tonic-gate rw_exit(&ufsvfsp->vfs_dqrwlock); 6947c478bd9Sstevel@tonic-gate return (ESRCH); 6957c478bd9Sstevel@tonic-gate } 6967c478bd9Sstevel@tonic-gate error = getdiskquota(uid, ufsvfsp, 0, &xdqp); 6977c478bd9Sstevel@tonic-gate if (error) { 6987c478bd9Sstevel@tonic-gate rw_exit(&ufsvfsp->vfs_dqrwlock); 6997c478bd9Sstevel@tonic-gate return (error); 7007c478bd9Sstevel@tonic-gate } 7017c478bd9Sstevel@tonic-gate dqp = xdqp; 7027c478bd9Sstevel@tonic-gate mutex_enter(&dqp->dq_lock); 7037c478bd9Sstevel@tonic-gate if (dqp->dq_fhardlimit == 0 && dqp->dq_fsoftlimit == 0 && 7047c478bd9Sstevel@tonic-gate dqp->dq_bhardlimit == 0 && dqp->dq_bsoftlimit == 0) { 7057c478bd9Sstevel@tonic-gate error = ESRCH; 7067c478bd9Sstevel@tonic-gate } else { 7077c478bd9Sstevel@tonic-gate bcopy(&dqp->dq_dqb, &dqb, sizeof (struct dqblk)); 7087c478bd9Sstevel@tonic-gate } 7097c478bd9Sstevel@tonic-gate dqput(dqp); 7107c478bd9Sstevel@tonic-gate mutex_exit(&dqp->dq_lock); 7117c478bd9Sstevel@tonic-gate rw_exit(&ufsvfsp->vfs_dqrwlock); 7127c478bd9Sstevel@tonic-gate if (error == 0 && copyout(&dqb, addr, sizeof (struct dqblk)) != 0) 7137c478bd9Sstevel@tonic-gate error = EFAULT; 7147c478bd9Sstevel@tonic-gate return (error); 7157c478bd9Sstevel@tonic-gate } 7167c478bd9Sstevel@tonic-gate 7177c478bd9Sstevel@tonic-gate /* 7187c478bd9Sstevel@tonic-gate * Q_SYNC - sync quota files to disk. 7197c478bd9Sstevel@tonic-gate */ 7207c478bd9Sstevel@tonic-gate int 7217c478bd9Sstevel@tonic-gate qsync(struct ufsvfs *ufsvfsp) 7227c478bd9Sstevel@tonic-gate { 7237c478bd9Sstevel@tonic-gate return (quotasync(ufsvfsp, /* do_lock */ 1)); 7247c478bd9Sstevel@tonic-gate } 7257c478bd9Sstevel@tonic-gate 7267c478bd9Sstevel@tonic-gate /* 7277c478bd9Sstevel@tonic-gate * Sync quota information records to disk for the specified file system 7287c478bd9Sstevel@tonic-gate * or all file systems with quotas if ufsvfsp == NULL. Grabs a reader 7297c478bd9Sstevel@tonic-gate * lock on vfs_dqrwlock if it is needed. 7307c478bd9Sstevel@tonic-gate * 7317c478bd9Sstevel@tonic-gate * Currently, if ufsvfsp is NULL, then do_lock is always true, but the 7327c478bd9Sstevel@tonic-gate * routine is coded to account for either do_lock value. This seemed 7337c478bd9Sstevel@tonic-gate * to be the safer thing to do. 7347c478bd9Sstevel@tonic-gate */ 7357c478bd9Sstevel@tonic-gate int 7367c478bd9Sstevel@tonic-gate quotasync(struct ufsvfs *ufsvfsp, int do_lock) 7377c478bd9Sstevel@tonic-gate { 7387c478bd9Sstevel@tonic-gate struct dquot *dqp; 7397c478bd9Sstevel@tonic-gate 7407c478bd9Sstevel@tonic-gate rw_enter(&dq_rwlock, RW_READER); 7417c478bd9Sstevel@tonic-gate if (!quotas_initialized) { 7427c478bd9Sstevel@tonic-gate rw_exit(&dq_rwlock); 7437c478bd9Sstevel@tonic-gate return (ESRCH); 7447c478bd9Sstevel@tonic-gate } 7457c478bd9Sstevel@tonic-gate rw_exit(&dq_rwlock); 7467c478bd9Sstevel@tonic-gate 7477c478bd9Sstevel@tonic-gate /* 7487c478bd9Sstevel@tonic-gate * The operation applies to a specific file system only. 7497c478bd9Sstevel@tonic-gate */ 7507c478bd9Sstevel@tonic-gate if (ufsvfsp) { 7517c478bd9Sstevel@tonic-gate if (do_lock) { 7527c478bd9Sstevel@tonic-gate rw_enter(&ufsvfsp->vfs_dqrwlock, RW_READER); 7537c478bd9Sstevel@tonic-gate } 7547c478bd9Sstevel@tonic-gate 7557c478bd9Sstevel@tonic-gate /* 7567c478bd9Sstevel@tonic-gate * Quotas are not enabled on this file system so bail. 7577c478bd9Sstevel@tonic-gate */ 7587c478bd9Sstevel@tonic-gate if ((ufsvfsp->vfs_qflags & MQ_ENABLED) == 0) { 7597c478bd9Sstevel@tonic-gate if (do_lock) { 7607c478bd9Sstevel@tonic-gate rw_exit(&ufsvfsp->vfs_dqrwlock); 7617c478bd9Sstevel@tonic-gate } 7627c478bd9Sstevel@tonic-gate return (ESRCH); 7637c478bd9Sstevel@tonic-gate } 7647c478bd9Sstevel@tonic-gate 7657c478bd9Sstevel@tonic-gate /* 7667c478bd9Sstevel@tonic-gate * This operation is a no-op on a logging file system because 7677c478bd9Sstevel@tonic-gate * quota information is treated as metadata and is in the log. 7687c478bd9Sstevel@tonic-gate * This code path treats quota information as user data which 7697c478bd9Sstevel@tonic-gate * is not necessary on a logging file system. 7707c478bd9Sstevel@tonic-gate */ 7717c478bd9Sstevel@tonic-gate if (TRANS_ISTRANS(ufsvfsp)) { 7727c478bd9Sstevel@tonic-gate if (do_lock) { 7737c478bd9Sstevel@tonic-gate rw_exit(&ufsvfsp->vfs_dqrwlock); 7747c478bd9Sstevel@tonic-gate } 7757c478bd9Sstevel@tonic-gate return (0); 7767c478bd9Sstevel@tonic-gate } 7777c478bd9Sstevel@tonic-gate 7787c478bd9Sstevel@tonic-gate /* 7797c478bd9Sstevel@tonic-gate * Try to sync all the quota info records for this 7807c478bd9Sstevel@tonic-gate * file system: 7817c478bd9Sstevel@tonic-gate */ 7827c478bd9Sstevel@tonic-gate for (dqp = dquot; dqp < dquotNDQUOT; dqp++) { 7837c478bd9Sstevel@tonic-gate /* 7847c478bd9Sstevel@tonic-gate * If someone else has it, then ignore it. 7857c478bd9Sstevel@tonic-gate */ 7867c478bd9Sstevel@tonic-gate if (!mutex_tryenter(&dqp->dq_lock)) { 7877c478bd9Sstevel@tonic-gate continue; 7887c478bd9Sstevel@tonic-gate } 7897c478bd9Sstevel@tonic-gate 7907c478bd9Sstevel@tonic-gate /* 7917c478bd9Sstevel@tonic-gate * The quota info record is for this file system 7927c478bd9Sstevel@tonic-gate * and it has changes. 7937c478bd9Sstevel@tonic-gate */ 7947c478bd9Sstevel@tonic-gate if (dqp->dq_ufsvfsp == ufsvfsp && 7957c478bd9Sstevel@tonic-gate (dqp->dq_flags & DQ_MOD)) { 7967c478bd9Sstevel@tonic-gate ASSERT(ufsvfsp->vfs_qflags & MQ_ENABLED); 7977c478bd9Sstevel@tonic-gate dqupdate(dqp); 7987c478bd9Sstevel@tonic-gate } 7997c478bd9Sstevel@tonic-gate 8007c478bd9Sstevel@tonic-gate mutex_exit(&dqp->dq_lock); 8017c478bd9Sstevel@tonic-gate } 8027c478bd9Sstevel@tonic-gate if (do_lock) { 8037c478bd9Sstevel@tonic-gate rw_exit(&ufsvfsp->vfs_dqrwlock); 8047c478bd9Sstevel@tonic-gate } 8057c478bd9Sstevel@tonic-gate 8067c478bd9Sstevel@tonic-gate return (0); 8077c478bd9Sstevel@tonic-gate } 8087c478bd9Sstevel@tonic-gate 8097c478bd9Sstevel@tonic-gate /* 8107c478bd9Sstevel@tonic-gate * Try to sync all the quota info records for *all* file systems 8117c478bd9Sstevel@tonic-gate * for which quotas are enabled. 8127c478bd9Sstevel@tonic-gate */ 8137c478bd9Sstevel@tonic-gate for (dqp = dquot; dqp < dquotNDQUOT; dqp++) { 8147c478bd9Sstevel@tonic-gate /* 8157c478bd9Sstevel@tonic-gate * If someone else has it, then ignore it. 8167c478bd9Sstevel@tonic-gate */ 8177c478bd9Sstevel@tonic-gate if (!mutex_tryenter(&dqp->dq_lock)) { 8187c478bd9Sstevel@tonic-gate continue; 8197c478bd9Sstevel@tonic-gate } 8207c478bd9Sstevel@tonic-gate 8217c478bd9Sstevel@tonic-gate ufsvfsp = dqp->dq_ufsvfsp; /* shorthand */ 8227c478bd9Sstevel@tonic-gate 8237c478bd9Sstevel@tonic-gate /* 8247c478bd9Sstevel@tonic-gate * This quota info record has no changes or is 8257c478bd9Sstevel@tonic-gate * not a valid quota info record yet. 8267c478bd9Sstevel@tonic-gate */ 8277c478bd9Sstevel@tonic-gate if ((dqp->dq_flags & DQ_MOD) == 0 || ufsvfsp == NULL) { 8287c478bd9Sstevel@tonic-gate mutex_exit(&dqp->dq_lock); 8297c478bd9Sstevel@tonic-gate continue; 8307c478bd9Sstevel@tonic-gate } 8317c478bd9Sstevel@tonic-gate 8327c478bd9Sstevel@tonic-gate /* 8337c478bd9Sstevel@tonic-gate * Now we have a potential lock order problem: 8347c478bd9Sstevel@tonic-gate * 8357c478bd9Sstevel@tonic-gate * vfs_dqrwlock > dq_lock 8367c478bd9Sstevel@tonic-gate * 8377c478bd9Sstevel@tonic-gate * so if we have to get vfs_dqrwlock, then go thru hoops 8387c478bd9Sstevel@tonic-gate * to avoid deadlock. If we cannot get the order right, 8397c478bd9Sstevel@tonic-gate * then we ignore this quota info record. 8407c478bd9Sstevel@tonic-gate */ 8417c478bd9Sstevel@tonic-gate if (do_lock) { 8427c478bd9Sstevel@tonic-gate /* 8437c478bd9Sstevel@tonic-gate * If we can't grab vfs_dqrwlock, then we don't 8447c478bd9Sstevel@tonic-gate * want to wait to avoid deadlock. 8457c478bd9Sstevel@tonic-gate */ 8467c478bd9Sstevel@tonic-gate if (rw_tryenter(&ufsvfsp->vfs_dqrwlock, 8477c478bd9Sstevel@tonic-gate RW_READER) == 0) { 8487c478bd9Sstevel@tonic-gate mutex_exit(&dqp->dq_lock); 8497c478bd9Sstevel@tonic-gate continue; 8507c478bd9Sstevel@tonic-gate } 8517c478bd9Sstevel@tonic-gate /* 8527c478bd9Sstevel@tonic-gate * Okay, now we have both dq_lock and vfs_dqrwlock. 8537c478bd9Sstevel@tonic-gate * We should not deadlock for the following reasons: 8547c478bd9Sstevel@tonic-gate * - If another thread has a reader lock on 8557c478bd9Sstevel@tonic-gate * vfs_dqrwlock and is waiting for dq_lock, 8567c478bd9Sstevel@tonic-gate * there is no conflict because we can also have 8577c478bd9Sstevel@tonic-gate * a reader lock on vfs_dqrwlock. 8587c478bd9Sstevel@tonic-gate * - If another thread has a writer lock on 8597c478bd9Sstevel@tonic-gate * vfs_dqrwlock and is waiting for dq_lock, 8607c478bd9Sstevel@tonic-gate * we would have failed the rw_tryenter() above 8617c478bd9Sstevel@tonic-gate * and given up dq_lock. 8627c478bd9Sstevel@tonic-gate * - Since we have dq_lock another thread cannot 8637c478bd9Sstevel@tonic-gate * have it and be waiting for vfs_dqrwlock. 8647c478bd9Sstevel@tonic-gate */ 8657c478bd9Sstevel@tonic-gate } 8667c478bd9Sstevel@tonic-gate 8677c478bd9Sstevel@tonic-gate /* 8687c478bd9Sstevel@tonic-gate * Since we got to this file system via a quota info 8697c478bd9Sstevel@tonic-gate * record and we have vfs_dqrwlock this is paranoia 8707c478bd9Sstevel@tonic-gate * to make sure that quotas are enabled. 8717c478bd9Sstevel@tonic-gate */ 8727c478bd9Sstevel@tonic-gate ASSERT(ufsvfsp->vfs_qflags & MQ_ENABLED); 8737c478bd9Sstevel@tonic-gate 8747c478bd9Sstevel@tonic-gate /* 8757c478bd9Sstevel@tonic-gate * We are not logging. See above logging file system 8767c478bd9Sstevel@tonic-gate * comment. 8777c478bd9Sstevel@tonic-gate */ 8787c478bd9Sstevel@tonic-gate if (!TRANS_ISTRANS(ufsvfsp)) { 8797c478bd9Sstevel@tonic-gate dqupdate(dqp); 8807c478bd9Sstevel@tonic-gate } 8817c478bd9Sstevel@tonic-gate 8827c478bd9Sstevel@tonic-gate /* 8837c478bd9Sstevel@tonic-gate * Since we have a private copy of dqp->dq_ufsvfsp, 8847c478bd9Sstevel@tonic-gate * we can drop dq_lock now. 8857c478bd9Sstevel@tonic-gate */ 8867c478bd9Sstevel@tonic-gate mutex_exit(&dqp->dq_lock); 8877c478bd9Sstevel@tonic-gate 8887c478bd9Sstevel@tonic-gate if (do_lock) { 8897c478bd9Sstevel@tonic-gate rw_exit(&ufsvfsp->vfs_dqrwlock); 8907c478bd9Sstevel@tonic-gate } 8917c478bd9Sstevel@tonic-gate } 8927c478bd9Sstevel@tonic-gate 8937c478bd9Sstevel@tonic-gate return (0); 8947c478bd9Sstevel@tonic-gate } 895