1/* 2 * Copyright (c) 2000-2006 Silicon Graphics, Inc. 3 * All Rights Reserved. 4 * 5 * This program is free software; you can redistribute it and/or 6 * modify it under the terms of the GNU General Public License as 7 * published by the Free Software Foundation. 8 * 9 * This program is distributed in the hope that it would be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with this program; if not, write the Free Software Foundation, 16 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 17 */ 18#include "xfs.h" 19#include "xfs_fs.h" 20#include "xfs_bit.h" 21#include "xfs_log.h" 22#include "xfs_inum.h" 23#include "xfs_clnt.h" 24#include "xfs_trans.h" 25#include "xfs_sb.h" 26#include "xfs_ag.h" 27#include "xfs_dir2.h" 28#include "xfs_alloc.h" 29#include "xfs_dmapi.h" 30#include "xfs_quota.h" 31#include "xfs_mount.h" 32#include "xfs_bmap_btree.h" 33#include "xfs_alloc_btree.h" 34#include "xfs_ialloc_btree.h" 35#include "xfs_dir2_sf.h" 36#include "xfs_attr_sf.h" 37#include "xfs_dinode.h" 38#include "xfs_inode.h" 39#include "xfs_ialloc.h" 40#include "xfs_itable.h" 41#include "xfs_btree.h" 42#include "xfs_bmap.h" 43#include "xfs_rtalloc.h" 44#include "xfs_error.h" 45#include "xfs_rw.h" 46#include "xfs_acl.h" 47#include "xfs_attr.h" 48#include "xfs_buf_item.h" 49#include "xfs_qm.h" 50 51#define MNTOPT_QUOTA "quota" /* disk quotas (user) */ 52#define MNTOPT_NOQUOTA "noquota" /* no quotas */ 53#define MNTOPT_USRQUOTA "usrquota" /* user quota enabled */ 54#define MNTOPT_GRPQUOTA "grpquota" /* group quota enabled */ 55#define MNTOPT_PRJQUOTA "prjquota" /* project quota enabled */ 56#define MNTOPT_UQUOTA "uquota" /* user quota (IRIX variant) */ 57#define MNTOPT_GQUOTA "gquota" /* group quota (IRIX variant) */ 58#define MNTOPT_PQUOTA "pquota" /* project quota (IRIX variant) */ 59#define MNTOPT_UQUOTANOENF "uqnoenforce"/* user quota limit enforcement */ 60#define MNTOPT_GQUOTANOENF "gqnoenforce"/* group quota limit enforcement */ 61#define MNTOPT_PQUOTANOENF "pqnoenforce"/* project quota limit enforcement */ 62#define MNTOPT_QUOTANOENF "qnoenforce" /* same as uqnoenforce */ 63 64STATIC int 65xfs_qm_parseargs( 66 struct bhv_desc *bhv, 67 char *options, 68 struct xfs_mount_args *args, 69 int update) 70{ 71 size_t length; 72 char *local_options = options; 73 char *this_char; 74 int error; 75 int referenced = update; 76 77 while ((this_char = strsep(&local_options, ",")) != NULL) { 78 length = strlen(this_char); 79 if (local_options) 80 length++; 81 82 if (!strcmp(this_char, MNTOPT_NOQUOTA)) { 83 args->flags &= ~(XFSMNT_UQUOTAENF|XFSMNT_UQUOTA); 84 args->flags &= ~(XFSMNT_GQUOTAENF|XFSMNT_GQUOTA); 85 referenced = update; 86 } else if (!strcmp(this_char, MNTOPT_QUOTA) || 87 !strcmp(this_char, MNTOPT_UQUOTA) || 88 !strcmp(this_char, MNTOPT_USRQUOTA)) { 89 args->flags |= XFSMNT_UQUOTA | XFSMNT_UQUOTAENF; 90 referenced = 1; 91 } else if (!strcmp(this_char, MNTOPT_QUOTANOENF) || 92 !strcmp(this_char, MNTOPT_UQUOTANOENF)) { 93 args->flags |= XFSMNT_UQUOTA; 94 args->flags &= ~XFSMNT_UQUOTAENF; 95 referenced = 1; 96 } else if (!strcmp(this_char, MNTOPT_PQUOTA) || 97 !strcmp(this_char, MNTOPT_PRJQUOTA)) { 98 args->flags |= XFSMNT_PQUOTA | XFSMNT_PQUOTAENF; 99 referenced = 1; 100 } else if (!strcmp(this_char, MNTOPT_PQUOTANOENF)) { 101 args->flags |= XFSMNT_PQUOTA; 102 args->flags &= ~XFSMNT_PQUOTAENF; 103 referenced = 1; 104 } else if (!strcmp(this_char, MNTOPT_GQUOTA) || 105 !strcmp(this_char, MNTOPT_GRPQUOTA)) { 106 args->flags |= XFSMNT_GQUOTA | XFSMNT_GQUOTAENF; 107 referenced = 1; 108 } else if (!strcmp(this_char, MNTOPT_GQUOTANOENF)) { 109 args->flags |= XFSMNT_GQUOTA; 110 args->flags &= ~XFSMNT_GQUOTAENF; 111 referenced = 1; 112 } else { 113 if (local_options) 114 *(local_options-1) = ','; 115 continue; 116 } 117 118 while (length--) 119 *this_char++ = ','; 120 } 121 122 if ((args->flags & XFSMNT_GQUOTA) && (args->flags & XFSMNT_PQUOTA)) { 123 cmn_err(CE_WARN, 124 "XFS: cannot mount with both project and group quota"); 125 return XFS_ERROR(EINVAL); 126 } 127 128 error = bhv_next_vfs_parseargs(BHV_NEXT(bhv), options, args, update); 129 if (!error && !referenced) 130 bhv_remove_vfsops(bhvtovfs(bhv), VFS_POSITION_QM); 131 return error; 132} 133 134STATIC int 135xfs_qm_showargs( 136 struct bhv_desc *bhv, 137 struct seq_file *m) 138{ 139 struct bhv_vfs *vfsp = bhvtovfs(bhv); 140 struct xfs_mount *mp = XFS_VFSTOM(vfsp); 141 142 if (mp->m_qflags & XFS_UQUOTA_ACCT) { 143 (mp->m_qflags & XFS_UQUOTA_ENFD) ? 144 seq_puts(m, "," MNTOPT_USRQUOTA) : 145 seq_puts(m, "," MNTOPT_UQUOTANOENF); 146 } 147 148 if (mp->m_qflags & XFS_PQUOTA_ACCT) { 149 (mp->m_qflags & XFS_OQUOTA_ENFD) ? 150 seq_puts(m, "," MNTOPT_PRJQUOTA) : 151 seq_puts(m, "," MNTOPT_PQUOTANOENF); 152 } 153 154 if (mp->m_qflags & XFS_GQUOTA_ACCT) { 155 (mp->m_qflags & XFS_OQUOTA_ENFD) ? 156 seq_puts(m, "," MNTOPT_GRPQUOTA) : 157 seq_puts(m, "," MNTOPT_GQUOTANOENF); 158 } 159 160 if (!(mp->m_qflags & XFS_ALL_QUOTA_ACCT)) 161 seq_puts(m, "," MNTOPT_NOQUOTA); 162 163 return bhv_next_vfs_showargs(BHV_NEXT(bhv), m); 164} 165 166STATIC int 167xfs_qm_mount( 168 struct bhv_desc *bhv, 169 struct xfs_mount_args *args, 170 struct cred *cr) 171{ 172 struct bhv_vfs *vfsp = bhvtovfs(bhv); 173 struct xfs_mount *mp = XFS_VFSTOM(vfsp); 174 175 if (args->flags & (XFSMNT_UQUOTA | XFSMNT_GQUOTA | XFSMNT_PQUOTA)) 176 xfs_qm_mount_quotainit(mp, args->flags); 177 return bhv_next_vfs_mount(BHV_NEXT(bhv), args, cr); 178} 179 180/* 181 * Directory tree accounting is implemented using project quotas, where 182 * the project identifier is inherited from parent directories. 183 * A statvfs (df, etc.) of a directory that is using project quota should 184 * return a statvfs of the project, not the entire filesystem. 185 * This makes such trees appear as if they are filesystems in themselves. 186 */ 187STATIC int 188xfs_qm_statvfs( 189 struct bhv_desc *bhv, 190 bhv_statvfs_t *statp, 191 struct bhv_vnode *vnode) 192{ 193 xfs_mount_t *mp; 194 xfs_inode_t *ip; 195 xfs_dquot_t *dqp; 196 xfs_disk_dquot_t *dp; 197 __uint64_t limit; 198 int error; 199 200 error = bhv_next_vfs_statvfs(BHV_NEXT(bhv), statp, vnode); 201 if (error || !vnode) 202 return error; 203 204 mp = xfs_vfstom(bhvtovfs(bhv)); 205 ip = xfs_vtoi(vnode); 206 207 if (!(ip->i_d.di_flags & XFS_DIFLAG_PROJINHERIT)) 208 return 0; 209 if (!(mp->m_qflags & XFS_PQUOTA_ACCT)) 210 return 0; 211 if (!(mp->m_qflags & XFS_OQUOTA_ENFD)) 212 return 0; 213 214 if (xfs_qm_dqget(mp, NULL, ip->i_d.di_projid, XFS_DQ_PROJ, 0, &dqp)) 215 return 0; 216 dp = &dqp->q_core; 217 218 limit = dp->d_blk_softlimit ? 219 be64_to_cpu(dp->d_blk_softlimit) : 220 be64_to_cpu(dp->d_blk_hardlimit); 221 if (limit && statp->f_blocks > limit) { 222 statp->f_blocks = limit; 223 statp->f_bfree = 224 (statp->f_blocks > be64_to_cpu(dp->d_bcount)) ? 225 (statp->f_blocks - be64_to_cpu(dp->d_bcount)) : 0; 226 } 227 228 limit = dp->d_ino_softlimit ? 229 be64_to_cpu(dp->d_ino_softlimit) : 230 be64_to_cpu(dp->d_ino_hardlimit); 231 if (limit && statp->f_files > limit) { 232 statp->f_files = limit; 233 statp->f_ffree = 234 (statp->f_files > be64_to_cpu(dp->d_icount)) ? 235 (statp->f_ffree - be64_to_cpu(dp->d_icount)) : 0; 236 } 237 238 xfs_qm_dqput(dqp); 239 return 0; 240} 241 242STATIC int 243xfs_qm_syncall( 244 struct bhv_desc *bhv, 245 int flags, 246 cred_t *credp) 247{ 248 struct bhv_vfs *vfsp = bhvtovfs(bhv); 249 struct xfs_mount *mp = XFS_VFSTOM(vfsp); 250 int error; 251 252 /* 253 * Get the Quota Manager to flush the dquots. 254 */ 255 if (XFS_IS_QUOTA_ON(mp)) { 256 if ((error = xfs_qm_sync(mp, flags))) { 257 /* 258 * If we got an IO error, we will be shutting down. 259 * So, there's nothing more for us to do here. 260 */ 261 ASSERT(error != EIO || XFS_FORCED_SHUTDOWN(mp)); 262 if (XFS_FORCED_SHUTDOWN(mp)) { 263 return XFS_ERROR(error); 264 } 265 } 266 } 267 return bhv_next_vfs_sync(BHV_NEXT(bhv), flags, credp); 268} 269 270STATIC int 271xfs_qm_newmount( 272 xfs_mount_t *mp, 273 uint *needquotamount, 274 uint *quotaflags) 275{ 276 uint quotaondisk; 277 uint uquotaondisk = 0, gquotaondisk = 0, pquotaondisk = 0; 278 279 *quotaflags = 0; 280 *needquotamount = B_FALSE; 281 282 quotaondisk = XFS_SB_VERSION_HASQUOTA(&mp->m_sb) && 283 (mp->m_sb.sb_qflags & XFS_ALL_QUOTA_ACCT); 284 285 if (quotaondisk) { 286 uquotaondisk = mp->m_sb.sb_qflags & XFS_UQUOTA_ACCT; 287 pquotaondisk = mp->m_sb.sb_qflags & XFS_PQUOTA_ACCT; 288 gquotaondisk = mp->m_sb.sb_qflags & XFS_GQUOTA_ACCT; 289 } 290 291 /* 292 * If the device itself is read-only, we can't allow 293 * the user to change the state of quota on the mount - 294 * this would generate a transaction on the ro device, 295 * which would lead to an I/O error and shutdown 296 */ 297 298 if (((uquotaondisk && !XFS_IS_UQUOTA_ON(mp)) || 299 (!uquotaondisk && XFS_IS_UQUOTA_ON(mp)) || 300 (pquotaondisk && !XFS_IS_PQUOTA_ON(mp)) || 301 (!pquotaondisk && XFS_IS_PQUOTA_ON(mp)) || 302 (gquotaondisk && !XFS_IS_GQUOTA_ON(mp)) || 303 (!gquotaondisk && XFS_IS_OQUOTA_ON(mp))) && 304 xfs_dev_is_read_only(mp, "changing quota state")) { 305 cmn_err(CE_WARN, 306 "XFS: please mount with%s%s%s%s.", 307 (!quotaondisk ? "out quota" : ""), 308 (uquotaondisk ? " usrquota" : ""), 309 (pquotaondisk ? " prjquota" : ""), 310 (gquotaondisk ? " grpquota" : "")); 311 return XFS_ERROR(EPERM); 312 } 313 314 if (XFS_IS_QUOTA_ON(mp) || quotaondisk) { 315 /* 316 * Call mount_quotas at this point only if we won't have to do 317 * a quotacheck. 318 */ 319 if (quotaondisk && !XFS_QM_NEED_QUOTACHECK(mp)) { 320 /* 321 * If an error occured, qm_mount_quotas code 322 * has already disabled quotas. So, just finish 323 * mounting, and get on with the boring life 324 * without disk quotas. 325 */ 326 xfs_qm_mount_quotas(mp, 0); 327 } else { 328 /* 329 * Clear the quota flags, but remember them. This 330 * is so that the quota code doesn't get invoked 331 * before we're ready. This can happen when an 332 * inode goes inactive and wants to free blocks, 333 * or via xfs_log_mount_finish. 334 */ 335 *needquotamount = B_TRUE; 336 *quotaflags = mp->m_qflags; 337 mp->m_qflags = 0; 338 } 339 } 340 341 return 0; 342} 343 344STATIC int 345xfs_qm_endmount( 346 xfs_mount_t *mp, 347 uint needquotamount, 348 uint quotaflags, 349 int mfsi_flags) 350{ 351 if (needquotamount) { 352 ASSERT(mp->m_qflags == 0); 353 mp->m_qflags = quotaflags; 354 xfs_qm_mount_quotas(mp, mfsi_flags); 355 } 356 357#if defined(DEBUG) && defined(XFS_LOUD_RECOVERY) 358 if (! (XFS_IS_QUOTA_ON(mp))) 359 xfs_fs_cmn_err(CE_NOTE, mp, "Disk quotas not turned on"); 360 else 361 xfs_fs_cmn_err(CE_NOTE, mp, "Disk quotas turned on"); 362#endif 363 364#ifdef QUOTADEBUG 365 if (XFS_IS_QUOTA_ON(mp) && xfs_qm_internalqcheck(mp)) 366 cmn_err(CE_WARN, "XFS: mount internalqcheck failed"); 367#endif 368 369 return 0; 370} 371 372STATIC void 373xfs_qm_dqrele_null( 374 xfs_dquot_t *dq) 375{ 376 /* 377 * Called from XFS, where we always check first for a NULL dquot. 378 */ 379 if (!dq) 380 return; 381 xfs_qm_dqrele(dq); 382} 383 384 385static struct xfs_qmops xfs_qmcore_xfs = { 386 .xfs_qminit = xfs_qm_newmount, 387 .xfs_qmdone = xfs_qm_unmount_quotadestroy, 388 .xfs_qmmount = xfs_qm_endmount, 389 .xfs_qmunmount = xfs_qm_unmount_quotas, 390 .xfs_dqrele = xfs_qm_dqrele_null, 391 .xfs_dqattach = xfs_qm_dqattach, 392 .xfs_dqdetach = xfs_qm_dqdetach, 393 .xfs_dqpurgeall = xfs_qm_dqpurge_all, 394 .xfs_dqvopalloc = xfs_qm_vop_dqalloc, 395 .xfs_dqvopcreate = xfs_qm_vop_dqattach_and_dqmod_newinode, 396 .xfs_dqvoprename = xfs_qm_vop_rename_dqattach, 397 .xfs_dqvopchown = xfs_qm_vop_chown, 398 .xfs_dqvopchownresv = xfs_qm_vop_chown_reserve, 399 .xfs_dqtrxops = &xfs_trans_dquot_ops, 400}; 401 402struct bhv_module_vfsops xfs_qmops = { { 403 BHV_IDENTITY_INIT(VFS_BHV_QM, VFS_POSITION_QM), 404 .vfs_parseargs = xfs_qm_parseargs, 405 .vfs_showargs = xfs_qm_showargs, 406 .vfs_mount = xfs_qm_mount, 407 .vfs_statvfs = xfs_qm_statvfs, 408 .vfs_sync = xfs_qm_syncall, 409 .vfs_quotactl = xfs_qm_quotactl, }, 410}; 411 412 413void __init 414xfs_qm_init(void) 415{ 416 static char message[] __initdata = 417 KERN_INFO "SGI XFS Quota Management subsystem\n"; 418 419 printk(message); 420 mutex_init(&xfs_Gqm_lock); 421 vfs_bhv_set_custom(&xfs_qmops, &xfs_qmcore_xfs); 422 xfs_qm_init_procfs(); 423} 424 425void __exit 426xfs_qm_exit(void) 427{ 428 vfs_bhv_clr_custom(&xfs_qmops); 429 xfs_qm_cleanup_procfs(); 430 if (qm_dqzone) 431 kmem_zone_destroy(qm_dqzone); 432 if (qm_dqtrxzone) 433 kmem_zone_destroy(qm_dqtrxzone); 434} 435