📄 dquot.c
字号:
{ struct dquot *dquot, *empty; struct vfsmount *vfsmnt; if ((vfsmnt = lookup_vfsmnt(dev)) == (struct vfsmount *)NULL || (vfsmnt->mnt_quotas[type] == (struct file *)0)) return(NODQUOT); dqstats.lookups++; empty = get_empty_dquot();repeat: dquot = *(hash(dev, id, type)); while (dquot) { if (dquot->dq_dev != dev || dquot->dq_id != id || dquot->dq_type != type) { dquot = dquot->dq_hash_next; continue; } wait_on_dquot(dquot); if (dquot->dq_dev != dev || dquot->dq_id != id || dquot->dq_type != type) goto repeat; if (!dquot->dq_count) nr_free_dquots--; dquot->dq_count++; if (empty) dqput(empty); dqstats.cache_hits++; return(dquot); } if (!empty) return(NODQUOT); dquot = empty; dquot->dq_id = id; dquot->dq_type = type; dquot->dq_dev = dev; dquot->dq_mnt = vfsmnt; put_last_free(dquot); insert_dquot_hash(dquot); read_dquot(dquot); return(dquot);}/* * Initialize a dquot-struct with new quota info. This is used by the * systemcall interface functions. */ static int set_dqblk(kdev_t dev, int id, short type, int flags, struct dqblk *dqblk){ struct dquot *dquot; struct dqblk dq_dqblk; int error; if (dqblk == (struct dqblk *)NULL) return(-EFAULT); if (flags & QUOTA_SYSCALL) { if ((error = verify_area(VERIFY_READ, dqblk, sizeof(struct dqblk))) != 0) return(error); memcpy_fromfs(&dq_dqblk, dqblk, sizeof(struct dqblk)); } else { memcpy(&dq_dqblk, dqblk, sizeof(struct dqblk)); } if ((dquot = dqget(dev, id, type)) != NODQUOT) { lock_dquot(dquot); if (id > 0 && ((flags & SET_QUOTA) || (flags & SET_QLIMIT))) { dquot->dq_bhardlimit = dq_dqblk.dqb_bhardlimit; dquot->dq_bsoftlimit = dq_dqblk.dqb_bsoftlimit; dquot->dq_ihardlimit = dq_dqblk.dqb_ihardlimit; dquot->dq_isoftlimit = dq_dqblk.dqb_isoftlimit; } if ((flags & SET_QUOTA) || (flags & SET_USE)) { if (dquot->dq_isoftlimit && dquot->dq_curinodes < dquot->dq_isoftlimit && dq_dqblk.dqb_curinodes >= dquot->dq_isoftlimit) dquot->dq_itime = CURRENT_TIME + dquot->dq_mnt->mnt_iexp[type]; dquot->dq_curinodes = dq_dqblk.dqb_curinodes; if (dquot->dq_curinodes < dquot->dq_isoftlimit) dquot->dq_flags &= ~DQ_INODES; if (dquot->dq_bsoftlimit && dquot->dq_curblocks < dquot->dq_bsoftlimit && dq_dqblk.dqb_curblocks >= dquot->dq_bsoftlimit) dquot->dq_btime = CURRENT_TIME + dquot->dq_mnt->mnt_bexp[type]; dquot->dq_curblocks = dq_dqblk.dqb_curblocks; if (dquot->dq_curblocks < dquot->dq_bsoftlimit) dquot->dq_flags &= ~DQ_BLKS; } if (id == 0) { /* * Change in expiretimes, change them in dq_mnt. */ dquot->dq_mnt->mnt_bexp[type] = dquot->dq_btime = dq_dqblk.dqb_btime; dquot->dq_mnt->mnt_iexp[type] = dquot->dq_itime = dq_dqblk.dqb_itime; } if (dq_dqblk.dqb_bhardlimit == 0 && dq_dqblk.dqb_bsoftlimit == 0 && dq_dqblk.dqb_ihardlimit == 0 && dq_dqblk.dqb_isoftlimit == 0) dquot->dq_flags |= DQ_FAKE; else dquot->dq_flags &= ~DQ_FAKE; dquot->dq_flags |= DQ_MOD; unlock_dquot(dquot); dqput(dquot); } return(0);}static int get_quota(kdev_t dev, int id, short type, struct dqblk *dqblk){ struct dquot *dquot; int error; if (has_quota_enabled(dev, type)) { if (dqblk == (struct dqblk *)NULL) return(-EFAULT); if ((error = verify_area(VERIFY_WRITE, dqblk, sizeof(struct dqblk))) != 0) return(error); if ((dquot = dqget(dev, id, type)) != NODQUOT) { memcpy_tofs(dqblk, (char *)&dquot->dq_dqb, sizeof(struct dqblk)); dqput(dquot); return(0); } } return(-ESRCH);}static int get_stats(caddr_t addr){ int error; if ((error = verify_area(VERIFY_WRITE, addr, sizeof(struct dqstats))) != 0) return(error); dqstats.allocated_dquots = nr_dquots; dqstats.free_dquots = nr_free_dquots; memcpy_tofs(addr, (caddr_t)&dqstats, sizeof(struct dqstats)); return(0);}/* * Initialize pointer in a inode to the right dquots. */void dquot_initialize(struct inode *inode, short type){ unsigned int id = 0; short cnt; struct dquot *tmp; if (S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode)) { for (cnt = 0; cnt < MAXQUOTAS; cnt++) { if (type != -1 && cnt != type) continue; if (!has_quota_enabled(inode->i_dev, cnt)) continue; if (inode->i_dquot[cnt] == NODQUOT) { switch (cnt) { case USRQUOTA: id = inode->i_uid; break; case GRPQUOTA: id = inode->i_gid; break; } tmp = dqget(inode->i_dev, id, cnt); /* We may sleep in dqget(), so check it again. * Dmitry Gorodchanin 02/11/96 */ if (inode->i_dquot[cnt] != NODQUOT) { dqput(tmp); continue; } inode->i_dquot[cnt] = tmp; inode->i_flags |= S_WRITE; } } }}void dquot_drop(struct inode *inode){ short cnt; struct dquot * tmp; for (cnt = 0; cnt < MAXQUOTAS; cnt++) { if (inode->i_dquot[cnt] == NODQUOT) continue; /* We can sleep at dqput(). So we must do it this way. * Dmitry Gorodchanin 02/11/96 */ tmp = inode->i_dquot[cnt]; inode->i_dquot[cnt] = NODQUOT; dqput(tmp); } inode->i_flags &= ~S_WRITE;}/* * This is a simple algorithm that calculates the size of a file in blocks. * This is only used on filesystems that do not have a i_blocks count. */static u_long isize_to_blocks(size_t isize, size_t blksize){ u_long blocks; u_long indirect; if (!blksize) blksize = BLOCK_SIZE; blocks = (isize / blksize) + ((isize % blksize) ? 1 : 0); if (blocks > 10) { indirect = ((blocks - 11) >> 8) + 1; /* single indirect blocks */ if (blocks > (10 + 256)) { indirect += ((blocks - 267) >> 16) + 1; /* double indirect blocks */ if (blocks > (10 + 256 + (256 << 8))) indirect++; /* triple indirect blocks */ } blocks += indirect; } return(blocks);}/* * Externally referenced functions through dquot_operations. */int dquot_alloc_block(const struct inode *inode, unsigned long number){ unsigned short cnt; for (cnt = 0; cnt < MAXQUOTAS; cnt++) { if (inode->i_dquot[cnt] == NODQUOT) continue; if (check_bdq(inode->i_dquot[cnt], cnt, number)) return(NO_QUOTA); } for (cnt = 0; cnt < MAXQUOTAS; cnt++) { if (inode->i_dquot[cnt] == NODQUOT) continue; dquot_incr_blocks(inode->i_dquot[cnt], number); } return(QUOTA_OK);}int dquot_alloc_inode(const struct inode *inode, unsigned long number){ unsigned short cnt; for (cnt = 0; cnt < MAXQUOTAS; cnt++) { if (inode->i_dquot[cnt] == NODQUOT) continue; if (check_idq(inode->i_dquot[cnt], cnt, number)) return(NO_QUOTA); } for (cnt = 0; cnt < MAXQUOTAS; cnt++) { if (inode->i_dquot[cnt] == NODQUOT) continue; dquot_incr_inodes(inode->i_dquot[cnt], number); } return(QUOTA_OK);}void dquot_free_block(const struct inode *inode, unsigned long number){ unsigned short cnt; for (cnt = 0; cnt < MAXQUOTAS; cnt++) { if (inode->i_dquot[cnt] == NODQUOT) continue; dquot_decr_blocks(inode->i_dquot[cnt], number); }}void dquot_free_inode(const struct inode *inode, unsigned long number){ unsigned short cnt; for (cnt = 0; cnt < MAXQUOTAS; cnt++) { if (inode->i_dquot[cnt] == NODQUOT) continue; dquot_decr_inodes(inode->i_dquot[cnt], number); }}/* * Transfer the number of inode and blocks from one diskquota to an other. */int dquot_transfer(struct inode *inode, struct iattr *iattr, char direction){ unsigned long blocks; struct dquot *transfer_from[MAXQUOTAS]; struct dquot *transfer_to[MAXQUOTAS]; short cnt, disc; /* * Find out if this filesystems uses i_blocks. */ if (inode->i_blksize == 0) blocks = isize_to_blocks(inode->i_size, BLOCK_SIZE); else blocks = (inode->i_blocks / 2); /* * Build the transfer_from and transfer_to lists and check quotas to see * if operation is permitted. */ for (cnt = 0; cnt < MAXQUOTAS; cnt++) { transfer_from[cnt] = NODQUOT; transfer_to[cnt] = NODQUOT; if (!has_quota_enabled(inode->i_dev, cnt)) continue; switch (cnt) { case USRQUOTA: if (inode->i_uid == iattr->ia_uid) continue; transfer_from[cnt] = dqget(inode->i_dev, (direction) ? iattr->ia_uid : inode->i_uid, cnt); transfer_to[cnt] = dqget(inode->i_dev, (direction) ? inode->i_uid : iattr->ia_uid, cnt); break; case GRPQUOTA: if (inode->i_gid == iattr->ia_gid) continue; transfer_from[cnt] = dqget(inode->i_dev, (direction) ? iattr->ia_gid : inode->i_gid, cnt); transfer_to[cnt] = dqget(inode->i_dev, (direction) ? inode->i_gid : iattr->ia_gid, cnt); break; } if (check_idq(transfer_to[cnt], cnt, 1) == NO_QUOTA || check_bdq(transfer_to[cnt], cnt, blocks) == NO_QUOTA) { for (disc = 0; disc <= cnt; disc++) { dqput(transfer_from[disc]); dqput(transfer_to[disc]); } return(NO_QUOTA); } } /* * Finally perform the needed transfer from transfer_from to transfer_to. * And release any pointer to dquots not needed anymore. */ 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; if (transfer_from[cnt] != NODQUOT) { dquot_decr_inodes(transfer_from[cnt], 1); dquot_decr_blocks(transfer_from[cnt], blocks); } if (transfer_to[cnt] != NODQUOT) { dquot_incr_inodes(transfer_to[cnt], 1); dquot_incr_blocks(transfer_to[cnt], blocks); } if (inode->i_dquot[cnt] != NODQUOT) { dqput(transfer_from[cnt]); dqput(inode->i_dquot[cnt]); inode->i_dquot[cnt] = transfer_to[cnt]; } else { dqput(transfer_from[cnt]); dqput(transfer_to[cnt]); } } return(QUOTA_OK);}void dquot_init(void){ printk(KERN_NOTICE "VFS: Diskquotas version %s initialized\r\n", __DQUOT_VERSION__); memset(hash_table, 0, sizeof(hash_table)); memset((caddr_t)&dqstats, 0, sizeof(dqstats)); first_dquot = NODQUOT;}/* * Definitions of diskquota operations. */struct dquot_operations dquot_operations = { dquot_initialize, dquot_drop, dquot_alloc_block, dquot_alloc_inode, dquot_free_block, dquot_free_inode, dquot_transfer};/* * Turn quota off on a device. type == -1 ==> quotaoff for all types (umount) */int quota_off(kdev_t dev, short type){ struct vfsmount *vfsmnt; short cnt; for (cnt = 0; cnt < MAXQUOTAS; cnt++) { if (type != -1 && cnt != type) continue; if ((vfsmnt = lookup_vfsmnt(dev)) == (struct vfsmount *)NULL || vfsmnt->mnt_quotas[cnt] == (struct file *)NULL) continue; vfsmnt->mnt_sb->dq_op = (struct dquot_operations *)NULL; reset_dquot_ptrs(dev, cnt); invalidate_dquots(dev, cnt); close_fp(vfsmnt->mnt_quotas[cnt]); vfsmnt->mnt_quotas[cnt] = (struct file *)NULL; vfsmnt->mnt_iexp[cnt] = vfsmnt->mnt_bexp[cnt] = (time_t)NULL; } return(0);}int quota_on(kdev_t dev, short type, char *path){ struct file *filp = (struct file *)NULL; struct vfsmount *vfsmnt; struct inode *inode; struct dquot *dquot; char *tmp; int error; if ((vfsmnt = lookup_vfsmnt(dev)) == (struct vfsmount *)NULL) return(-ENODEV); if (vfsmnt->mnt_quotas[type] != (struct file *)NULL) return(-EBUSY); if ((error = getname(path, &tmp)) != 0) return(error); error = open_namei(tmp, O_RDWR, 0600, &inode, 0); putname(tmp); if (error) return(error); if (!S_ISREG(inode->i_mode)) { iput(inode); return(-EACCES); } if ((filp = get_empty_filp()) != (struct file *)NULL) { filp->f_mode = (O_RDWR + 1) & O_ACCMODE; filp->f_flags = O_RDWR; filp->f_inode = inode; filp->f_pos = 0; filp->f_reada = 0; filp->f_op = inode->i_op->default_file_ops; if (filp->f_op->read || filp->f_op->write) { if ((error = get_write_access(inode)) == 0) { if (filp->f_op && filp->f_op->open) error = filp->f_op->open(inode, filp); if (error == 0) { vfsmnt->mnt_quotas[type] = filp; dquot = dqget(dev, 0, type); vfsmnt->mnt_iexp[type] = (dquot) ? dquot->dq_itime : MAX_IQ_TIME; vfsmnt->mnt_bexp[type] = (dquot) ? dquot->dq_btime : MAX_DQ_TIME; dqput(dquot); vfsmnt->mnt_sb->dq_op = &dquot_operations; add_dquot_ref(dev, type); return(0); } put_write_access(inode); } } else error = -EIO; filp->f_count--; } else error = -EMFILE; iput(inode); return(error);}/* * Ok this is the systemcall interface, this communicates with * the userlevel programs. Currently this only supports diskquota * calls. Maybe we need to add the process quotas etc in the future. * But we probably better use rlimits for that. */asmlinkage int sys_quotactl(int cmd, const char *special, int id, caddr_t addr){ int cmds = 0, type = 0, flags = 0; struct inode *ino; kdev_t dev; cmds = cmd >> SUBCMDSHIFT; type = cmd & SUBCMDMASK; if ((u_int) type >= MAXQUOTAS) return(-EINVAL); switch (cmds) { case Q_SYNC: case Q_GETSTATS: break; case Q_GETQUOTA: if (((type == USRQUOTA && current->uid != id) || (type == GRPQUOTA && current->gid != id)) && !fsuser()) return(-EPERM); break; default: if (!fsuser()) return(-EPERM); } if (special == (char *)NULL && (cmds == Q_SYNC || cmds == Q_GETSTATS)) dev = 0; else { if (namei(special, &ino)) return(-EINVAL); dev = ino->i_rdev; if (!S_ISBLK(ino->i_mode)) { iput(ino); return(-ENOTBLK); } iput(ino); } switch (cmds) { case Q_QUOTAON: return(quota_on(dev, type, (char *) addr)); case Q_QUOTAOFF: return(quota_off(dev, type)); case Q_GETQUOTA: return(get_quota(dev, id, type, (struct dqblk *) addr)); case Q_SETQUOTA: flags |= SET_QUOTA; break; case Q_SETUSE: flags |= SET_USE; break; case Q_SETQLIM: flags |= SET_QLIMIT; break; case Q_SYNC: return(sync_dquots(dev, type)); case Q_GETSTATS: return(get_stats(addr)); default: return(-EINVAL); } if (id & ~0xFFFF) return(-EINVAL); flags |= QUOTA_SYSCALL; if (has_quota_enabled(dev, type)) return(set_dqblk(dev, id, type, flags, (struct dqblk *) addr)); return(-ESRCH);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -