// SPDX-License-Identifier: GPL-2.0 /* * Quota code necessary even when VFS quota support is not compiled * into the kernel. The interesting stuff is over in dquot.c, here * we have symbols for initial quotactl(2) handling, the sysctl(2) * variables, etc - things needed even when quota support disabled.
*/
staticint check_quotactl_permission(struct super_block *sb, int type, int cmd,
qid_t id)
{ switch (cmd) { /* these commands do not require any special privilegues */ case Q_GETFMT: case Q_SYNC: case Q_GETINFO: case Q_XGETQSTAT: case Q_XGETQSTATV: case Q_XQUOTASYNC: break; /* allow to query information for dquots we "own" */ case Q_GETQUOTA: case Q_XGETQUOTA: if ((type == USRQUOTA && uid_eq(current_euid(), make_kuid(current_user_ns(), id))) ||
(type == GRPQUOTA && in_egroup_p(make_kgid(current_user_ns(), id)))) break;
fallthrough; default: if (!capable(CAP_SYS_ADMIN)) return -EPERM;
}
return security_quotactl(cmd, type, id, sb);
}
staticvoid quota_sync_one(struct super_block *sb, void *arg)
{ int type = *(int *)arg;
if (!sb->s_qcop->get_dqblk) return -ENOSYS;
qid = make_kqid(current_user_ns(), type, id); if (!qid_has_mapping(sb->s_user_ns, qid)) return -EINVAL;
ret = sb->s_qcop->get_dqblk(sb, qid, &fdq); if (ret) return ret;
copy_to_if_dqblk(&idq, &fdq);
if (compat_need_64bit_alignment_fixup()) { struct compat_if_dqblk __user *compat_dqblk = addr;
if (copy_to_user(compat_dqblk, &idq, sizeof(*compat_dqblk))) return -EFAULT; if (put_user(idq.dqb_valid, &compat_dqblk->dqb_valid)) return -EFAULT;
} else { if (copy_to_user(addr, &idq, sizeof(idq))) return -EFAULT;
} return 0;
}
/* * Return quota for next active quota >= this id, if any exists, * otherwise return -ENOENT via ->get_nextdqblk
*/ staticint quota_getnextquota(struct super_block *sb, int type, qid_t id, void __user *addr)
{ struct kqid qid; struct qc_dqblk fdq; struct if_nextdqblk idq; int ret;
if (!sb->s_qcop->get_nextdqblk) return -ENOSYS;
qid = make_kqid(current_user_ns(), type, id); if (!qid_has_mapping(sb->s_user_ns, qid)) return -EINVAL;
ret = sb->s_qcop->get_nextdqblk(sb, &qid, &fdq); if (ret) return ret; /* struct if_nextdqblk is a superset of struct if_dqblk */
copy_to_if_dqblk((struct if_dqblk *)&idq, &fdq);
idq.dqb_id = from_kqid(current_user_ns(), qid); if (copy_to_user(addr, &idq, sizeof(idq))) return -EFAULT; return 0;
}
/* Inodes may be allocated even if inactive; copy out if present */ if (state.s_state[USRQUOTA].ino) {
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].ino) {
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].ino) { /* * 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;
}
/* Inodes may be allocated even if inactive; copy out if present */ if (state.s_state[USRQUOTA].ino) {
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].ino) {
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].ino) {
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;
}
staticint quota_getxstatev(struct super_block *sb, int type, void __user *addr)
{ struct fs_quota_statv fqs; int ret;
if (!sb->s_qcop->get_state) return -ENOSYS;
memset(&fqs, 0, sizeof(fqs)); if (copy_from_user(&fqs, addr, 1)) /* Just read qs_version */ return -EFAULT;
/* If this kernel doesn't support user specified version, fail */ switch (fqs.qs_version) { case FS_QSTATV_VERSION1: break; default: return -EINVAL;
}
ret = quota_getstatev(sb, type, &fqs); if (!ret && copy_to_user(addr, &fqs, sizeof(fqs))) return -EFAULT; return ret;
}
/* * XFS defines BBTOB and BTOBB macros inside fs/xfs/ and we cannot move them * out of there as xfsprogs rely on definitions being in that header file. So * just define same functions here for quota purposes.
*/ #define XFS_BB_SHIFT 9
if (copy_from_user(&flags, addr, sizeof(flags))) return -EFAULT; if (!sb->s_qcop->rm_xquota) return -ENOSYS; return sb->s_qcop->rm_xquota(sb, flags);
}
/* Copy parameters and call proper function */ staticint do_quotactl(struct super_block *sb, int type, int cmd, qid_t id, void __user *addr, conststruct path *path)
{ int ret;
type = array_index_nospec(type, MAXQUOTAS); /* * Quota not supported on this fs? Check this before s_quota_types * since they needn't be set if quota is not supported at all.
*/ if (!sb->s_qcop) return -ENOSYS; if (!(sb->s_quota_types & (1 << type))) return -EINVAL;
ret = check_quotactl_permission(sb, type, cmd, id); if (ret < 0) return ret;
switch (cmd) { case Q_QUOTAON: return quota_quotaon(sb, type, id, path); case Q_QUOTAOFF: return quota_quotaoff(sb, type); case Q_GETFMT: return quota_getfmt(sb, type, addr); case Q_GETINFO: return quota_getinfo(sb, type, addr); case Q_SETINFO: return quota_setinfo(sb, type, addr); case Q_GETQUOTA: return quota_getquota(sb, type, id, addr); case Q_GETNEXTQUOTA: return quota_getnextquota(sb, type, id, addr); case Q_SETQUOTA: return quota_setquota(sb, type, id, addr); case Q_SYNC: if (!sb->s_qcop->quota_sync) return -ENOSYS; return sb->s_qcop->quota_sync(sb, type); case Q_XQUOTAON: return quota_enable(sb, addr); case Q_XQUOTAOFF: return quota_disable(sb, addr); case Q_XQUOTARM: return quota_rmxquota(sb, addr); case Q_XGETQSTAT: return quota_getxstate(sb, type, addr); case Q_XGETQSTATV: return quota_getxstatev(sb, type, addr); case Q_XSETQLIM: return quota_setxquota(sb, type, id, addr); case Q_XGETQUOTA: return quota_getxquota(sb, type, id, addr); case Q_XGETNEXTQUOTA: return quota_getnextxquota(sb, type, id, addr); case Q_XQUOTASYNC: if (sb_rdonly(sb)) return -EROFS; /* XFS quotas are fully coherent now, making this call a noop */ return 0; default: return -EINVAL;
}
}
/* Return 1 if 'cmd' will block on frozen filesystem */ staticint quotactl_cmd_write(int cmd)
{ /* * We cannot allow Q_GETQUOTA and Q_GETNEXTQUOTA without write access * as dquot_acquire() may allocate space for new structure and OCFS2 * needs to increment on-disk use count.
*/ switch (cmd) { case Q_GETFMT: case Q_GETINFO: case Q_SYNC: case Q_XGETQSTAT: case Q_XGETQSTATV: case Q_XGETQUOTA: case Q_XGETNEXTQUOTA: case Q_XQUOTASYNC: return 0;
} return 1;
}
/* * look up a superblock on which quota ops will be performed * - use the name of a block device to find the superblock thereon
*/ staticstruct super_block *quotactl_block(constchar __user *special, int cmd)
{ #ifdef CONFIG_BLOCK struct super_block *sb; struct filename *tmp = getname(special); bool excl = false, thawed = false; int error;
dev_t dev;
if (IS_ERR(tmp)) return ERR_CAST(tmp);
error = lookup_bdev(tmp->name, &dev);
putname(tmp); if (error) return ERR_PTR(error);
retry:
sb = user_get_super(dev, excl); if (!sb) return ERR_PTR(-ENODEV); if (thawed && sb->s_writers.frozen != SB_UNFROZEN) { if (excl)
up_write(&sb->s_umount); else
up_read(&sb->s_umount); /* Wait for sb to unfreeze */
sb_start_write(sb);
sb_end_write(sb);
put_super(sb); goto retry;
} return sb;
#else return ERR_PTR(-ENODEV); #endif
}
/* * 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.
*/
SYSCALL_DEFINE4(quotactl, unsignedint, cmd, constchar __user *, special,
qid_t, id, void __user *, addr)
{
uint cmds, type; struct super_block *sb = NULL; struct path path, *pathp = NULL; int ret;
cmds = cmd >> SUBCMDSHIFT;
type = cmd & SUBCMDMASK;
if (type >= MAXQUOTAS) return -EINVAL;
/* * As a special case Q_SYNC can be called without a specific device. * It will iterate all superblocks that have quota enabled and call * the sync action on each of them.
*/ if (!special) { if (cmds == Q_SYNC) return quota_sync_all(type); return -ENODEV;
}
/* * Path for quotaon has to be resolved before grabbing superblock * because that gets s_umount sem which is also possibly needed by path * resolution (think about autofs) and thus deadlocks could arise.
*/ if (cmds == Q_QUOTAON) {
ret = user_path_at(AT_FDCWD, addr, LOOKUP_FOLLOW|LOOKUP_AUTOMOUNT, &path); if (ret)
pathp = ERR_PTR(ret); else
pathp = &path;
}
sb = quotactl_block(special, cmds); if (IS_ERR(sb)) {
ret = PTR_ERR(sb); goto out;
}
ret = do_quotactl(sb, type, cmds, id, addr, pathp);
if (!quotactl_cmd_onoff(cmds))
drop_super(sb); else
drop_super_exclusive(sb);
out: if (pathp && !IS_ERR(pathp))
path_put(pathp); return ret;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.