static int v1_commit_dqblk(struct dquot *dquot) { short type = dquot->dq_type; ssize_t ret; struct v1_disk_dqblk dqblk; v1_mem2disk_dqblk(&dqblk, &dquot->dq_dqb); if (dquot->dq_id == 0) { dqblk.dqb_btime = sb_dqopt(dquot->dq_sb)->info[type].dqi_bgrace; dqblk.dqb_itime = sb_dqopt(dquot->dq_sb)->info[type].dqi_igrace; } ret = 0; if (sb_dqopt(dquot->dq_sb)->files[type]) ret = dquot->dq_sb->s_op->quota_write(dquot->dq_sb, type, (char *)&dqblk, sizeof(struct v1_disk_dqblk), v1_dqoff(dquot->dq_id)); if (ret != sizeof(struct v1_disk_dqblk)) { printk(KERN_WARNING "VFS: dquota write failed on dev %s\n", dquot->dq_sb->s_id); if (ret >= 0) ret = -EIO; goto out; } ret = 0; out: dqstats.writes++; return ret; }
static int v1_commit_dqblk(struct dquot *dquot) { short type = dquot->dq_id.type; ssize_t ret; struct v1_disk_dqblk dqblk; v1_mem2disk_dqblk(&dqblk, &dquot->dq_dqb); if (((type == USRQUOTA) && uid_eq(dquot->dq_id.uid, GLOBAL_ROOT_UID)) || ((type == GRPQUOTA) && gid_eq(dquot->dq_id.gid, GLOBAL_ROOT_GID))) { dqblk.dqb_btime = sb_dqopt(dquot->dq_sb)->info[type].dqi_bgrace; dqblk.dqb_itime = sb_dqopt(dquot->dq_sb)->info[type].dqi_igrace; } ret = 0; if (sb_dqopt(dquot->dq_sb)->files[type]) ret = dquot->dq_sb->s_op->quota_write(dquot->dq_sb, type, (char *)&dqblk, sizeof(struct v1_disk_dqblk), v1_dqoff(from_kqid(&init_user_ns, dquot->dq_id))); if (ret != sizeof(struct v1_disk_dqblk)) { quota_error(dquot->dq_sb, "dquota write failed"); if (ret >= 0) ret = -EIO; goto out; } ret = 0; out: dqstats_inc(DQST_WRITES); return ret; }
static int v1_commit_dqblk(struct dquot *dquot) { short type = dquot->dq_type; ssize_t ret; struct v1_disk_dqblk dqblk; v1_mem2disk_dqblk(&dqblk, &dquot->dq_dqb); if (dquot->dq_id == 0) { dqblk.dqb_btime = sb_dqopt(dquot->dq_sb)->info[type].dqi_bgrace; dqblk.dqb_itime = sb_dqopt(dquot->dq_sb)->info[type].dqi_igrace; } ret = 0; if (sb_dqopt(dquot->dq_sb)->files[type]) ret = dquot->dq_sb->s_op->quota_write(dquot->dq_sb, type, (char *)&dqblk, sizeof(struct v1_disk_dqblk), v1_dqoff(dquot->dq_id)); if (ret != sizeof(struct v1_disk_dqblk)) { quota_error(dquot->dq_sb, "dquota write failed"); if (ret >= 0) ret = -EIO; goto out; } ret = 0; out: dqstats_inc(DQST_WRITES); return ret; }
static int quota_getfmt(struct super_block *sb, int type, void __user *addr) { __u32 fmt; down_read(&sb_dqopt(sb)->dqptr_sem); if (!sb_has_quota_active(sb, type)) { up_read(&sb_dqopt(sb)->dqptr_sem); return -ESRCH; } fmt = sb_dqopt(sb)->info[type].dqi_format->qf_fmt_id; up_read(&sb_dqopt(sb)->dqptr_sem); if (copy_to_user(addr, &fmt, sizeof(fmt))) return -EFAULT; return 0; }
static int quota_getfmt(struct super_block *sb, int type, void __user *addr) { __u32 fmt; mutex_lock(&sb_dqopt(sb)->dqonoff_mutex); if (!sb_has_quota_active(sb, type)) { mutex_unlock(&sb_dqopt(sb)->dqonoff_mutex); return -ESRCH; } fmt = sb_dqopt(sb)->info[type].dqi_format->qf_fmt_id; mutex_unlock(&sb_dqopt(sb)->dqonoff_mutex); if (copy_to_user(addr, &fmt, sizeof(fmt))) return -EFAULT; return 0; }
static int v2_get_info(struct super_block *sb, int type, struct v2c_mem_dqinfo *oinfo) { struct if_dqinfo info; int ret; if ((ret = sb->s_qcop->get_info(sb, type, &info)) < 0) return ret; oinfo->dqi_bgrace = info.dqi_bgrace; oinfo->dqi_igrace = info.dqi_igrace; oinfo->dqi_flags = info.dqi_flags; oinfo->dqi_blocks = sb_dqopt(sb)->info[type].u.v2_i.dqi_blocks; oinfo->dqi_free_blk = sb_dqopt(sb)->info[type].u.v2_i.dqi_free_blk; oinfo->dqi_free_entry = sb_dqopt(sb)->info[type].u.v2_i.dqi_free_entry; return 0; }
static int v1_read_dqblk(struct dquot *dquot) { int type = dquot->dq_id.type; struct v1_disk_dqblk dqblk; struct quota_info *dqopt = sb_dqopt(dquot->dq_sb); if (!dqopt->files[type]) return -EINVAL; /* Set structure to 0s in case read fails/is after end of file */ memset(&dqblk, 0, sizeof(struct v1_disk_dqblk)); dquot->dq_sb->s_op->quota_read(dquot->dq_sb, type, (char *)&dqblk, sizeof(struct v1_disk_dqblk), v1_dqoff(from_kqid(&init_user_ns, dquot->dq_id))); v1_disk2mem_dqblk(&dquot->dq_dqb, &dqblk); if (dquot->dq_dqb.dqb_bhardlimit == 0 && dquot->dq_dqb.dqb_bsoftlimit == 0 && dquot->dq_dqb.dqb_ihardlimit == 0 && dquot->dq_dqb.dqb_isoftlimit == 0) set_bit(DQ_FAKE_B, &dquot->dq_flags); dqstats_inc(DQST_READS); return 0; }
/* Write to quotafile */ static ssize_t ext2_quota_write(struct super_block *sb, int type, const char *data, size_t len, loff_t off) { struct inode *inode = sb_dqopt(sb)->files[type]; sector_t blk = off >> EXT2_BLOCK_SIZE_BITS(sb); int err = 0; int offset = off & (sb->s_blocksize - 1); int tocopy; size_t towrite = len; struct buffer_head tmp_bh; struct buffer_head *bh; mutex_lock_nested(&inode->i_mutex, I_MUTEX_QUOTA); while (towrite > 0) { tocopy = sb->s_blocksize - offset < towrite ? sb->s_blocksize - offset : towrite; tmp_bh.b_state = 0; err = ext2_get_block(inode, blk, &tmp_bh, 1); if (err < 0) goto out; if (offset || tocopy != EXT2_BLOCK_SIZE(sb)) bh = sb_bread(sb, tmp_bh.b_blocknr); else bh = sb_getblk(sb, tmp_bh.b_blocknr); if (!bh) { err = -EIO; goto out; } lock_buffer(bh); memcpy(bh->b_data+offset, data, tocopy); flush_dcache_page(bh->b_page); set_buffer_uptodate(bh); mark_buffer_dirty(bh); unlock_buffer(bh); brelse(bh); offset = 0; towrite -= tocopy; data += tocopy; blk++; } out: if (len == towrite) { mutex_unlock(&inode->i_mutex); return err; } if (inode->i_size < off+len-towrite) i_size_write(inode, off+len-towrite); inode->i_version++; inode->i_mtime = inode->i_ctime = CURRENT_TIME; mark_inode_dirty(inode); mutex_unlock(&inode->i_mutex); return len - towrite; }
/* Write to quotafile */ static ssize_t ufs_quota_write(struct super_block *sb, int type, const char *data, size_t len, loff_t off) { struct inode *inode = sb_dqopt(sb)->files[type]; sector_t blk = off >> sb->s_blocksize_bits; int err = 0; int offset = off & (sb->s_blocksize - 1); int tocopy; size_t towrite = len; struct buffer_head *bh; mutex_lock_nested(&inode->i_mutex, I_MUTEX_QUOTA); while (towrite > 0) { tocopy = sb->s_blocksize - offset < towrite ? sb->s_blocksize - offset : towrite; bh = ufs_bread(inode, blk, 1, &err); if (!bh) goto out; lock_buffer(bh); memcpy(bh->b_data+offset, data, tocopy); flush_dcache_page(bh->b_page); set_buffer_uptodate(bh); mark_buffer_dirty(bh); unlock_buffer(bh); brelse(bh); offset = 0; towrite -= tocopy; data += tocopy; blk++; } out: if (len == towrite) { mutex_unlock(&inode->i_mutex); return err; } if (inode->i_size < off+len-towrite) i_size_write(inode, off+len-towrite); inode->i_version++; inode->i_mtime = inode->i_ctime = CURRENT_TIME_SEC; mark_inode_dirty(inode); mutex_unlock(&inode->i_mutex); return len - towrite; }
/* Read data from quotafile - avoid pagecache and such because we cannot afford * acquiring the locks... As quota files are never truncated and quota code * itself serializes the operations (and noone else should touch the files) * we don't have to be afraid of races */ static ssize_t ext2_quota_read(struct super_block *sb, int type, char *data, size_t len, loff_t off) { struct inode *inode = sb_dqopt(sb)->files[type]; sector_t blk = off >> EXT2_BLOCK_SIZE_BITS(sb); int err = 0; int offset = off & (sb->s_blocksize - 1); int tocopy; size_t toread; struct buffer_head tmp_bh; struct buffer_head *bh; loff_t i_size = i_size_read(inode); if (off > i_size) return 0; if (off+len > i_size) len = i_size-off; toread = len; while (toread > 0) { tocopy = sb->s_blocksize - offset < toread ? sb->s_blocksize - offset : toread; tmp_bh.b_state = 0; tmp_bh.b_size = sb->s_blocksize; err = ext2_get_block(inode, blk, &tmp_bh, 0); if (err < 0) return err; if (!buffer_mapped(&tmp_bh)) /* A hole? */ memset(data, 0, tocopy); else { bh = sb_bread(sb, tmp_bh.b_blocknr); if (!bh) return -EIO; memcpy(data, bh->b_data+offset, tocopy); brelse(bh); } offset = 0; toread -= tocopy; data += tocopy; blk++; } return len; }
static int v1_read_dqblk(struct dquot *dquot) { int type = dquot->dq_type; struct v1_disk_dqblk dqblk; if (!sb_dqopt(dquot->dq_sb)->files[type]) return -EINVAL; /* Set structure to 0s in case read fails/is after end of file */ memset(&dqblk, 0, sizeof(struct v1_disk_dqblk)); dquot->dq_sb->s_op->quota_read(dquot->dq_sb, type, (char *)&dqblk, sizeof(struct v1_disk_dqblk), v1_dqoff(dquot->dq_id)); v1_disk2mem_dqblk(&dquot->dq_dqb, &dqblk); if (dquot->dq_dqb.dqb_bhardlimit == 0 && dquot->dq_dqb.dqb_bsoftlimit == 0 && dquot->dq_dqb.dqb_ihardlimit == 0 && dquot->dq_dqb.dqb_isoftlimit == 0) set_bit(DQ_FAKE_B, &dquot->dq_flags); dqstats.reads++; return 0; }
static int ext2_quota_off(struct super_block *sb, int type) { struct inode *inode = sb_dqopt(sb)->files[type]; int err; if (!inode || !igrab(inode)) goto out; err = dquot_quota_off(sb, type); if (err) goto out_put; inode_lock(inode); EXT2_I(inode)->i_flags &= ~(EXT2_NOATIME_FL | EXT2_IMMUTABLE_FL); inode_set_flags(inode, 0, S_NOATIME | S_IMMUTABLE); inode_unlock(inode); mark_inode_dirty(inode); out_put: iput(inode); return err; out: return dquot_quota_off(sb, type); }
/* Read data from quotafile - avoid pagecache and such because we cannot afford * acquiring the locks... As quota files are never truncated and quota code * itself serializes the operations (and noone else should touch the files) * we don't have to be afraid of races */ static ssize_t ufs_quota_read(struct super_block *sb, int type, char *data, size_t len, loff_t off) { struct inode *inode = sb_dqopt(sb)->files[type]; sector_t blk = off >> sb->s_blocksize_bits; int err = 0; int offset = off & (sb->s_blocksize - 1); int tocopy; size_t toread; struct buffer_head *bh; loff_t i_size = i_size_read(inode); if (off > i_size) return 0; if (off+len > i_size) len = i_size-off; toread = len; while (toread > 0) { tocopy = sb->s_blocksize - offset < toread ? sb->s_blocksize - offset : toread; bh = ufs_bread(inode, blk, 0, &err); if (err) return err; if (!bh) /* A hole? */ memset(data, 0, tocopy); else { memcpy(data, bh->b_data+offset, tocopy); brelse(bh); } offset = 0; toread -= tocopy; data += tocopy; blk++; } return len; }
static int check_compat_quotactl_valid(struct super_block *sb, int type, int cmd, qid_t id) { if (type >= MAXQUOTAS) return -EINVAL; /* Is operation supported? */ /* sb==NULL for GETSTATS calls */ if (sb && !sb->s_qcop) return -ENOSYS; switch (cmd) { case Q_COMP_QUOTAON: if (!sb->s_qcop->quota_on) return -ENOSYS; break; case Q_COMP_QUOTAOFF: if (!sb->s_qcop->quota_off) return -ENOSYS; break; case Q_COMP_SYNC: if (!sb->s_qcop->quota_sync) return -ENOSYS; break; #ifdef CONFIG_QIFACE_V2 case Q_V2_SETFLAGS: case Q_V2_SETGRACE: case Q_V2_SETINFO: if (!sb->s_qcop->set_info) return -ENOSYS; break; case Q_V2_GETINFO: if (!sb->s_qcop->get_info) return -ENOSYS; break; case Q_V2_SETQLIM: case Q_V2_SETUSE: case Q_V2_SETQUOTA: if (!sb->s_qcop->set_dqblk) return -ENOSYS; break; case Q_V2_GETQUOTA: if (!sb->s_qcop->get_dqblk) return -ENOSYS; break; case Q_V2_GETSTATS: return 0; /* GETSTATS need no other checks */ #endif #ifdef CONFIG_QIFACE_V1 case Q_V1_SETQLIM: case Q_V1_SETUSE: case Q_V1_SETQUOTA: if (!sb->s_qcop->set_dqblk) return -ENOSYS; break; case Q_V1_GETQUOTA: if (!sb->s_qcop->get_dqblk) return -ENOSYS; break; case Q_V1_RSQUASH: if (!sb->s_qcop->set_info) return -ENOSYS; break; case Q_V1_GETSTATS: return 0; /* GETSTATS need no other checks */ #endif default: return -EINVAL; } /* Is quota turned on for commands which need it? */ switch (cmd) { case Q_V2_SETFLAGS: case Q_V2_SETGRACE: case Q_V2_SETINFO: case Q_V2_GETINFO: case Q_COMP_QUOTAOFF: case Q_V1_RSQUASH: case Q_V1_SETQUOTA: case Q_V1_SETQLIM: case Q_V1_SETUSE: case Q_V2_SETQUOTA: /* Q_V2_SETQLIM: collision with Q_V1_SETQLIM */ case Q_V2_SETUSE: case Q_V1_GETQUOTA: case Q_V2_GETQUOTA: if (!sb_has_quota_enabled(sb, type)) return -ESRCH; } #ifdef CONFIG_QIFACE_V1 if (cmd != Q_COMP_QUOTAON && cmd != Q_COMP_QUOTAOFF && cmd != Q_COMP_SYNC && sb_dqopt(sb)->info[type].dqi_format->qf_fmt_id != QFMT_VFS_OLD) #else if (cmd != Q_COMP_QUOTAON && cmd != Q_COMP_QUOTAOFF && cmd != Q_COMP_SYNC && sb_dqopt(sb)->info[type].dqi_format->qf_fmt_id != QFMT_VFS_V0) #endif return -ESRCH; /* Check privileges */ if (cmd == Q_V1_GETQUOTA || cmd == Q_V2_GETQUOTA) { if (((type == USRQUOTA && current->euid != id) || (type == GRPQUOTA && !in_egroup_p(id))) && !capable(CAP_SYS_ADMIN)) return -EPERM; } else if (cmd != Q_V1_GETSTATS && cmd != Q_V2_GETSTATS && cmd != Q_V2_GETINFO && cmd != Q_COMP_SYNC) if (!capable(CAP_SYS_ADMIN)) return -EPERM; return 0; }
/* Copy parameters and call proper function */ static int do_quotactl(struct super_block *sb, int type, int cmd, qid_t id, caddr_t addr) { int ret; switch (cmd) { case Q_QUOTAON: { char *pathname; if (IS_ERR(pathname = getname(addr))) return PTR_ERR(pathname); ret = sb->s_qcop->quota_on(sb, type, id, pathname); putname(pathname); return ret; } case Q_QUOTAOFF: return sb->s_qcop->quota_off(sb, type); case Q_GETFMT: { __u32 fmt; fmt = sb_dqopt(sb)->info[type].dqi_format->qf_fmt_id; if (copy_to_user(addr, &fmt, sizeof(fmt))) return -EFAULT; return 0; } case Q_GETINFO: { struct if_dqinfo info; if ((ret = sb->s_qcop->get_info(sb, type, &info))) return ret; if (copy_to_user(addr, &info, sizeof(info))) return -EFAULT; return 0; } case Q_SETINFO: { struct if_dqinfo info; if (copy_from_user(&info, addr, sizeof(info))) return -EFAULT; return sb->s_qcop->set_info(sb, type, &info); } case Q_GETQUOTA: { struct if_dqblk idq; if ((ret = sb->s_qcop->get_dqblk(sb, type, id, &idq))) return ret; if (copy_to_user(addr, &idq, sizeof(idq))) return -EFAULT; return 0; } case Q_SETQUOTA: { struct if_dqblk idq; if (copy_from_user(&idq, addr, sizeof(idq))) return -EFAULT; return sb->s_qcop->set_dqblk(sb, type, id, &idq); } case Q_SYNC: return sb->s_qcop->quota_sync(sb, type); case Q_XQUOTAON: case Q_XQUOTAOFF: case Q_XQUOTARM: { __u32 flags; if (copy_from_user(&flags, addr, sizeof(flags))) return -EFAULT; return sb->s_qcop->set_xstate(sb, flags, cmd); } case Q_XGETQSTAT: { struct fs_quota_stat fqs; if ((ret = sb->s_qcop->get_xstate(sb, &fqs))) return ret; if (copy_to_user(addr, &fqs, sizeof(fqs))) return -EFAULT; return 0; } case Q_XSETQLIM: { struct fs_disk_quota fdq; if (copy_from_user(&fdq, addr, sizeof(fdq))) return -EFAULT; return sb->s_qcop->set_xquota(sb, type, id, &fdq); } case Q_XGETQUOTA: { struct fs_disk_quota fdq; if ((ret = sb->s_qcop->get_xquota(sb, type, id, &fdq))) return ret; if (copy_to_user(addr, &fdq, sizeof(fdq))) return -EFAULT; return 0; } /* We never reach here unless validity check is broken */ default: BUG(); } return 0; }