📄 dquot.c
字号:
inode->i_flags |= S_QUOTA; } /* NOBLOCK END */ /* Put quotas which we didn't use */ for (cnt = 0; cnt < MAXQUOTAS; cnt++) if (dquot[cnt] != NODQUOT) dqput(dquot[cnt]);}/* * Release all quota for the specified inode. * * Note: this is a blocking operation. */void dquot_drop(struct inode *inode){ struct dquot *dquot; short cnt; inode->i_flags &= ~S_QUOTA; for (cnt = 0; cnt < MAXQUOTAS; cnt++) { if (inode->i_dquot[cnt] == NODQUOT) continue; dquot = inode->i_dquot[cnt]; inode->i_dquot[cnt] = NODQUOT; dqput(dquot); }}/* * This operation can block, but only after everything is updated */int dquot_alloc_block(struct inode *inode, unsigned long number, char warn){ int cnt, ret = NO_QUOTA; struct dquot *dquot[MAXQUOTAS]; char warntype[MAXQUOTAS]; for (cnt = 0; cnt < MAXQUOTAS; cnt++) { dquot[cnt] = NODQUOT; warntype[cnt] = NOWARN; } /* NOBLOCK Start */ for (cnt = 0; cnt < MAXQUOTAS; cnt++) { dquot[cnt] = dqduplicate(inode->i_dquot[cnt]); if (dquot[cnt] == NODQUOT) continue; if (check_bdq(dquot[cnt], number, warn, warntype+cnt) == NO_QUOTA) goto warn_put_all; } for (cnt = 0; cnt < MAXQUOTAS; cnt++) { if (dquot[cnt] == NODQUOT) continue; dquot_incr_blocks(dquot[cnt], number); } inode->i_blocks += number << (BLOCK_SIZE_BITS - 9); /* NOBLOCK End */ ret = QUOTA_OK;warn_put_all: flush_warnings(dquot, warntype); for (cnt = 0; cnt < MAXQUOTAS; cnt++) if (dquot[cnt] != NODQUOT) dqput(dquot[cnt]); return ret;}/* * This operation can block, but only after everything is updated */int dquot_alloc_inode(const struct inode *inode, unsigned long number){ int cnt, ret = NO_QUOTA; struct dquot *dquot[MAXQUOTAS]; char warntype[MAXQUOTAS]; for (cnt = 0; cnt < MAXQUOTAS; cnt++) { dquot[cnt] = NODQUOT; warntype[cnt] = NOWARN; } /* NOBLOCK Start */ for (cnt = 0; cnt < MAXQUOTAS; cnt++) { dquot[cnt] = dqduplicate(inode -> i_dquot[cnt]); if (dquot[cnt] == NODQUOT) continue; if (check_idq(dquot[cnt], number, warntype+cnt) == NO_QUOTA) goto warn_put_all; } for (cnt = 0; cnt < MAXQUOTAS; cnt++) { if (dquot[cnt] == NODQUOT) continue; dquot_incr_inodes(dquot[cnt], number); } /* NOBLOCK End */ ret = QUOTA_OK;warn_put_all: flush_warnings(dquot, warntype); for (cnt = 0; cnt < MAXQUOTAS; cnt++) if (dquot[cnt] != NODQUOT) dqput(dquot[cnt]); return ret;}/* * This is a non-blocking operation. */void dquot_free_block(struct inode *inode, unsigned long number){ unsigned short cnt; struct dquot *dquot; /* NOBLOCK Start */ for (cnt = 0; cnt < MAXQUOTAS; cnt++) { dquot = dqduplicate(inode->i_dquot[cnt]); if (dquot == NODQUOT) continue; dquot_decr_blocks(dquot, number); dqput(dquot); } inode->i_blocks -= number << (BLOCK_SIZE_BITS - 9); /* NOBLOCK End */}/* * This is a non-blocking operation. */void dquot_free_inode(const struct inode *inode, unsigned long number){ unsigned short cnt; struct dquot *dquot; /* NOBLOCK Start */ for (cnt = 0; cnt < MAXQUOTAS; cnt++) { dquot = dqduplicate(inode->i_dquot[cnt]); if (dquot == NODQUOT) continue; dquot_decr_inodes(dquot, number); dqput(dquot); } /* NOBLOCK End */}/* * Transfer the number of inode and blocks from one diskquota to an other. * * This operation can block, but only after everything is updated */int dquot_transfer(struct inode *inode, struct iattr *iattr){ unsigned long blocks; struct dquot *transfer_from[MAXQUOTAS]; struct dquot *transfer_to[MAXQUOTAS]; int cnt, ret = NO_QUOTA, chuid = (iattr->ia_valid & ATTR_UID) && inode->i_uid != iattr->ia_uid, chgid = (iattr->ia_valid & ATTR_GID) && inode->i_gid != iattr->ia_gid; char warntype[MAXQUOTAS]; /* Clear the arrays */ for (cnt = 0; cnt < MAXQUOTAS; cnt++) { transfer_to[cnt] = transfer_from[cnt] = NODQUOT; warntype[cnt] = NOWARN; } /* First build the transfer_to list - here we can block on reading of dquots... */ for (cnt = 0; cnt < MAXQUOTAS; cnt++) { if (!sb_has_quota_enabled(inode->i_sb, cnt)) continue; switch (cnt) { case USRQUOTA: if (!chuid) continue; transfer_to[cnt] = dqget(inode->i_sb, iattr->ia_uid, cnt); break; case GRPQUOTA: if (!chgid) continue; transfer_to[cnt] = dqget(inode->i_sb, iattr->ia_gid, cnt); break; } } /* NOBLOCK START: From now on we shouldn't block */ blocks = (inode->i_blocks >> 1); /* Build the transfer_from list and check the limits */ for (cnt = 0; cnt < MAXQUOTAS; cnt++) { /* The second test can fail when quotaoff is in progress... */ if (transfer_to[cnt] == NODQUOT || !sb_has_quota_enabled(inode->i_sb, cnt)) continue; transfer_from[cnt] = dqduplicate(inode->i_dquot[cnt]); if (transfer_from[cnt] == NODQUOT) /* Can happen on quotafiles (quota isn't initialized on them)... */ continue; if (check_idq(transfer_to[cnt], 1, warntype+cnt) == NO_QUOTA || check_bdq(transfer_to[cnt], blocks, 0, warntype+cnt) == NO_QUOTA) goto warn_put_all; } /* * Finally perform the needed transfer from transfer_from to transfer_to */ for (cnt = 0; cnt < MAXQUOTAS; cnt++) { /* * Skip changes for same uid or gid or for non-existing quota-type. */ if (transfer_from[cnt] == NODQUOT || transfer_to[cnt] == NODQUOT) continue; dquot_decr_inodes(transfer_from[cnt], 1); dquot_decr_blocks(transfer_from[cnt], blocks); dquot_incr_inodes(transfer_to[cnt], 1); dquot_incr_blocks(transfer_to[cnt], blocks); if (inode->i_dquot[cnt] == NODQUOT) BUG(); inode->i_dquot[cnt] = transfer_to[cnt]; /* * We've got to release transfer_from[] twice - once for dquot_transfer() and * once for inode. We don't want to release transfer_to[] as it's now placed in inode */ transfer_to[cnt] = transfer_from[cnt]; } /* NOBLOCK END. From now on we can block as we wish */ ret = QUOTA_OK;warn_put_all: flush_warnings(transfer_to, warntype); for (cnt = 0; cnt < MAXQUOTAS; cnt++) { if (transfer_to[cnt] != NODQUOT) dqput(transfer_to[cnt]); if (transfer_from[cnt] != NODQUOT) dqput(transfer_from[cnt]); } return ret;}static int __init dquot_init(void){ int i; for (i = 0; i < NR_DQHASH; i++) INIT_LIST_HEAD(dquot_hash + i); printk(KERN_NOTICE "VFS: Diskquotas version %s initialized\n", __DQUOT_VERSION__); return 0;}__initcall(dquot_init);/* * Definitions of diskquota operations. */struct dquot_operations dquot_operations = { dquot_initialize, /* mandatory */ dquot_drop, /* mandatory */ dquot_alloc_block, dquot_alloc_inode, dquot_free_block, dquot_free_inode, dquot_transfer};static inline void set_enable_flags(struct quota_mount_options *dqopt, short type){ switch (type) { case USRQUOTA: dqopt->flags |= DQUOT_USR_ENABLED; break; case GRPQUOTA: dqopt->flags |= DQUOT_GRP_ENABLED; break; }}static inline void reset_enable_flags(struct quota_mount_options *dqopt, short type){ switch (type) { case USRQUOTA: dqopt->flags &= ~DQUOT_USR_ENABLED; break; case GRPQUOTA: dqopt->flags &= ~DQUOT_GRP_ENABLED; break; }}/* Function in inode.c - remove pointers to dquots in icache */extern void remove_dquot_ref(struct super_block *, short);/* * Turn quota off on a device. type == -1 ==> quotaoff for all types (umount) */int quota_off(struct super_block *sb, short type){ struct file *filp; short cnt; struct quota_mount_options *dqopt = sb_dqopt(sb); lock_kernel(); if (!sb) goto out; /* We need to serialize quota_off() for device */ down(&dqopt->dqoff_sem); for (cnt = 0; cnt < MAXQUOTAS; cnt++) { if (type != -1 && cnt != type) continue; if (!is_enabled(dqopt, cnt)) continue; reset_enable_flags(dqopt, cnt); /* Note: these are blocking operations */ remove_dquot_ref(sb, cnt); invalidate_dquots(sb, cnt); filp = dqopt->files[cnt]; dqopt->files[cnt] = (struct file *)NULL; dqopt->inode_expire[cnt] = 0; dqopt->block_expire[cnt] = 0; fput(filp); } up(&dqopt->dqoff_sem);out: unlock_kernel(); return 0;}static inline int check_quotafile_size(loff_t size){ ulong blocks = size >> BLOCK_SIZE_BITS; size_t off = size & (BLOCK_SIZE - 1); return !(((blocks % sizeof(struct dqblk)) * BLOCK_SIZE + off % sizeof(struct dqblk)) % sizeof(struct dqblk));}static int quota_on(struct super_block *sb, short type, char *path){ struct file *f; struct inode *inode; struct dquot *dquot; struct quota_mount_options *dqopt = sb_dqopt(sb); char *tmp; int error; if (is_enabled(dqopt, type)) return -EBUSY; down(&dqopt->dqoff_sem); tmp = getname(path); error = PTR_ERR(tmp); if (IS_ERR(tmp)) goto out_lock; f = filp_open(tmp, O_RDWR, 0600); putname(tmp); error = PTR_ERR(f); if (IS_ERR(f)) goto out_lock; error = -EIO; if (!f->f_op || !f->f_op->read || !f->f_op->write) goto out_f; inode = f->f_dentry->d_inode; error = -EACCES; if (!S_ISREG(inode->i_mode)) goto out_f; error = -EINVAL; if (inode->i_size == 0 || !check_quotafile_size(inode->i_size)) goto out_f; /* We don't want quota on quota files */ dquot_drop(inode); inode->i_flags |= S_NOQUOTA; dqopt->files[type] = f; sb->dq_op = &dquot_operations; set_enable_flags(dqopt, type); dquot = dqget(sb, 0, type); dqopt->inode_expire[type] = (dquot != NODQUOT) ? dquot->dq_itime : MAX_IQ_TIME; dqopt->block_expire[type] = (dquot != NODQUOT) ? dquot->dq_btime : MAX_DQ_TIME; dqput(dquot); add_dquot_ref(sb, type); up(&dqopt->dqoff_sem); return 0;out_f: filp_close(f, NULL);out_lock: up(&dqopt->dqoff_sem); return error; }/* * This is the system call interface. This communicates with * the user-level programs. Currently this only supports diskquota * calls. Maybe we need to add the process quotas etc. in the future, * but we probably should use rlimits for that. */asmlinkage long sys_quotactl(int cmd, const char *special, int id, caddr_t addr){ int cmds = 0, type = 0, flags = 0; kdev_t dev; struct super_block *sb = NULL; int ret = -EINVAL; lock_kernel(); cmds = cmd >> SUBCMDSHIFT; type = cmd & SUBCMDMASK; if ((u_int) type >= MAXQUOTAS) goto out; if (id & ~0xFFFF) goto out; ret = -EPERM; switch (cmds) { case Q_SYNC: case Q_GETSTATS: break; case Q_GETQUOTA: if (((type == USRQUOTA && current->euid != id) || (type == GRPQUOTA && !in_egroup_p(id))) && !capable(CAP_SYS_ADMIN)) goto out; break; default: if (!capable(CAP_SYS_ADMIN)) goto out; } ret = -EINVAL; dev = NODEV; if (special != NULL || (cmds != Q_SYNC && cmds != Q_GETSTATS)) { mode_t mode; struct nameidata nd; ret = user_path_walk(special, &nd); if (ret) goto out; dev = nd.dentry->d_inode->i_rdev; mode = nd.dentry->d_inode->i_mode; path_release(&nd); ret = -ENOTBLK; if (!S_ISBLK(mode)) goto out; ret = -ENODEV; sb = get_super(dev); if (!sb) goto out; } ret = -EINVAL; switch (cmds) { case Q_QUOTAON: ret = quota_on(sb, type, (char *) addr); goto out; case Q_QUOTAOFF: ret = quota_off(sb, type); goto out; case Q_GETQUOTA: ret = get_quota(sb, id, type, (struct dqblk *) addr); goto out; case Q_SETQUOTA: flags |= SET_QUOTA; break; case Q_SETUSE: flags |= SET_USE; break; case Q_SETQLIM: flags |= SET_QLIMIT; break; case Q_SYNC: ret = sync_dquots(dev, type); goto out; case Q_GETSTATS: ret = get_stats(addr); goto out; case Q_RSQUASH: ret = quota_root_squash(sb, type, (int *) addr); goto out; default: goto out; } ret = -NODEV; if (sb && sb_has_quota_enabled(sb, type)) ret = set_dqblk(sb, id, type, flags, (struct dqblk *) addr);out: if (sb) drop_super(sb); unlock_kernel(); return ret;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -