Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs

Pull ubifs fixes from Al Viro:
"A couple of ubifs readdir/lseek race fixes. Stable fodder, really
nasty..."

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs:
UBIFS: fix a horrid bug
UBIFS: prepare to fix a horrid bug

+39 -15
+39 -15
fs/ubifs/dir.c
··· 349 349 static int ubifs_readdir(struct file *file, void *dirent, filldir_t filldir) 350 350 { 351 351 int err, over = 0; 352 + loff_t pos = file->f_pos; 352 353 struct qstr nm; 353 354 union ubifs_key key; 354 355 struct ubifs_dent_node *dent; 355 356 struct inode *dir = file_inode(file); 356 357 struct ubifs_info *c = dir->i_sb->s_fs_info; 357 358 358 - dbg_gen("dir ino %lu, f_pos %#llx", dir->i_ino, file->f_pos); 359 + dbg_gen("dir ino %lu, f_pos %#llx", dir->i_ino, pos); 359 360 360 - if (file->f_pos > UBIFS_S_KEY_HASH_MASK || file->f_pos == 2) 361 + if (pos > UBIFS_S_KEY_HASH_MASK || pos == 2) 361 362 /* 362 363 * The directory was seek'ed to a senseless position or there 363 364 * are no more entries. 364 365 */ 365 366 return 0; 366 367 368 + if (file->f_version == 0) { 369 + /* 370 + * The file was seek'ed, which means that @file->private_data 371 + * is now invalid. This may also be just the first 372 + * 'ubifs_readdir()' invocation, in which case 373 + * @file->private_data is NULL, and the below code is 374 + * basically a no-op. 375 + */ 376 + kfree(file->private_data); 377 + file->private_data = NULL; 378 + } 379 + 380 + /* 381 + * 'generic_file_llseek()' unconditionally sets @file->f_version to 382 + * zero, and we use this for detecting whether the file was seek'ed. 383 + */ 384 + file->f_version = 1; 385 + 367 386 /* File positions 0 and 1 correspond to "." and ".." */ 368 - if (file->f_pos == 0) { 387 + if (pos == 0) { 369 388 ubifs_assert(!file->private_data); 370 389 over = filldir(dirent, ".", 1, 0, dir->i_ino, DT_DIR); 371 390 if (over) 372 391 return 0; 373 - file->f_pos = 1; 392 + file->f_pos = pos = 1; 374 393 } 375 394 376 - if (file->f_pos == 1) { 395 + if (pos == 1) { 377 396 ubifs_assert(!file->private_data); 378 397 over = filldir(dirent, "..", 2, 1, 379 398 parent_ino(file->f_path.dentry), DT_DIR); ··· 408 389 goto out; 409 390 } 410 391 411 - file->f_pos = key_hash_flash(c, &dent->key); 392 + file->f_pos = pos = key_hash_flash(c, &dent->key); 412 393 file->private_data = dent; 413 394 } 414 395 ··· 416 397 if (!dent) { 417 398 /* 418 399 * The directory was seek'ed to and is now readdir'ed. 419 - * Find the entry corresponding to @file->f_pos or the 420 - * closest one. 400 + * Find the entry corresponding to @pos or the closest one. 421 401 */ 422 - dent_key_init_hash(c, &key, dir->i_ino, file->f_pos); 402 + dent_key_init_hash(c, &key, dir->i_ino, pos); 423 403 nm.name = NULL; 424 404 dent = ubifs_tnc_next_ent(c, &key, &nm); 425 405 if (IS_ERR(dent)) { 426 406 err = PTR_ERR(dent); 427 407 goto out; 428 408 } 429 - file->f_pos = key_hash_flash(c, &dent->key); 409 + file->f_pos = pos = key_hash_flash(c, &dent->key); 430 410 file->private_data = dent; 431 411 } 432 412 ··· 437 419 ubifs_inode(dir)->creat_sqnum); 438 420 439 421 nm.len = le16_to_cpu(dent->nlen); 440 - over = filldir(dirent, dent->name, nm.len, file->f_pos, 422 + over = filldir(dirent, dent->name, nm.len, pos, 441 423 le64_to_cpu(dent->inum), 442 424 vfs_dent_type(dent->type)); 443 425 if (over) ··· 453 435 } 454 436 455 437 kfree(file->private_data); 456 - file->f_pos = key_hash_flash(c, &dent->key); 438 + file->f_pos = pos = key_hash_flash(c, &dent->key); 457 439 file->private_data = dent; 458 440 cond_resched(); 441 + 442 + if (file->f_version == 0) 443 + /* 444 + * The file was seek'ed meanwhile, lets return and start 445 + * reading direntries from the new position on the next 446 + * invocation. 447 + */ 448 + return 0; 459 449 } 460 450 461 451 out: ··· 474 448 475 449 kfree(file->private_data); 476 450 file->private_data = NULL; 451 + /* 2 is a special value indicating that there are no more direntries */ 477 452 file->f_pos = 2; 478 453 return 0; 479 454 } 480 455 481 - /* If a directory is seeked, we have to free saved readdir() state */ 482 456 static loff_t ubifs_dir_llseek(struct file *file, loff_t offset, int whence) 483 457 { 484 - kfree(file->private_data); 485 - file->private_data = NULL; 486 458 return generic_file_llseek(file, offset, whence); 487 459 } 488 460