Commit 7dca0548 authored by Jan Kara's avatar Jan Kara

Merge branch 'quota_interface' into for_next_testing

parents 1be440de c39fb53b
......@@ -789,7 +789,7 @@ static const struct quotactl_ops ext3_qctl_operations = {
.quota_on = ext3_quota_on,
.quota_off = dquot_quota_off,
.quota_sync = dquot_quota_sync,
.get_info = dquot_get_dqinfo,
.get_state = dquot_get_state,
.set_info = dquot_set_dqinfo,
.get_dqblk = dquot_get_dqblk,
.set_dqblk = dquot_set_dqblk
......
......@@ -1076,7 +1076,7 @@ static const struct quotactl_ops ext4_qctl_operations = {
.quota_on = ext4_quota_on,
.quota_off = ext4_quota_off,
.quota_sync = dquot_quota_sync,
.get_info = dquot_get_dqinfo,
.get_state = dquot_get_state,
.set_info = dquot_set_dqinfo,
.get_dqblk = dquot_get_dqblk,
.set_dqblk = dquot_set_dqblk
......
......@@ -1468,32 +1468,34 @@ int gfs2_quotad(void *data)
return 0;
}
static int gfs2_quota_get_xstate(struct super_block *sb,
struct fs_quota_stat *fqs)
static int gfs2_quota_get_state(struct super_block *sb, struct qc_state *state)
{
struct gfs2_sbd *sdp = sb->s_fs_info;
memset(fqs, 0, sizeof(struct fs_quota_stat));
fqs->qs_version = FS_QSTAT_VERSION;
memset(state, 0, sizeof(*state));
switch (sdp->sd_args.ar_quota) {
case GFS2_QUOTA_ON:
fqs->qs_flags |= (FS_QUOTA_UDQ_ENFD | FS_QUOTA_GDQ_ENFD);
state->s_state[USRQUOTA].flags |= QCI_LIMITS_ENFORCED;
state->s_state[GRPQUOTA].flags |= QCI_LIMITS_ENFORCED;
/*FALLTHRU*/
case GFS2_QUOTA_ACCOUNT:
fqs->qs_flags |= (FS_QUOTA_UDQ_ACCT | FS_QUOTA_GDQ_ACCT);
state->s_state[USRQUOTA].flags |= QCI_ACCT_ENABLED |
QCI_SYSFILE;
state->s_state[GRPQUOTA].flags |= QCI_ACCT_ENABLED |
QCI_SYSFILE;
break;
case GFS2_QUOTA_OFF:
break;
}
if (sdp->sd_quota_inode) {
fqs->qs_uquota.qfs_ino = GFS2_I(sdp->sd_quota_inode)->i_no_addr;
fqs->qs_uquota.qfs_nblks = sdp->sd_quota_inode->i_blocks;
state->s_state[USRQUOTA].ino =
GFS2_I(sdp->sd_quota_inode)->i_no_addr;
state->s_state[USRQUOTA].blocks = sdp->sd_quota_inode->i_blocks;
}
fqs->qs_uquota.qfs_nextents = 1; /* unsupported */
fqs->qs_gquota = fqs->qs_uquota; /* its the same inode in both cases */
fqs->qs_incoredqs = list_lru_count(&gfs2_qd_lru);
state->s_state[USRQUOTA].nextents = 1; /* unsupported */
state->s_state[GRPQUOTA] = state->s_state[USRQUOTA];
state->s_incoredqs = list_lru_count(&gfs2_qd_lru);
return 0;
}
......@@ -1638,7 +1640,7 @@ static int gfs2_set_dqblk(struct super_block *sb, struct kqid qid,
const struct quotactl_ops gfs2_quotactl_ops = {
.quota_sync = gfs2_quota_sync,
.get_xstate = gfs2_quota_get_xstate,
.get_state = gfs2_quota_get_state,
.get_dqblk = gfs2_get_dqblk,
.set_dqblk = gfs2_set_dqblk,
};
......
......@@ -2629,55 +2629,73 @@ int dquot_set_dqblk(struct super_block *sb, struct kqid qid,
EXPORT_SYMBOL(dquot_set_dqblk);
/* Generic routine for getting common part of quota file information */
int dquot_get_dqinfo(struct super_block *sb, int type, struct if_dqinfo *ii)
int dquot_get_state(struct super_block *sb, struct qc_state *state)
{
struct mem_dqinfo *mi;
struct qc_type_state *tstate;
struct quota_info *dqopt = sb_dqopt(sb);
int type;
mutex_lock(&sb_dqopt(sb)->dqonoff_mutex);
if (!sb_has_quota_active(sb, type)) {
mutex_unlock(&sb_dqopt(sb)->dqonoff_mutex);
return -ESRCH;
memset(state, 0, sizeof(*state));
for (type = 0; type < MAXQUOTAS; type++) {
if (!sb_has_quota_active(sb, type))
continue;
tstate = state->s_state + type;
mi = sb_dqopt(sb)->info + type;
tstate->flags = QCI_ACCT_ENABLED;
spin_lock(&dq_data_lock);
if (mi->dqi_flags & DQF_SYS_FILE)
tstate->flags |= QCI_SYSFILE;
if (mi->dqi_flags & DQF_ROOT_SQUASH)
tstate->flags |= QCI_ROOT_SQUASH;
if (sb_has_quota_limits_enabled(sb, type))
tstate->flags |= QCI_LIMITS_ENFORCED;
tstate->spc_timelimit = mi->dqi_bgrace;
tstate->ino_timelimit = mi->dqi_igrace;
tstate->ino = dqopt->files[type]->i_ino;
tstate->blocks = dqopt->files[type]->i_blocks;
tstate->nextents = 1; /* We don't know... */
spin_unlock(&dq_data_lock);
}
mi = sb_dqopt(sb)->info + type;
spin_lock(&dq_data_lock);
ii->dqi_bgrace = mi->dqi_bgrace;
ii->dqi_igrace = mi->dqi_igrace;
ii->dqi_flags = mi->dqi_flags & DQF_GETINFO_MASK;
ii->dqi_valid = IIF_ALL;
spin_unlock(&dq_data_lock);
mutex_unlock(&sb_dqopt(sb)->dqonoff_mutex);
return 0;
}
EXPORT_SYMBOL(dquot_get_dqinfo);
EXPORT_SYMBOL(dquot_get_state);
/* Generic routine for setting common part of quota file information */
int dquot_set_dqinfo(struct super_block *sb, int type, struct if_dqinfo *ii)
int dquot_set_dqinfo(struct super_block *sb, int type, struct qc_info *ii)
{
struct mem_dqinfo *mi;
int err = 0;
if ((ii->i_fieldmask & QC_WARNS_MASK) ||
(ii->i_fieldmask & QC_RT_SPC_TIMER))
return -EINVAL;
mutex_lock(&sb_dqopt(sb)->dqonoff_mutex);
if (!sb_has_quota_active(sb, type)) {
err = -ESRCH;
goto out;
}
mi = sb_dqopt(sb)->info + type;
if (ii->dqi_valid & IIF_FLAGS) {
if (ii->dqi_flags & ~DQF_SETINFO_MASK ||
(ii->dqi_flags & DQF_ROOT_SQUASH &&
if (ii->i_fieldmask & QC_FLAGS) {
if ((ii->i_flags & QCI_ROOT_SQUASH &&
mi->dqi_format->qf_fmt_id != QFMT_VFS_OLD)) {
err = -EINVAL;
goto out;
}
}
spin_lock(&dq_data_lock);
if (ii->dqi_valid & IIF_BGRACE)
mi->dqi_bgrace = ii->dqi_bgrace;
if (ii->dqi_valid & IIF_IGRACE)
mi->dqi_igrace = ii->dqi_igrace;
if (ii->dqi_valid & IIF_FLAGS)
mi->dqi_flags = (mi->dqi_flags & ~DQF_SETINFO_MASK) |
(ii->dqi_flags & DQF_SETINFO_MASK);
if (ii->i_fieldmask & QC_SPC_TIMER)
mi->dqi_bgrace = ii->i_spc_timelimit;
if (ii->i_fieldmask & QC_INO_TIMER)
mi->dqi_igrace = ii->i_ino_timelimit;
if (ii->i_fieldmask & QC_FLAGS) {
if (ii->i_flags & QCI_ROOT_SQUASH)
mi->dqi_flags |= DQF_ROOT_SQUASH;
else
mi->dqi_flags &= ~DQF_ROOT_SQUASH;
}
spin_unlock(&dq_data_lock);
mark_info_dirty(sb, type);
/* Force write to disk */
......@@ -2692,7 +2710,7 @@ const struct quotactl_ops dquot_quotactl_ops = {
.quota_on = dquot_quota_on,
.quota_off = dquot_quota_off,
.quota_sync = dquot_quota_sync,
.get_info = dquot_get_dqinfo,
.get_state = dquot_get_state,
.set_info = dquot_set_dqinfo,
.get_dqblk = dquot_get_dqblk,
.set_dqblk = dquot_set_dqblk
......@@ -2703,7 +2721,7 @@ const struct quotactl_ops dquot_quotactl_sysfile_ops = {
.quota_enable = dquot_quota_enable,
.quota_disable = dquot_quota_disable,
.quota_sync = dquot_quota_sync,
.get_info = dquot_get_dqinfo,
.get_state = dquot_get_state,
.set_info = dquot_set_dqinfo,
.get_dqblk = dquot_get_dqblk,
.set_dqblk = dquot_set_dqblk
......
......@@ -118,13 +118,30 @@ static int quota_getfmt(struct super_block *sb, int type, void __user *addr)
static int quota_getinfo(struct super_block *sb, int type, void __user *addr)
{
struct if_dqinfo info;
struct qc_state state;
struct qc_type_state *tstate;
struct if_dqinfo uinfo;
int ret;
if (!sb->s_qcop->get_info)
/* This checks whether qc_state has enough entries... */
BUILD_BUG_ON(MAXQUOTAS > XQM_MAXQUOTAS);
if (!sb->s_qcop->get_state)
return -ENOSYS;
ret = sb->s_qcop->get_info(sb, type, &info);
if (!ret && copy_to_user(addr, &info, sizeof(info)))
ret = sb->s_qcop->get_state(sb, &state);
if (ret)
return ret;
tstate = state.s_state + type;
if (!(tstate->flags & QCI_ACCT_ENABLED))
return -ESRCH;
memset(&uinfo, 0, sizeof(uinfo));
uinfo.dqi_bgrace = tstate->spc_timelimit;
uinfo.dqi_igrace = tstate->ino_timelimit;
if (tstate->flags & QCI_SYSFILE)
uinfo.dqi_flags |= DQF_SYS_FILE;
if (tstate->flags & QCI_ROOT_SQUASH)
uinfo.dqi_flags |= DQF_ROOT_SQUASH;
uinfo.dqi_valid = IIF_ALL;
if (!ret && copy_to_user(addr, &uinfo, sizeof(uinfo)))
return -EFAULT;
return ret;
}
......@@ -132,12 +149,31 @@ static int quota_getinfo(struct super_block *sb, int type, void __user *addr)
static int quota_setinfo(struct super_block *sb, int type, void __user *addr)
{
struct if_dqinfo info;
struct qc_info qinfo;
if (copy_from_user(&info, addr, sizeof(info)))
return -EFAULT;
if (!sb->s_qcop->set_info)
return -ENOSYS;
return sb->s_qcop->set_info(sb, type, &info);
if (info.dqi_valid & ~(IIF_FLAGS | IIF_BGRACE | IIF_IGRACE))
return -EINVAL;
memset(&qinfo, 0, sizeof(qinfo));
if (info.dqi_valid & IIF_FLAGS) {
if (info.dqi_flags & ~DQF_SETINFO_MASK)
return -EINVAL;
if (info.dqi_flags & DQF_ROOT_SQUASH)
qinfo.i_flags |= QCI_ROOT_SQUASH;
qinfo.i_fieldmask |= QC_FLAGS;
}
if (info.dqi_valid & IIF_BGRACE) {
qinfo.i_spc_timelimit = info.dqi_bgrace;
qinfo.i_fieldmask |= QC_SPC_TIMER;
}
if (info.dqi_valid & IIF_IGRACE) {
qinfo.i_ino_timelimit = info.dqi_igrace;
qinfo.i_fieldmask |= QC_INO_TIMER;
}
return sb->s_qcop->set_info(sb, type, &qinfo);
}
static inline qsize_t qbtos(qsize_t blocks)
......@@ -252,25 +288,149 @@ static int quota_disable(struct super_block *sb, void __user *addr)
return sb->s_qcop->quota_disable(sb, flags);
}
static int quota_state_to_flags(struct qc_state *state)
{
int flags = 0;
if (state->s_state[USRQUOTA].flags & QCI_ACCT_ENABLED)
flags |= FS_QUOTA_UDQ_ACCT;
if (state->s_state[USRQUOTA].flags & QCI_LIMITS_ENFORCED)
flags |= FS_QUOTA_UDQ_ENFD;
if (state->s_state[GRPQUOTA].flags & QCI_ACCT_ENABLED)
flags |= FS_QUOTA_GDQ_ACCT;
if (state->s_state[GRPQUOTA].flags & QCI_LIMITS_ENFORCED)
flags |= FS_QUOTA_GDQ_ENFD;
if (state->s_state[PRJQUOTA].flags & QCI_ACCT_ENABLED)
flags |= FS_QUOTA_PDQ_ACCT;
if (state->s_state[PRJQUOTA].flags & QCI_LIMITS_ENFORCED)
flags |= FS_QUOTA_PDQ_ENFD;
return flags;
}
static int quota_getstate(struct super_block *sb, struct fs_quota_stat *fqs)
{
int type;
struct qc_state state;
int ret;
ret = sb->s_qcop->get_state(sb, &state);
if (ret < 0)
return ret;
memset(fqs, 0, sizeof(*fqs));
fqs->qs_version = FS_QSTAT_VERSION;
fqs->qs_flags = quota_state_to_flags(&state);
/* No quota enabled? */
if (!fqs->qs_flags)
return -ENOSYS;
fqs->qs_incoredqs = state.s_incoredqs;
/*
* GETXSTATE quotactl has space for just one set of time limits so
* report them for the first enabled quota type
*/
for (type = 0; type < XQM_MAXQUOTAS; type++)
if (state.s_state[type].flags & QCI_ACCT_ENABLED)
break;
BUG_ON(type == XQM_MAXQUOTAS);
fqs->qs_btimelimit = state.s_state[type].spc_timelimit;
fqs->qs_itimelimit = state.s_state[type].ino_timelimit;
fqs->qs_rtbtimelimit = state.s_state[type].rt_spc_timelimit;
fqs->qs_bwarnlimit = state.s_state[type].spc_warnlimit;
fqs->qs_iwarnlimit = state.s_state[type].ino_warnlimit;
if (state.s_state[USRQUOTA].flags & QCI_ACCT_ENABLED) {
fqs->qs_uquota.qfs_ino = state.s_state[USRQUOTA].ino;
fqs->qs_uquota.qfs_nblks = state.s_state[USRQUOTA].blocks;
fqs->qs_uquota.qfs_nextents = state.s_state[USRQUOTA].nextents;
}
if (state.s_state[GRPQUOTA].flags & QCI_ACCT_ENABLED) {
fqs->qs_gquota.qfs_ino = state.s_state[GRPQUOTA].ino;
fqs->qs_gquota.qfs_nblks = state.s_state[GRPQUOTA].blocks;
fqs->qs_gquota.qfs_nextents = state.s_state[GRPQUOTA].nextents;
}
if (state.s_state[PRJQUOTA].flags & QCI_ACCT_ENABLED) {
/*
* Q_XGETQSTAT doesn't have room for both group and project
* quotas. So, allow the project quota values to be copied out
* only if there is no group quota information available.
*/
if (!(state.s_state[GRPQUOTA].flags & QCI_ACCT_ENABLED)) {
fqs->qs_gquota.qfs_ino = state.s_state[PRJQUOTA].ino;
fqs->qs_gquota.qfs_nblks =
state.s_state[PRJQUOTA].blocks;
fqs->qs_gquota.qfs_nextents =
state.s_state[PRJQUOTA].nextents;
}
}
return 0;
}
static int quota_getxstate(struct super_block *sb, void __user *addr)
{
struct fs_quota_stat fqs;
int ret;
if (!sb->s_qcop->get_xstate)
if (!sb->s_qcop->get_state)
return -ENOSYS;
ret = sb->s_qcop->get_xstate(sb, &fqs);
ret = quota_getstate(sb, &fqs);
if (!ret && copy_to_user(addr, &fqs, sizeof(fqs)))
return -EFAULT;
return ret;
}
static int quota_getstatev(struct super_block *sb, struct fs_quota_statv *fqs)
{
int type;
struct qc_state state;
int ret;
ret = sb->s_qcop->get_state(sb, &state);
if (ret < 0)
return ret;
memset(fqs, 0, sizeof(*fqs));
fqs->qs_version = FS_QSTAT_VERSION;
fqs->qs_flags = quota_state_to_flags(&state);
/* No quota enabled? */
if (!fqs->qs_flags)
return -ENOSYS;
fqs->qs_incoredqs = state.s_incoredqs;
/*
* GETXSTATV quotactl has space for just one set of time limits so
* report them for the first enabled quota type
*/
for (type = 0; type < XQM_MAXQUOTAS; type++)
if (state.s_state[type].flags & QCI_ACCT_ENABLED)
break;
BUG_ON(type == XQM_MAXQUOTAS);
fqs->qs_btimelimit = state.s_state[type].spc_timelimit;
fqs->qs_itimelimit = state.s_state[type].ino_timelimit;
fqs->qs_rtbtimelimit = state.s_state[type].rt_spc_timelimit;
fqs->qs_bwarnlimit = state.s_state[type].spc_warnlimit;
fqs->qs_iwarnlimit = state.s_state[type].ino_warnlimit;
if (state.s_state[USRQUOTA].flags & QCI_ACCT_ENABLED) {
fqs->qs_uquota.qfs_ino = state.s_state[USRQUOTA].ino;
fqs->qs_uquota.qfs_nblks = state.s_state[USRQUOTA].blocks;
fqs->qs_uquota.qfs_nextents = state.s_state[USRQUOTA].nextents;
}
if (state.s_state[GRPQUOTA].flags & QCI_ACCT_ENABLED) {
fqs->qs_gquota.qfs_ino = state.s_state[GRPQUOTA].ino;
fqs->qs_gquota.qfs_nblks = state.s_state[GRPQUOTA].blocks;
fqs->qs_gquota.qfs_nextents = state.s_state[GRPQUOTA].nextents;
}
if (state.s_state[PRJQUOTA].flags & QCI_ACCT_ENABLED) {
fqs->qs_pquota.qfs_ino = state.s_state[PRJQUOTA].ino;
fqs->qs_pquota.qfs_nblks = state.s_state[PRJQUOTA].blocks;
fqs->qs_pquota.qfs_nextents = state.s_state[PRJQUOTA].nextents;
}
return 0;
}
static int quota_getxstatev(struct super_block *sb, void __user *addr)
{
struct fs_quota_statv fqs;
int ret;
if (!sb->s_qcop->get_xstatev)
if (!sb->s_qcop->get_state)
return -ENOSYS;
memset(&fqs, 0, sizeof(fqs));
......@@ -284,7 +444,7 @@ static int quota_getxstatev(struct super_block *sb, void __user *addr)
default:
return -EINVAL;
}
ret = sb->s_qcop->get_xstatev(sb, &fqs);
ret = quota_getstatev(sb, &fqs);
if (!ret && copy_to_user(addr, &fqs, sizeof(fqs)))
return -EFAULT;
return ret;
......@@ -357,6 +517,30 @@ static void copy_from_xfs_dqblk(struct qc_dqblk *dst, struct fs_disk_quota *src)
dst->d_fieldmask |= QC_RT_SPACE;
}
static void copy_qcinfo_from_xfs_dqblk(struct qc_info *dst,
struct fs_disk_quota *src)
{
memset(dst, 0, sizeof(*dst));
dst->i_spc_timelimit = src->d_btimer;
dst->i_ino_timelimit = src->d_itimer;
dst->i_rt_spc_timelimit = src->d_rtbtimer;
dst->i_ino_warnlimit = src->d_iwarns;
dst->i_spc_warnlimit = src->d_bwarns;
dst->i_rt_spc_warnlimit = src->d_rtbwarns;
if (src->d_fieldmask & FS_DQ_BWARNS)
dst->i_fieldmask |= QC_SPC_WARNS;
if (src->d_fieldmask & FS_DQ_IWARNS)
dst->i_fieldmask |= QC_INO_WARNS;
if (src->d_fieldmask & FS_DQ_RTBWARNS)
dst->i_fieldmask |= QC_RT_SPC_WARNS;
if (src->d_fieldmask & FS_DQ_BTIMER)
dst->i_fieldmask |= QC_SPC_TIMER;
if (src->d_fieldmask & FS_DQ_ITIMER)
dst->i_fieldmask |= QC_INO_TIMER;
if (src->d_fieldmask & FS_DQ_RTBTIMER)
dst->i_fieldmask |= QC_RT_SPC_TIMER;
}
static int quota_setxquota(struct super_block *sb, int type, qid_t id,
void __user *addr)
{
......@@ -371,6 +555,21 @@ static int quota_setxquota(struct super_block *sb, int type, qid_t id,
qid = make_kqid(current_user_ns(), type, id);
if (!qid_valid(qid))
return -EINVAL;
/* Are we actually setting timer / warning limits for all users? */
if (from_kqid(&init_user_ns, qid) == 0 &&
fdq.d_fieldmask & (FS_DQ_WARNS_MASK | FS_DQ_TIMER_MASK)) {
struct qc_info qinfo;
int ret;
if (!sb->s_qcop->set_info)
return -EINVAL;
copy_qcinfo_from_xfs_dqblk(&qinfo, &fdq);
ret = sb->s_qcop->set_info(sb, type, &qinfo);
if (ret)
return ret;
/* These are already done */
fdq.d_fieldmask &= ~(FS_DQ_WARNS_MASK | FS_DQ_TIMER_MASK);
}
copy_from_xfs_dqblk(&qdq, &fdq);
return sb->s_qcop->set_dqblk(sb, qid, &qdq);
}
......
......@@ -805,7 +805,7 @@ static const struct quotactl_ops reiserfs_qctl_operations = {
.quota_on = reiserfs_quota_on,
.quota_off = dquot_quota_off,
.quota_sync = dquot_quota_sync,
.get_info = dquot_get_dqinfo,
.get_state = dquot_get_state,
.set_info = dquot_set_dqinfo,
.get_dqblk = dquot_get_dqblk,
.set_dqblk = dquot_set_dqblk,
......
......@@ -168,10 +168,6 @@ extern int xfs_qm_scall_getquota(struct xfs_mount *, xfs_dqid_t,
uint, struct qc_dqblk *);
extern int xfs_qm_scall_setqlim(struct xfs_mount *, xfs_dqid_t, uint,
struct qc_dqblk *);
extern int xfs_qm_scall_getqstat(struct xfs_mount *,
struct fs_quota_stat *);
extern int xfs_qm_scall_getqstatv(struct xfs_mount *,
struct fs_quota_statv *);
extern int xfs_qm_scall_quotaon(struct xfs_mount *, uint);
extern int xfs_qm_scall_quotaoff(struct xfs_mount *, uint);
......
......@@ -38,7 +38,6 @@
STATIC int xfs_qm_log_quotaoff(xfs_mount_t *, xfs_qoff_logitem_t **, uint);
STATIC int xfs_qm_log_quotaoff_end(xfs_mount_t *, xfs_qoff_logitem_t *,
uint);
STATIC uint xfs_qm_export_flags(uint);
/*
* Turn off quota accounting and/or enforcement for all udquots and/or
......@@ -389,159 +388,6 @@ xfs_qm_scall_quotaon(
return 0;
}
/*
* Return quota status information, such as uquota-off, enforcements, etc.
* for Q_XGETQSTAT command.
*/
int
xfs_qm_scall_getqstat(
struct xfs_mount *mp,
struct fs_quota_stat *out)
{
struct xfs_quotainfo *q = mp->m_quotainfo;
struct xfs_inode *uip = NULL;
struct xfs_inode *gip = NULL;
struct xfs_inode *pip = NULL;
bool tempuqip = false;
bool tempgqip = false;
bool temppqip = false;
memset(out, 0, sizeof(fs_quota_stat_t));
out->qs_version = FS_QSTAT_VERSION;
out->qs_flags = (__uint16_t) xfs_qm_export_flags(mp->m_qflags &
(XFS_ALL_QUOTA_ACCT|
XFS_ALL_QUOTA_ENFD));
uip = q->qi_uquotaip;
gip = q->qi_gquotaip;
pip = q->qi_pquotaip;
if (!uip && mp->m_sb.sb_uquotino != NULLFSINO) {
if (xfs_iget(mp, NULL, mp->m_sb.sb_uquotino,
0, 0, &uip) == 0)
tempuqip = true;
}
if (!gip && mp->m_sb.sb_gquotino != NULLFSINO) {
if (xfs_iget(mp, NULL, mp->m_sb.sb_gquotino,
0, 0, &gip) == 0)
tempgqip = true;
}
/*
* Q_XGETQSTAT doesn't have room for both group and project quotas.
* So, allow the project quota values to be copied out only if
* there is no group quota information available.
*/
if (!gip) {
if (!pip && mp->m_sb.sb_pquotino != NULLFSINO) {
if (xfs_iget(mp, NULL, mp->m_sb.sb_pquotino,
0, 0, &pip) == 0)
temppqip = true;
}
} else
pip = NULL;
if (uip) {
out->qs_uquota.qfs_ino = mp->m_sb.sb_uquotino;
out->qs_uquota.qfs_nblks = uip->i_d.di_nblocks;
out->qs_uquota.qfs_nextents = uip->i_d.di_nextents;
if (tempuqip)
IRELE(uip);
}
if (gip) {
out->qs_gquota.qfs_ino = mp->m_sb.sb_gquotino;
out->qs_gquota.qfs_nblks = gip->i_d.di_nblocks;
out->qs_gquota.qfs_nextents = gip->i_d.di_nextents;
if (tempgqip)
IRELE(gip);
}
if (pip) {
out->qs_gquota.qfs_ino = mp->m_sb.sb_gquotino;
out->qs_gquota.qfs_nblks = pip->i_d.di_nblocks;
out->qs_gquota.qfs_nextents = pip->i_d.di_nextents;
if (temppqip)
IRELE(pip);
}
out->qs_incoredqs = q->qi_dquots;
out->qs_btimelimit = q->qi_btimelimit;
out->qs_itimelimit = q->qi_itimelimit;
out->qs_rtbtimelimit = q->qi_rtbtimelimit;
out->qs_bwarnlimit = q->qi_bwarnlimit;
out->qs_iwarnlimit = q->qi_iwarnlimit;
return 0;
}
/*
* Return quota status information, such as uquota-off, enforcements, etc.
* for Q_XGETQSTATV command, to support separate project quota field.
*/
int
xfs_qm_scall_getqstatv(
struct xfs_mount *mp,
struct fs_quota_statv *out)
{
struct xfs_quotainfo *q = mp->m_quotainfo;
struct xfs_inode *uip = NULL;
struct xfs_inode *gip = NULL;
struct xfs_inode *pip = NULL;
bool tempuqip = false;
bool tempgqip = false;
bool temppqip = false;
out->qs_flags = (__uint16_t) xfs_qm_export_flags(mp->m_qflags &
(XFS_ALL_QUOTA_ACCT|
XFS_ALL_QUOTA_ENFD));
out->qs_uquota.qfs_ino = mp->m_sb.sb_uquotino;
out->qs_gquota.qfs_ino = mp->m_sb.sb_gquotino;
out->qs_pquota.qfs_ino = mp->m_sb.sb_pquotino;
uip = q->qi_uquotaip;
gip = q->qi_gquotaip;
pip = q->qi_pquotaip;
if (!uip && mp->m_sb.sb_uquotino != NULLFSINO) {
if (xfs_iget(mp, NULL, mp->m_sb.sb_uquotino,
0, 0, &uip) == 0)
tempuqip = true;
}
if (!gip && mp->m_sb.sb_gquotino != NULLFSINO) {
if (xfs_iget(mp, NULL, mp->m_sb.sb_gquotino,
0, 0, &gip) == 0)
tempgqip = true;
}
if (!pip && mp->m_sb.sb_pquotino != NULLFSINO) {
if (xfs_iget(mp, NULL, mp->m_sb.sb_pquotino,
0, 0, &pip) == 0)
temppqip = true;
}
if (uip) {
out->qs_uquota.qfs_nblks = uip->i_d.di_nblocks;
out->qs_uquota.qfs_nextents = uip->i_d.di_nextents;
if (tempuqip)
IRELE(uip);
}
if (gip) {
out->qs_gquota.qfs_nblks = gip->i_d.di_nblocks;
out->qs_gquota.qfs_nextents = gip->i_d.di_nextents;
if (tempgqip)
IRELE(gip);
}
if (pip) {
out->qs_pquota.qfs_nblks = pip->i_d.di_nblocks;
out->qs_pquota.qfs_nextents = pip->i_d.di_nextents;
if (temppqip)
IRELE(pip);
}
out->qs_incoredqs = q->qi_dquots;
out->qs_btimelimit = q->qi_btimelimit;
out->qs_itimelimit = q->qi_itimelimit;
out->qs_rtbtimelimit = q->qi_rtbtimelimit;
out->qs_bwarnlimit = q->qi_bwarnlimit;
out->qs_iwarnlimit = q->qi_iwarnlimit;
return 0;
}
#define XFS_QC_MASK \
(QC_LIMIT_MASK | QC_TIMER_MASK | QC_WARNS_MASK)
......@@ -873,28 +719,6 @@ xfs_qm_scall_getquota(
return error;
}
STATIC uint
xfs_qm_export_flags(
uint flags)
{
uint uflags;
uflags = 0;
if (flags & XFS_UQUOTA_ACCT)
uflags |= FS_QUOTA_UDQ_ACCT;
if (flags & XFS_GQUOTA_ACCT)
uflags |= FS_QUOTA_GDQ_ACCT;
if (flags & XFS_PQUOTA_ACCT)
uflags |= FS_QUOTA_PDQ_ACCT;
if (flags & XFS_UQUOTA_ENFD)
uflags |= FS_QUOTA_UDQ_ENFD;
if (flags & XFS_GQUOTA_ENFD)
uflags |= FS_QUOTA_GDQ_ENFD;
if (flags & XFS_PQUOTA_ENFD)
uflags |= FS_QUOTA_PDQ_ENFD;
return uflags;
}
STATIC int
xfs_dqrele_inode(
......
......@@ -23,10 +23,81 @@
#include "xfs_inode.h"
#include "xfs_quota.h"
#include "xfs_trans.h"
#include "xfs_trace.h"
#include "xfs_icache.h"
#include "xfs_qm.h"
#include <linux/quota.h>
static void
xfs_qm_fill_state(
struct qc_type_state *tstate,
struct xfs_mount *mp,
struct xfs_inode *ip,
xfs_ino_t ino)
{
struct xfs_quotainfo *q = mp->m_quotainfo;
bool tempqip = false;
tstate->ino = ino;
if (!ip && ino == NULLFSINO)
return;
if (!ip) {
if (xfs_iget(mp, NULL, ino, 0, 0, &ip))
return;
tempqip = true;
}
tstate->flags |= QCI_SYSFILE;
tstate->blocks = ip->i_d.di_nblocks;
tstate->nextents = ip->i_d.di_nextents;
tstate->spc_timelimit = q->qi_btimelimit;
tstate->ino_timelimit = q->qi_itimelimit;
tstate->rt_spc_timelimit = q->qi_rtbtimelimit;
tstate->spc_warnlimit = q->qi_bwarnlimit;
tstate->ino_warnlimit = q->qi_iwarnlimit;
tstate->rt_spc_warnlimit = q->qi_rtbwarnlimit;
if (tempqip)
IRELE(ip);
}
/*
* Return quota status information, such as enforcements, quota file inode
* numbers etc.
*/
static int
xfs_fs_get_quota_state(
struct super_block *sb,
struct qc_state *state)
{
struct xfs_mount *mp = XFS_M(sb);
struct xfs_quotainfo *q = mp->m_quotainfo;
memset(state, 0, sizeof(*state));
if (!XFS_IS_QUOTA_RUNNING(mp))
return 0;
state->s_incoredqs = q->qi_dquots;
if (XFS_IS_UQUOTA_RUNNING(mp))
state->s_state[USRQUOTA].flags |= QCI_ACCT_ENABLED;
if (XFS_IS_UQUOTA_ENFORCED(mp))
state->s_state[USRQUOTA].flags |= QCI_LIMITS_ENFORCED;
if (XFS_IS_GQUOTA_RUNNING(mp))
state->s_state[GRPQUOTA].flags |= QCI_ACCT_ENABLED;
if (XFS_IS_GQUOTA_ENFORCED(mp))
state->s_state[GRPQUOTA].flags |= QCI_LIMITS_ENFORCED;
if (XFS_IS_PQUOTA_RUNNING(mp))
state->s_state[PRJQUOTA].flags |= QCI_ACCT_ENABLED;
if (XFS_IS_PQUOTA_ENFORCED(mp))
state->s_state[PRJQUOTA].flags |= QCI_LIMITS_ENFORCED;
xfs_qm_fill_state(&state->s_state[USRQUOTA], mp, q->qi_uquotaip,
mp->m_sb.sb_uquotino);
xfs_qm_fill_state(&state->s_state[GRPQUOTA], mp, q->qi_gquotaip,
mp->m_sb.sb_gquotino);
xfs_qm_fill_state(&state->s_state[PRJQUOTA], mp, q->qi_pquotaip,
mp->m_sb.sb_pquotino);
return 0;
}
STATIC int
xfs_quota_type(int type)
{
......@@ -40,28 +111,40 @@ xfs_quota_type(int type)
}
}
STATIC int
xfs_fs_get_xstate(
#define XFS_QC_SETINFO_MASK (QC_TIMER_MASK | QC_WARNS_MASK)
/*
* Adjust quota timers & warnings
*/
static int
xfs_fs_set_info(
struct super_block *sb,
struct fs_quota_stat *fqs)
int type,
struct qc_info *info)
{
struct xfs_mount *mp = XFS_M(sb);
struct xfs_mount *mp = XFS_M(sb);
struct qc_dqblk newlim;
if (sb->s_flags & MS_RDONLY)
return -EROFS;
if (!XFS_IS_QUOTA_RUNNING(mp))
return -ENOSYS;
return xfs_qm_scall_getqstat(mp, fqs);
}
if (!XFS_IS_QUOTA_ON(mp))
return -ESRCH;
if (info->i_fieldmask & ~XFS_QC_SETINFO_MASK)
return -EINVAL;
if ((info->i_fieldmask & XFS_QC_SETINFO_MASK) == 0)
return 0;
STATIC int
xfs_fs_get_xstatev(
struct super_block *sb,
struct fs_quota_statv *fqs)
{
struct xfs_mount *mp = XFS_M(sb);
newlim.d_fieldmask = info->i_fieldmask;
newlim.d_spc_timer = info->i_spc_timelimit;
newlim.d_ino_timer = info->i_ino_timelimit;
newlim.d_rt_spc_timer = info->i_rt_spc_timelimit;
newlim.d_ino_warns = info->i_ino_warnlimit;
newlim.d_spc_warns = info->i_spc_warnlimit;
newlim.d_rt_spc_warns = info->i_rt_spc_warnlimit;
if (!XFS_IS_QUOTA_RUNNING(mp))
return -ENOSYS;
return xfs_qm_scall_getqstatv(mp, fqs);
return xfs_qm_scall_setqlim(mp, 0, xfs_quota_type(type), &newlim);
}
static unsigned int
......@@ -178,8 +261,8 @@ xfs_fs_set_dqblk(
}
const struct quotactl_ops xfs_quotactl_operations = {
.get_xstatev = xfs_fs_get_xstatev,
.get_xstate = xfs_fs_get_xstate,
.get_state = xfs_fs_get_quota_state,
.set_info = xfs_fs_set_info,
.quota_enable = xfs_quota_enable,
.quota_disable = xfs_quota_disable,
.rm_xquota = xfs_fs_rm_xquota,
......
......@@ -344,7 +344,10 @@ struct qc_dqblk {
int d_rt_spc_warns; /* # warnings issued wrt RT space */
};
/* Field specifiers for ->set_dqblk() in struct qc_dqblk */
/*
* Field specifiers for ->set_dqblk() in struct qc_dqblk and also for
* ->set_info() in struct qc_info
*/
#define QC_INO_SOFT (1<<0)
#define QC_INO_HARD (1<<1)
#define QC_SPC_SOFT (1<<2)
......@@ -365,6 +368,51 @@ struct qc_dqblk {
#define QC_INO_COUNT (1<<13)
#define QC_RT_SPACE (1<<14)
#define QC_ACCT_MASK (QC_SPACE | QC_INO_COUNT | QC_RT_SPACE)
#define QC_FLAGS (1<<15)
#define QCI_SYSFILE (1 << 0) /* Quota file is hidden from userspace */
#define QCI_ROOT_SQUASH (1 << 1) /* Root squash turned on */
#define QCI_ACCT_ENABLED (1 << 2) /* Quota accounting enabled */
#define QCI_LIMITS_ENFORCED (1 << 3) /* Quota limits enforced */
/* Structures for communicating via ->get_state */
struct qc_type_state {
unsigned int flags; /* Flags QCI_* */
unsigned int spc_timelimit; /* Time after which space softlimit is
* enforced */
unsigned int ino_timelimit; /* Ditto for inode softlimit */
unsigned int rt_spc_timelimit; /* Ditto for real-time space */
unsigned int spc_warnlimit; /* Limit for number of space warnings */
unsigned int ino_warnlimit; /* Ditto for inodes */
unsigned int rt_spc_warnlimit; /* Ditto for real-time space */
unsigned long long ino; /* Inode number of quota file */
blkcnt_t blocks; /* Number of 512-byte blocks in the file */
blkcnt_t nextents; /* Number of extents in the file */
};
struct qc_state {
unsigned int s_incoredqs; /* Number of dquots in core */
/*
* Per quota type information. The array should really have
* max(MAXQUOTAS, XQM_MAXQUOTAS) entries. BUILD_BUG_ON in
* quota_getinfo() makes sure XQM_MAXQUOTAS is large enough. Once VFS
* supports project quotas, this can be changed to MAXQUOTAS
*/
struct qc_type_state s_state[XQM_MAXQUOTAS];
};
/* Structure for communicating via ->set_info */
struct qc_info {
int i_fieldmask; /* mask of fields to change in ->set_info() */
unsigned int i_flags; /* Flags QCI_* */
unsigned int i_spc_timelimit; /* Time after which space softlimit is
* enforced */
unsigned int i_ino_timelimit; /* Ditto for inode softlimit */
unsigned int i_rt_spc_timelimit;/* Ditto for real-time space */
unsigned int i_spc_warnlimit; /* Limit for number of space warnings */
unsigned int i_ino_warnlimit; /* Limit for number of inode warnings */
unsigned int i_rt_spc_warnlimit; /* Ditto for real-time space */
};
/* Operations handling requests from userspace */
struct quotactl_ops {
......@@ -373,12 +421,10 @@ struct quotactl_ops {
int (*quota_enable)(struct super_block *, unsigned int);
int (*quota_disable)(struct super_block *, unsigned int);
int (*quota_sync)(struct super_block *, int);
int (*get_info)(struct super_block *, int, struct if_dqinfo *);
int (*set_info)(struct super_block *, int, struct if_dqinfo *);
int (*set_info)(struct super_block *, int, struct qc_info *);
int (*get_dqblk)(struct super_block *, struct kqid, struct qc_dqblk *);
int (*set_dqblk)(struct super_block *, struct kqid, struct qc_dqblk *);
int (*get_xstate)(struct super_block *, struct fs_quota_stat *);
int (*get_xstatev)(struct super_block *, struct fs_quota_statv *);
int (*get_state)(struct super_block *, struct qc_state *);
int (*rm_xquota)(struct super_block *, unsigned int);
};
......
......@@ -95,8 +95,8 @@ int dquot_quota_on_mount(struct super_block *sb, char *qf_name,
int dquot_quota_off(struct super_block *sb, int type);
int dquot_writeback_dquots(struct super_block *sb, int type);
int dquot_quota_sync(struct super_block *sb, int type);
int dquot_get_dqinfo(struct super_block *sb, int type, struct if_dqinfo *ii);
int dquot_set_dqinfo(struct super_block *sb, int type, struct if_dqinfo *ii);
int dquot_get_state(struct super_block *sb, struct qc_state *state);
int dquot_set_dqinfo(struct super_block *sb, int type, struct qc_info *ii);
int dquot_get_dqblk(struct super_block *sb, struct kqid id,
struct qc_dqblk *di);
int dquot_set_dqblk(struct super_block *sb, struct kqid id,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment