Commit 29333920a5a46edcc9b728e2cf0134d5a9b516ee

Authored by Al Viro
1 parent afc70ed05a

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

... ... @@ -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. */
... ... @@ -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] == '/') {
... ... @@ -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();
... ... @@ -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)