Skip to content

Commit 04a9157

Browse files
committed
ext4: implemet new ioctls to set and get superblock parameters
Implement the EXT4_IOC_GET_TUNE_SB_PARAM and EXT4_IOC_SET_TUNE_SB_PARAM ioctls, which allow certains superblock parameters to be set while the file system is mounted, without needing write access to the block device. Reviewed-by: Darrick J. Wong <[email protected]> Reviewed-by: Jan Kara <[email protected]> Signed-off-by: Theodore Ts'o <[email protected]> Message-ID: <[email protected]> Signed-off-by: Theodore Ts'o <[email protected]>
1 parent 12c84dd commit 04a9157

File tree

2 files changed

+358
-7
lines changed

2 files changed

+358
-7
lines changed

fs/ext4/ioctl.c

Lines changed: 305 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,16 @@
2727
#include "fsmap.h"
2828
#include <trace/events/ext4.h>
2929

30-
typedef void ext4_update_sb_callback(struct ext4_super_block *es,
31-
const void *arg);
30+
typedef void ext4_update_sb_callback(struct ext4_sb_info *sbi,
31+
struct ext4_super_block *es,
32+
const void *arg);
3233

3334
/*
3435
* Superblock modification callback function for changing file system
3536
* label
3637
*/
37-
static void ext4_sb_setlabel(struct ext4_super_block *es, const void *arg)
38+
static void ext4_sb_setlabel(struct ext4_sb_info *sbi,
39+
struct ext4_super_block *es, const void *arg)
3840
{
3941
/* Sanity check, this should never happen */
4042
BUILD_BUG_ON(sizeof(es->s_volume_name) < EXT4_LABEL_MAX);
@@ -46,7 +48,8 @@ static void ext4_sb_setlabel(struct ext4_super_block *es, const void *arg)
4648
* Superblock modification callback function for changing file system
4749
* UUID.
4850
*/
49-
static void ext4_sb_setuuid(struct ext4_super_block *es, const void *arg)
51+
static void ext4_sb_setuuid(struct ext4_sb_info *sbi,
52+
struct ext4_super_block *es, const void *arg)
5053
{
5154
memcpy(es->s_uuid, (__u8 *)arg, UUID_SIZE);
5255
}
@@ -71,7 +74,7 @@ int ext4_update_primary_sb(struct super_block *sb, handle_t *handle,
7174
goto out_err;
7275

7376
lock_buffer(bh);
74-
func(es, arg);
77+
func(sbi, es, arg);
7578
ext4_superblock_csum_set(sb);
7679
unlock_buffer(bh);
7780

@@ -149,7 +152,7 @@ static int ext4_update_backup_sb(struct super_block *sb,
149152
unlock_buffer(bh);
150153
goto out_bh;
151154
}
152-
func(es, arg);
155+
func(EXT4_SB(sb), es, arg);
153156
if (ext4_has_feature_metadata_csum(sb))
154157
es->s_checksum = ext4_superblock_csum(es);
155158
set_buffer_uptodate(bh);
@@ -1230,6 +1233,295 @@ static int ext4_ioctl_setuuid(struct file *filp,
12301233
return ret;
12311234
}
12321235

1236+
1237+
#define TUNE_OPS_SUPPORTED (EXT4_TUNE_FL_ERRORS_BEHAVIOR | \
1238+
EXT4_TUNE_FL_MNT_COUNT | EXT4_TUNE_FL_MAX_MNT_COUNT | \
1239+
EXT4_TUNE_FL_CHECKINTRVAL | EXT4_TUNE_FL_LAST_CHECK_TIME | \
1240+
EXT4_TUNE_FL_RESERVED_BLOCKS | EXT4_TUNE_FL_RESERVED_UID | \
1241+
EXT4_TUNE_FL_RESERVED_GID | EXT4_TUNE_FL_DEFAULT_MNT_OPTS | \
1242+
EXT4_TUNE_FL_DEF_HASH_ALG | EXT4_TUNE_FL_RAID_STRIDE | \
1243+
EXT4_TUNE_FL_RAID_STRIPE_WIDTH | EXT4_TUNE_FL_MOUNT_OPTS | \
1244+
EXT4_TUNE_FL_FEATURES | EXT4_TUNE_FL_EDIT_FEATURES | \
1245+
EXT4_TUNE_FL_FORCE_FSCK | EXT4_TUNE_FL_ENCODING | \
1246+
EXT4_TUNE_FL_ENCODING_FLAGS)
1247+
1248+
#define EXT4_TUNE_SET_COMPAT_SUPP \
1249+
(EXT4_FEATURE_COMPAT_DIR_INDEX | \
1250+
EXT4_FEATURE_COMPAT_STABLE_INODES)
1251+
#define EXT4_TUNE_SET_INCOMPAT_SUPP \
1252+
(EXT4_FEATURE_INCOMPAT_EXTENTS | \
1253+
EXT4_FEATURE_INCOMPAT_EA_INODE | \
1254+
EXT4_FEATURE_INCOMPAT_ENCRYPT | \
1255+
EXT4_FEATURE_INCOMPAT_CSUM_SEED | \
1256+
EXT4_FEATURE_INCOMPAT_LARGEDIR | \
1257+
EXT4_FEATURE_INCOMPAT_CASEFOLD)
1258+
#define EXT4_TUNE_SET_RO_COMPAT_SUPP \
1259+
(EXT4_FEATURE_RO_COMPAT_LARGE_FILE | \
1260+
EXT4_FEATURE_RO_COMPAT_DIR_NLINK | \
1261+
EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE | \
1262+
EXT4_FEATURE_RO_COMPAT_PROJECT | \
1263+
EXT4_FEATURE_RO_COMPAT_VERITY)
1264+
1265+
#define EXT4_TUNE_CLEAR_COMPAT_SUPP (0)
1266+
#define EXT4_TUNE_CLEAR_INCOMPAT_SUPP (0)
1267+
#define EXT4_TUNE_CLEAR_RO_COMPAT_SUPP (0)
1268+
1269+
#define SB_ENC_SUPP_MASK (SB_ENC_STRICT_MODE_FL | \
1270+
SB_ENC_NO_COMPAT_FALLBACK_FL)
1271+
1272+
static int ext4_ioctl_get_tune_sb(struct ext4_sb_info *sbi,
1273+
struct ext4_tune_sb_params __user *params)
1274+
{
1275+
struct ext4_tune_sb_params ret;
1276+
struct ext4_super_block *es = sbi->s_es;
1277+
1278+
memset(&ret, 0, sizeof(ret));
1279+
ret.set_flags = TUNE_OPS_SUPPORTED;
1280+
ret.errors_behavior = le16_to_cpu(es->s_errors);
1281+
ret.mnt_count = le16_to_cpu(es->s_mnt_count);
1282+
ret.max_mnt_count = le16_to_cpu(es->s_max_mnt_count);
1283+
ret.checkinterval = le32_to_cpu(es->s_checkinterval);
1284+
ret.last_check_time = le32_to_cpu(es->s_lastcheck);
1285+
ret.reserved_blocks = ext4_r_blocks_count(es);
1286+
ret.blocks_count = ext4_blocks_count(es);
1287+
ret.reserved_uid = ext4_get_resuid(es);
1288+
ret.reserved_gid = ext4_get_resgid(es);
1289+
ret.default_mnt_opts = le32_to_cpu(es->s_default_mount_opts);
1290+
ret.def_hash_alg = es->s_def_hash_version;
1291+
ret.raid_stride = le16_to_cpu(es->s_raid_stride);
1292+
ret.raid_stripe_width = le32_to_cpu(es->s_raid_stripe_width);
1293+
ret.encoding = le16_to_cpu(es->s_encoding);
1294+
ret.encoding_flags = le16_to_cpu(es->s_encoding_flags);
1295+
strscpy_pad(ret.mount_opts, es->s_mount_opts);
1296+
ret.feature_compat = le32_to_cpu(es->s_feature_compat);
1297+
ret.feature_incompat = le32_to_cpu(es->s_feature_incompat);
1298+
ret.feature_ro_compat = le32_to_cpu(es->s_feature_ro_compat);
1299+
ret.set_feature_compat_mask = EXT4_TUNE_SET_COMPAT_SUPP;
1300+
ret.set_feature_incompat_mask = EXT4_TUNE_SET_INCOMPAT_SUPP;
1301+
ret.set_feature_ro_compat_mask = EXT4_TUNE_SET_RO_COMPAT_SUPP;
1302+
ret.clear_feature_compat_mask = EXT4_TUNE_CLEAR_COMPAT_SUPP;
1303+
ret.clear_feature_incompat_mask = EXT4_TUNE_CLEAR_INCOMPAT_SUPP;
1304+
ret.clear_feature_ro_compat_mask = EXT4_TUNE_CLEAR_RO_COMPAT_SUPP;
1305+
if (copy_to_user(params, &ret, sizeof(ret)))
1306+
return -EFAULT;
1307+
return 0;
1308+
}
1309+
1310+
static void ext4_sb_setparams(struct ext4_sb_info *sbi,
1311+
struct ext4_super_block *es, const void *arg)
1312+
{
1313+
const struct ext4_tune_sb_params *params = arg;
1314+
1315+
if (params->set_flags & EXT4_TUNE_FL_ERRORS_BEHAVIOR)
1316+
es->s_errors = cpu_to_le16(params->errors_behavior);
1317+
if (params->set_flags & EXT4_TUNE_FL_MNT_COUNT)
1318+
es->s_mnt_count = cpu_to_le16(params->mnt_count);
1319+
if (params->set_flags & EXT4_TUNE_FL_MAX_MNT_COUNT)
1320+
es->s_max_mnt_count = cpu_to_le16(params->max_mnt_count);
1321+
if (params->set_flags & EXT4_TUNE_FL_CHECKINTRVAL)
1322+
es->s_checkinterval = cpu_to_le32(params->checkinterval);
1323+
if (params->set_flags & EXT4_TUNE_FL_LAST_CHECK_TIME)
1324+
es->s_lastcheck = cpu_to_le32(params->last_check_time);
1325+
if (params->set_flags & EXT4_TUNE_FL_RESERVED_BLOCKS) {
1326+
ext4_fsblk_t blk = params->reserved_blocks;
1327+
1328+
es->s_r_blocks_count_lo = cpu_to_le32((u32)blk);
1329+
es->s_r_blocks_count_hi = cpu_to_le32(blk >> 32);
1330+
}
1331+
if (params->set_flags & EXT4_TUNE_FL_RESERVED_UID) {
1332+
int uid = params->reserved_uid;
1333+
1334+
es->s_def_resuid = cpu_to_le16(uid & 0xFFFF);
1335+
es->s_def_resuid_hi = cpu_to_le16(uid >> 16);
1336+
}
1337+
if (params->set_flags & EXT4_TUNE_FL_RESERVED_GID) {
1338+
int gid = params->reserved_gid;
1339+
1340+
es->s_def_resgid = cpu_to_le16(gid & 0xFFFF);
1341+
es->s_def_resgid_hi = cpu_to_le16(gid >> 16);
1342+
}
1343+
if (params->set_flags & EXT4_TUNE_FL_DEFAULT_MNT_OPTS)
1344+
es->s_default_mount_opts = cpu_to_le32(params->default_mnt_opts);
1345+
if (params->set_flags & EXT4_TUNE_FL_DEF_HASH_ALG)
1346+
es->s_def_hash_version = params->def_hash_alg;
1347+
if (params->set_flags & EXT4_TUNE_FL_RAID_STRIDE)
1348+
es->s_raid_stride = cpu_to_le16(params->raid_stride);
1349+
if (params->set_flags & EXT4_TUNE_FL_RAID_STRIPE_WIDTH)
1350+
es->s_raid_stripe_width =
1351+
cpu_to_le32(params->raid_stripe_width);
1352+
if (params->set_flags & EXT4_TUNE_FL_ENCODING)
1353+
es->s_encoding = cpu_to_le16(params->encoding);
1354+
if (params->set_flags & EXT4_TUNE_FL_ENCODING_FLAGS)
1355+
es->s_encoding_flags = cpu_to_le16(params->encoding_flags);
1356+
strscpy_pad(es->s_mount_opts, params->mount_opts);
1357+
if (params->set_flags & EXT4_TUNE_FL_EDIT_FEATURES) {
1358+
es->s_feature_compat |=
1359+
cpu_to_le32(params->set_feature_compat_mask);
1360+
es->s_feature_incompat |=
1361+
cpu_to_le32(params->set_feature_incompat_mask);
1362+
es->s_feature_ro_compat |=
1363+
cpu_to_le32(params->set_feature_ro_compat_mask);
1364+
es->s_feature_compat &=
1365+
~cpu_to_le32(params->clear_feature_compat_mask);
1366+
es->s_feature_incompat &=
1367+
~cpu_to_le32(params->clear_feature_incompat_mask);
1368+
es->s_feature_ro_compat &=
1369+
~cpu_to_le32(params->clear_feature_ro_compat_mask);
1370+
if (params->set_feature_compat_mask &
1371+
EXT4_FEATURE_COMPAT_DIR_INDEX)
1372+
es->s_def_hash_version = sbi->s_def_hash_version;
1373+
if (params->set_feature_incompat_mask &
1374+
EXT4_FEATURE_INCOMPAT_CSUM_SEED)
1375+
es->s_checksum_seed = cpu_to_le32(sbi->s_csum_seed);
1376+
}
1377+
if (params->set_flags & EXT4_TUNE_FL_FORCE_FSCK)
1378+
es->s_state |= cpu_to_le16(EXT4_ERROR_FS);
1379+
}
1380+
1381+
static int ext4_ioctl_set_tune_sb(struct file *filp,
1382+
struct ext4_tune_sb_params __user *in)
1383+
{
1384+
struct ext4_tune_sb_params params;
1385+
struct super_block *sb = file_inode(filp)->i_sb;
1386+
struct ext4_sb_info *sbi = EXT4_SB(sb);
1387+
struct ext4_super_block *es = sbi->s_es;
1388+
int enabling_casefold = 0;
1389+
int ret;
1390+
1391+
if (!capable(CAP_SYS_ADMIN))
1392+
return -EPERM;
1393+
1394+
if (copy_from_user(&params, in, sizeof(params)))
1395+
return -EFAULT;
1396+
1397+
if ((params.set_flags & ~TUNE_OPS_SUPPORTED) != 0)
1398+
return -EOPNOTSUPP;
1399+
1400+
if ((params.set_flags & EXT4_TUNE_FL_ERRORS_BEHAVIOR) &&
1401+
(params.errors_behavior > EXT4_ERRORS_PANIC))
1402+
return -EINVAL;
1403+
1404+
if ((params.set_flags & EXT4_TUNE_FL_RESERVED_BLOCKS) &&
1405+
(params.reserved_blocks > ext4_blocks_count(sbi->s_es) / 2))
1406+
return -EINVAL;
1407+
if ((params.set_flags & EXT4_TUNE_FL_DEF_HASH_ALG) &&
1408+
((params.def_hash_alg > DX_HASH_LAST) ||
1409+
(params.def_hash_alg == DX_HASH_SIPHASH)))
1410+
return -EINVAL;
1411+
if ((params.set_flags & EXT4_TUNE_FL_FEATURES) &&
1412+
(params.set_flags & EXT4_TUNE_FL_EDIT_FEATURES))
1413+
return -EINVAL;
1414+
1415+
if (params.set_flags & EXT4_TUNE_FL_FEATURES) {
1416+
params.set_feature_compat_mask =
1417+
params.feature_compat &
1418+
~le32_to_cpu(es->s_feature_compat);
1419+
params.set_feature_incompat_mask =
1420+
params.feature_incompat &
1421+
~le32_to_cpu(es->s_feature_incompat);
1422+
params.set_feature_ro_compat_mask =
1423+
params.feature_ro_compat &
1424+
~le32_to_cpu(es->s_feature_ro_compat);
1425+
params.clear_feature_compat_mask =
1426+
~params.feature_compat &
1427+
le32_to_cpu(es->s_feature_compat);
1428+
params.clear_feature_incompat_mask =
1429+
~params.feature_incompat &
1430+
le32_to_cpu(es->s_feature_incompat);
1431+
params.clear_feature_ro_compat_mask =
1432+
~params.feature_ro_compat &
1433+
le32_to_cpu(es->s_feature_ro_compat);
1434+
params.set_flags |= EXT4_TUNE_FL_EDIT_FEATURES;
1435+
}
1436+
if (params.set_flags & EXT4_TUNE_FL_EDIT_FEATURES) {
1437+
if ((params.set_feature_compat_mask &
1438+
~EXT4_TUNE_SET_COMPAT_SUPP) ||
1439+
(params.set_feature_incompat_mask &
1440+
~EXT4_TUNE_SET_INCOMPAT_SUPP) ||
1441+
(params.set_feature_ro_compat_mask &
1442+
~EXT4_TUNE_SET_RO_COMPAT_SUPP) ||
1443+
(params.clear_feature_compat_mask &
1444+
~EXT4_TUNE_CLEAR_COMPAT_SUPP) ||
1445+
(params.clear_feature_incompat_mask &
1446+
~EXT4_TUNE_CLEAR_INCOMPAT_SUPP) ||
1447+
(params.clear_feature_ro_compat_mask &
1448+
~EXT4_TUNE_CLEAR_RO_COMPAT_SUPP))
1449+
return -EOPNOTSUPP;
1450+
1451+
/*
1452+
* Filter out the features that are already set from
1453+
* the set_mask.
1454+
*/
1455+
params.set_feature_compat_mask &=
1456+
~le32_to_cpu(es->s_feature_compat);
1457+
params.set_feature_incompat_mask &=
1458+
~le32_to_cpu(es->s_feature_incompat);
1459+
params.set_feature_ro_compat_mask &=
1460+
~le32_to_cpu(es->s_feature_ro_compat);
1461+
if ((params.set_feature_incompat_mask &
1462+
EXT4_FEATURE_INCOMPAT_CASEFOLD)) {
1463+
enabling_casefold = 1;
1464+
if (!(params.set_flags & EXT4_TUNE_FL_ENCODING)) {
1465+
params.encoding = EXT4_ENC_UTF8_12_1;
1466+
params.set_flags |= EXT4_TUNE_FL_ENCODING;
1467+
}
1468+
if (!(params.set_flags & EXT4_TUNE_FL_ENCODING_FLAGS)) {
1469+
params.encoding_flags = 0;
1470+
params.set_flags |= EXT4_TUNE_FL_ENCODING_FLAGS;
1471+
}
1472+
}
1473+
if ((params.set_feature_compat_mask &
1474+
EXT4_FEATURE_COMPAT_DIR_INDEX)) {
1475+
uuid_t uu;
1476+
1477+
memcpy(&uu, sbi->s_hash_seed, UUID_SIZE);
1478+
if (uuid_is_null(&uu))
1479+
generate_random_uuid((char *)
1480+
&sbi->s_hash_seed);
1481+
if (params.set_flags & EXT4_TUNE_FL_DEF_HASH_ALG)
1482+
sbi->s_def_hash_version = params.def_hash_alg;
1483+
else if (sbi->s_def_hash_version == 0)
1484+
sbi->s_def_hash_version = DX_HASH_HALF_MD4;
1485+
if (!(es->s_flags &
1486+
cpu_to_le32(EXT2_FLAGS_UNSIGNED_HASH)) &&
1487+
!(es->s_flags &
1488+
cpu_to_le32(EXT2_FLAGS_SIGNED_HASH))) {
1489+
#ifdef __CHAR_UNSIGNED__
1490+
sbi->s_hash_unsigned = 3;
1491+
#else
1492+
sbi->s_hash_unsigned = 0;
1493+
#endif
1494+
}
1495+
}
1496+
}
1497+
if (params.set_flags & EXT4_TUNE_FL_ENCODING) {
1498+
if (!enabling_casefold)
1499+
return -EINVAL;
1500+
if (params.encoding == 0)
1501+
params.encoding = EXT4_ENC_UTF8_12_1;
1502+
else if (params.encoding != EXT4_ENC_UTF8_12_1)
1503+
return -EINVAL;
1504+
}
1505+
if (params.set_flags & EXT4_TUNE_FL_ENCODING_FLAGS) {
1506+
if (!enabling_casefold)
1507+
return -EINVAL;
1508+
if (params.encoding_flags & ~SB_ENC_SUPP_MASK)
1509+
return -EINVAL;
1510+
}
1511+
1512+
ret = mnt_want_write_file(filp);
1513+
if (ret)
1514+
return ret;
1515+
1516+
ret = ext4_update_superblocks_fn(sb, ext4_sb_setparams, &params);
1517+
mnt_drop_write_file(filp);
1518+
1519+
if (params.set_flags & EXT4_TUNE_FL_DEF_HASH_ALG)
1520+
sbi->s_def_hash_version = params.def_hash_alg;
1521+
1522+
return ret;
1523+
}
1524+
12331525
static long __ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
12341526
{
12351527
struct inode *inode = file_inode(filp);
@@ -1616,6 +1908,11 @@ static long __ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
16161908
return ext4_ioctl_getuuid(EXT4_SB(sb), (void __user *)arg);
16171909
case EXT4_IOC_SETFSUUID:
16181910
return ext4_ioctl_setuuid(filp, (const void __user *)arg);
1911+
case EXT4_IOC_GET_TUNE_SB_PARAM:
1912+
return ext4_ioctl_get_tune_sb(EXT4_SB(sb),
1913+
(void __user *)arg);
1914+
case EXT4_IOC_SET_TUNE_SB_PARAM:
1915+
return ext4_ioctl_set_tune_sb(filp, (void __user *)arg);
16191916
default:
16201917
return -ENOTTY;
16211918
}
@@ -1703,7 +2000,8 @@ long ext4_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
17032000
}
17042001
#endif
17052002

1706-
static void set_overhead(struct ext4_super_block *es, const void *arg)
2003+
static void set_overhead(struct ext4_sb_info *sbi,
2004+
struct ext4_super_block *es, const void *arg)
17072005
{
17082006
es->s_overhead_clusters = cpu_to_le32(*((unsigned long *) arg));
17092007
}

0 commit comments

Comments
 (0)