Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

Merge branch 'quota_interface' into for_next_testing

parents 1be440de c39fb53b
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -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
+1 −1
Original line number Diff line number Diff line
@@ -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
+15 −13
Original line number Diff line number Diff line
@@ -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,
};
+43 −25
Original line number Diff line number Diff line
@@ -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);
	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;
		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);
	}
	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
+208 −9
Original line number Diff line number Diff line
@@ -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);
}
Loading