Skip to content

Commit af2ee52

Browse files
committed
Avoid double parsing of mount options
Originally all mount options were parsed inside apfs_fill_super(), because I was following the example of other filesystems (such as ext2) that have no subvolumes or snapshots. When I implemented those features, I realized that some mount options would be needed early on mount, so that sget() could use them to decide if the superblock is already mounted. My lazy solution at the time was to leave parse_options() as it was, but also add some redundant code early on mount to read the needed options. Just looking at the amount of TODOs in those functions makes it clear that this was always very silly. And now that the locking during mount has been simplified, this is causing a race, and I want to take that stuff seriously. The bug is that while parse_options() rereads the mount options needed by sget(), there is nothing preventing another concurrent mount from testing the superblock. Say the first mount is for volume 1: the volume number gets briefly reset to the default value of zero, and if the other mount actually wants volume 0, sget() will consider this a match. Anyway, split parse_options() into two equally clean functions, one for early mount and another to get called within apfs_fill_super(). I even considered simply parsing all the options early during mount, but that would require a few more changes because the nxi is not attached at that point. Maybe some other time. Signed-off-by: Ernesto A. Fernández <[email protected]>
1 parent 44f4b4f commit af2ee52

File tree

1 file changed

+70
-64
lines changed

1 file changed

+70
-64
lines changed

super.c

Lines changed: 70 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -1167,59 +1167,6 @@ static void apfs_set_nx_flags(struct super_block *sb, unsigned int flags)
11671167
mutex_unlock(&nxs_mutex);
11681168
}
11691169

1170-
/**
1171-
* apfs_get_vol_number - Retrieve the volume number from the mount options
1172-
* @options: string of mount options
1173-
*
1174-
* On error, it will just return the default volume 0.
1175-
*/
1176-
static unsigned int apfs_get_vol_number(char *options)
1177-
{
1178-
char needle[] = "vol=";
1179-
char *volstr;
1180-
long vol;
1181-
1182-
if (!options)
1183-
return 0;
1184-
1185-
/* TODO: just parse all the options once... */
1186-
volstr = strstr(options, needle);
1187-
if (!volstr)
1188-
return 0;
1189-
volstr += sizeof(needle) - 1;
1190-
1191-
/* TODO: other bases? */
1192-
if (kstrtol(volstr, 10, &vol) < 0)
1193-
return 0;
1194-
return vol;
1195-
}
1196-
1197-
/**
1198-
* apfs_get_snap_name - Duplicate the snapshot label from the mount options
1199-
* @options: string of mount options
1200-
*
1201-
* On error, it will just return the default NULL snapshot name. TODO: this is
1202-
* actually a bit dangerous because a memory allocation failure might get the
1203-
* same snapshot mounted twice, without a shared superblock.
1204-
*/
1205-
static char *apfs_get_snap_name(char *options)
1206-
{
1207-
char needle[] = "snap=";
1208-
char *name = NULL, *end = NULL;
1209-
1210-
if (!options)
1211-
return NULL;
1212-
1213-
name = strstr(options, needle);
1214-
if (!name)
1215-
return NULL;
1216-
1217-
name += sizeof(needle) - 1;
1218-
end = strchrnul(name, ',');
1219-
1220-
return kmemdup_nul(name, end - name, GFP_KERNEL);
1221-
}
1222-
12231170
/*
12241171
* Many of the parse_options() functions in other file systems return 0
12251172
* on error. This one returns an error code, and 0 on success.
@@ -1235,7 +1182,6 @@ static int parse_options(struct super_block *sb, char *options)
12351182
unsigned int nx_flags;
12361183

12371184
/* Set default values before parsing */
1238-
sbi->s_vol_nr = 0;
12391185
nx_flags = 0;
12401186

12411187
if (!options)
@@ -1283,15 +1229,8 @@ static int parse_options(struct super_block *sb, char *options)
12831229
}
12841230
break;
12851231
case Opt_vol:
1286-
err = match_int(&args[0], &sbi->s_vol_nr);
1287-
if (err)
1288-
return err;
1289-
break;
12901232
case Opt_snap:
1291-
kfree(sbi->s_snap_name);
1292-
sbi->s_snap_name = match_strdup(&args[0]);
1293-
if (!sbi->s_snap_name)
1294-
return -ENOMEM;
1233+
/* Already read early on mount */
12951234
break;
12961235
default:
12971236
return -EINVAL;
@@ -1774,6 +1713,71 @@ static int apfs_attach_nxi(struct apfs_sb_info *sbi, const char *dev_name, fmode
17741713
return ret;
17751714
}
17761715

1716+
/**
1717+
* apfs_preparse_options - Parse the options used to identify a superblock
1718+
* @sbi: superblock info
1719+
* @options: options string to parse
1720+
*
1721+
* Returns 0 on success, or a negative error code in case of failure. Even on
1722+
* failure, the caller is responsible for freeing all superblock fields.
1723+
*/
1724+
static int apfs_preparse_options(struct apfs_sb_info *sbi, char *options)
1725+
{
1726+
char *tofree = NULL;
1727+
char *p;
1728+
substring_t args[MAX_OPT_ARGS];
1729+
int err = 0;
1730+
1731+
/* Set default values before parsing */
1732+
sbi->s_vol_nr = 0;
1733+
sbi->s_snap_name = NULL;
1734+
1735+
if (!options)
1736+
return 0;
1737+
1738+
/* Later parse_options() will need the unmodified options string */
1739+
options = kstrdup(options, GFP_KERNEL);
1740+
if (!options)
1741+
return -ENOMEM;
1742+
tofree = options;
1743+
1744+
while ((p = strsep(&options, ",")) != NULL) {
1745+
int token;
1746+
1747+
if (!*p)
1748+
continue;
1749+
token = match_token(p, tokens, args);
1750+
switch (token) {
1751+
case Opt_vol:
1752+
err = match_int(&args[0], &sbi->s_vol_nr);
1753+
if (err)
1754+
goto out;
1755+
break;
1756+
case Opt_snap:
1757+
kfree(sbi->s_snap_name);
1758+
sbi->s_snap_name = match_strdup(&args[0]);
1759+
if (!sbi->s_snap_name) {
1760+
err = -ENOMEM;
1761+
goto out;
1762+
}
1763+
break;
1764+
case Opt_readwrite:
1765+
case Opt_cknodes:
1766+
case Opt_uid:
1767+
case Opt_gid:
1768+
/* Not needed for sget(), will be read later */
1769+
break;
1770+
default:
1771+
err = -EINVAL;
1772+
goto out;
1773+
}
1774+
}
1775+
err = 0;
1776+
out:
1777+
kfree(tofree);
1778+
return err;
1779+
}
1780+
17771781
/*
17781782
* This function is a copy of mount_bdev() that allows multiple mounts.
17791783
*/
@@ -1792,8 +1796,10 @@ static struct dentry *apfs_mount(struct file_system_type *fs_type, int flags,
17921796
sbi = kzalloc(sizeof(*sbi), GFP_KERNEL);
17931797
if (!sbi)
17941798
return ERR_PTR(-ENOMEM);
1795-
sbi->s_vol_nr = apfs_get_vol_number(data);
1796-
sbi->s_snap_name = apfs_get_snap_name(data);
1799+
/* Set up the fields that sget() will need to id the superblock */
1800+
error = apfs_preparse_options(sbi, data);
1801+
if (error)
1802+
goto out_free_sbi;
17971803

17981804
/* Make sure that snapshots are mounted read-only */
17991805
if (sbi->s_snap_name)

0 commit comments

Comments
 (0)