Commit 29333920a5a46edcc9b728e2cf0134d5a9b516ee
1 parent
afc70ed05a
Exists in
master
and in
20 other branches
Fix remount races with symlink handling in affs
A couple of fields in affs_sb_info is used in follow_link() and symlink() for handling AFFS "absolute" symlinks. Need locking against affs_remount() updates. Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Showing 4 changed files with 25 additions and 8 deletions Side-by-side Diff
fs/affs/affs.h
... | ... | @@ -106,8 +106,8 @@ |
106 | 106 | u32 s_last_bmap; |
107 | 107 | struct buffer_head *s_bmap_bh; |
108 | 108 | char *s_prefix; /* Prefix for volumes and assigns. */ |
109 | - int s_prefix_len; /* Length of prefix. */ | |
110 | 109 | char s_volume[32]; /* Volume prefix for absolute symlinks. */ |
110 | + spinlock_t symlink_lock; /* protects the previous two */ | |
111 | 111 | }; |
112 | 112 | |
113 | 113 | #define SF_INTL 0x0001 /* International filesystem. */ |
fs/affs/namei.c
... | ... | @@ -341,10 +341,13 @@ |
341 | 341 | p = (char *)AFFS_HEAD(bh)->table; |
342 | 342 | lc = '/'; |
343 | 343 | if (*symname == '/') { |
344 | + struct affs_sb_info *sbi = AFFS_SB(sb); | |
344 | 345 | while (*symname == '/') |
345 | 346 | symname++; |
346 | - while (AFFS_SB(sb)->s_volume[i]) /* Cannot overflow */ | |
347 | - *p++ = AFFS_SB(sb)->s_volume[i++]; | |
347 | + spin_lock(&sbi->symlink_lock); | |
348 | + while (sbi->s_volume[i]) /* Cannot overflow */ | |
349 | + *p++ = sbi->s_volume[i++]; | |
350 | + spin_unlock(&sbi->symlink_lock); | |
348 | 351 | } |
349 | 352 | while (i < maxlen && (c = *symname++)) { |
350 | 353 | if (c == '.' && lc == '/' && *symname == '.' && symname[1] == '/') { |
fs/affs/super.c
... | ... | @@ -221,8 +221,6 @@ |
221 | 221 | *mount_opts |= SF_MUFS; |
222 | 222 | break; |
223 | 223 | case Opt_prefix: |
224 | - /* Free any previous prefix */ | |
225 | - kfree(*prefix); | |
226 | 224 | *prefix = match_strdup(&args[0]); |
227 | 225 | if (!*prefix) |
228 | 226 | return 0; |
... | ... | @@ -311,6 +309,7 @@ |
311 | 309 | return -ENOMEM; |
312 | 310 | sb->s_fs_info = sbi; |
313 | 311 | mutex_init(&sbi->s_bmlock); |
312 | + spin_lock_init(&sbi->symlink_lock); | |
314 | 313 | |
315 | 314 | if (!parse_options(data,&uid,&gid,&i,&reserved,&root_block, |
316 | 315 | &blocksize,&sbi->s_prefix, |
317 | 316 | |
318 | 317 | |
319 | 318 | |
... | ... | @@ -518,14 +517,18 @@ |
518 | 517 | unsigned long mount_flags; |
519 | 518 | int res = 0; |
520 | 519 | char *new_opts = kstrdup(data, GFP_KERNEL); |
520 | + char volume[32]; | |
521 | + char *prefix = NULL; | |
521 | 522 | |
522 | 523 | pr_debug("AFFS: remount(flags=0x%x,opts=\"%s\")\n",*flags,data); |
523 | 524 | |
524 | 525 | *flags |= MS_NODIRATIME; |
525 | 526 | |
527 | + memcpy(volume, sbi->s_volume, 32); | |
526 | 528 | if (!parse_options(data, &uid, &gid, &mode, &reserved, &root_block, |
527 | - &blocksize, &sbi->s_prefix, sbi->s_volume, | |
529 | + &blocksize, &prefix, volume, | |
528 | 530 | &mount_flags)) { |
531 | + kfree(prefix); | |
529 | 532 | kfree(new_opts); |
530 | 533 | return -EINVAL; |
531 | 534 | } |
... | ... | @@ -536,6 +539,14 @@ |
536 | 539 | sbi->s_mode = mode; |
537 | 540 | sbi->s_uid = uid; |
538 | 541 | sbi->s_gid = gid; |
542 | + /* protect against readers */ | |
543 | + spin_lock(&sbi->symlink_lock); | |
544 | + if (prefix) { | |
545 | + kfree(sbi->s_prefix); | |
546 | + sbi->s_prefix = prefix; | |
547 | + } | |
548 | + memcpy(sbi->s_volume, volume, 32); | |
549 | + spin_unlock(&sbi->symlink_lock); | |
539 | 550 | |
540 | 551 | if ((*flags & MS_RDONLY) == (sb->s_flags & MS_RDONLY)) { |
541 | 552 | unlock_kernel(); |
fs/affs/symlink.c
... | ... | @@ -20,7 +20,6 @@ |
20 | 20 | int i, j; |
21 | 21 | char c; |
22 | 22 | char lc; |
23 | - char *pf; | |
24 | 23 | |
25 | 24 | pr_debug("AFFS: follow_link(ino=%lu)\n",inode->i_ino); |
26 | 25 | |
27 | 26 | |
28 | 27 | |
... | ... | @@ -32,11 +31,15 @@ |
32 | 31 | j = 0; |
33 | 32 | lf = (struct slink_front *)bh->b_data; |
34 | 33 | lc = 0; |
35 | - pf = AFFS_SB(inode->i_sb)->s_prefix ? AFFS_SB(inode->i_sb)->s_prefix : "/"; | |
36 | 34 | |
37 | 35 | if (strchr(lf->symname,':')) { /* Handle assign or volume name */ |
36 | + struct affs_sb_info *sbi = AFFS_SB(inode->i_sb); | |
37 | + char *pf; | |
38 | + spin_lock(&sbi->symlink_lock); | |
39 | + pf = sbi->s_prefix ? sbi->s_prefix : "/"; | |
38 | 40 | while (i < 1023 && (c = pf[i])) |
39 | 41 | link[i++] = c; |
42 | + spin_unlock(&sbi->symlink_lock); | |
40 | 43 | while (i < 1023 && lf->symname[j] != ':') |
41 | 44 | link[i++] = lf->symname[j++]; |
42 | 45 | if (i < 1023) |