aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSougata Santra <sougata@tuxera.com>2014-12-18 19:17:12 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2014-12-18 22:08:10 -0500
commit89ac9b4d3d1a049ae1054f99b1aed81092cd0a82 (patch)
treeb76f017fbe75cacd124b6ab796d2e4d5ddb9ea76
parent859f7ef142a956676cb387b90f18e2e71e959c68 (diff)
hfsplus: fix longname handling
Longname is not correctly handled by hfsplus driver. If an attempt to create a longname(>255) file/directory is made, it succeeds by creating a file/directory with HFSPLUS_MAX_STRLEN and incorrect catalog key. Thus leaving the volume in an inconsistent state. This patch fixes this issue. Although lookup is always called first to create a negative entry, so just doing a check in lookup would probably fix this issue. I choose to propagate error to other iops as well. Please NOTE: I have factored out hfsplus_cat_build_key_with_cnid from hfsplus_cat_build_key, to avoid unncessary branching. Thanks a lot. TEST: ------ dir="TEST_DIR" cdir=`pwd` name255="_123456789_123456789_123456789_123456789_123456789_123456789\ _123456789_123456789_123456789_123456789_123456789_123456789_123456789\ _123456789_123456789_123456789_123456789_123456789_123456789_123456789\ _123456789_123456789_123456789_123456789_123456789_1234" name256="${name255}5" mkdir $dir cd $dir touch $name255 rm -f $name255 touch $name256 ls -la cd $cdir rm -rf $dir RESULT: ------- [sougata@ultrabook tmp]$ cdir=`pwd` [sougata@ultrabook tmp]$ name255="_123456789_123456789_123456789_123456789_123456789_123456789\ > _123456789_123456789_123456789_123456789_123456789_123456789_123456789\ > _123456789_123456789_123456789_123456789_123456789_123456789_123456789\ > _123456789_123456789_123456789_123456789_123456789_1234" [sougata@ultrabook tmp]$ name256="${name255}5" [sougata@ultrabook tmp]$ [sougata@ultrabook tmp]$ mkdir $dir [sougata@ultrabook tmp]$ cd $dir [sougata@ultrabook TEST_DIR]$ touch $name255 [sougata@ultrabook TEST_DIR]$ rm -f $name255 [sougata@ultrabook TEST_DIR]$ touch $name256 [sougata@ultrabook TEST_DIR]$ ls -la ls: cannot access _123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_1234: No such file or directory total 0 drwxrwxr-x 1 sougata sougata 3 Feb 20 19:56 . drwxrwxrwx 1 root root 6 Feb 20 19:56 .. -????????? ? ? ? ? ? _123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789_1234 [sougata@ultrabook TEST_DIR]$ cd $cdir [sougata@ultrabook tmp]$ rm -rf $dir rm: cannot remove `TEST_DIR': Directory not empty -ENAMETOOLONG returned from hfsplus_asc2uni was not propaged to iops. This allowed hfsplus to create files/directories with HFSPLUS_MAX_STRLEN and incorrect keys, leaving the FS in an inconsistent state. This patch fixes this issue. Signed-off-by: Sougata Santra <sougata@tuxera.com> Reviewed-by: Christoph Hellwig <hch@lst.de> Cc: Vyacheslav Dubeyko <slava@dubeyko.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r--fs/hfsplus/catalog.c89
-rw-r--r--fs/hfsplus/dir.c11
-rw-r--r--fs/hfsplus/hfsplus_fs.h4
-rw-r--r--fs/hfsplus/super.c4
4 files changed, 79 insertions, 29 deletions
diff --git a/fs/hfsplus/catalog.c b/fs/hfsplus/catalog.c
index 32602c667b4a..7892e6fddb66 100644
--- a/fs/hfsplus/catalog.c
+++ b/fs/hfsplus/catalog.c
@@ -38,21 +38,30 @@ int hfsplus_cat_bin_cmp_key(const hfsplus_btree_key *k1,
38 return hfsplus_strcmp(&k1->cat.name, &k2->cat.name); 38 return hfsplus_strcmp(&k1->cat.name, &k2->cat.name);
39} 39}
40 40
41void hfsplus_cat_build_key(struct super_block *sb, hfsplus_btree_key *key, 41/* Generates key for catalog file/folders record. */
42 u32 parent, struct qstr *str) 42int hfsplus_cat_build_key(struct super_block *sb,
43 hfsplus_btree_key *key, u32 parent, struct qstr *str)
43{ 44{
44 int len; 45 int len, err;
45 46
46 key->cat.parent = cpu_to_be32(parent); 47 key->cat.parent = cpu_to_be32(parent);
47 if (str) { 48 err = hfsplus_asc2uni(sb, &key->cat.name, HFSPLUS_MAX_STRLEN,
48 hfsplus_asc2uni(sb, &key->cat.name, HFSPLUS_MAX_STRLEN, 49 str->name, str->len);
49 str->name, str->len); 50 if (unlikely(err < 0))
50 len = be16_to_cpu(key->cat.name.length); 51 return err;
51 } else { 52
52 key->cat.name.length = 0; 53 len = be16_to_cpu(key->cat.name.length);
53 len = 0;
54 }
55 key->key_len = cpu_to_be16(6 + 2 * len); 54 key->key_len = cpu_to_be16(6 + 2 * len);
55 return 0;
56}
57
58/* Generates key for catalog thread record. */
59void hfsplus_cat_build_key_with_cnid(struct super_block *sb,
60 hfsplus_btree_key *key, u32 parent)
61{
62 key->cat.parent = cpu_to_be32(parent);
63 key->cat.name.length = 0;
64 key->key_len = cpu_to_be16(6);
56} 65}
57 66
58static void hfsplus_cat_build_key_uni(hfsplus_btree_key *key, u32 parent, 67static void hfsplus_cat_build_key_uni(hfsplus_btree_key *key, u32 parent,
@@ -167,11 +176,16 @@ static int hfsplus_fill_cat_thread(struct super_block *sb,
167 hfsplus_cat_entry *entry, int type, 176 hfsplus_cat_entry *entry, int type,
168 u32 parentid, struct qstr *str) 177 u32 parentid, struct qstr *str)
169{ 178{
179 int err;
180
170 entry->type = cpu_to_be16(type); 181 entry->type = cpu_to_be16(type);
171 entry->thread.reserved = 0; 182 entry->thread.reserved = 0;
172 entry->thread.parentID = cpu_to_be32(parentid); 183 entry->thread.parentID = cpu_to_be32(parentid);
173 hfsplus_asc2uni(sb, &entry->thread.nodeName, HFSPLUS_MAX_STRLEN, 184 err = hfsplus_asc2uni(sb, &entry->thread.nodeName, HFSPLUS_MAX_STRLEN,
174 str->name, str->len); 185 str->name, str->len);
186 if (unlikely(err < 0))
187 return err;
188
175 return 10 + be16_to_cpu(entry->thread.nodeName.length) * 2; 189 return 10 + be16_to_cpu(entry->thread.nodeName.length) * 2;
176} 190}
177 191
@@ -183,7 +197,7 @@ int hfsplus_find_cat(struct super_block *sb, u32 cnid,
183 int err; 197 int err;
184 u16 type; 198 u16 type;
185 199
186 hfsplus_cat_build_key(sb, fd->search_key, cnid, NULL); 200 hfsplus_cat_build_key_with_cnid(sb, fd->search_key, cnid);
187 err = hfs_brec_read(fd, &tmp, sizeof(hfsplus_cat_entry)); 201 err = hfs_brec_read(fd, &tmp, sizeof(hfsplus_cat_entry));
188 if (err) 202 if (err)
189 return err; 203 return err;
@@ -250,11 +264,16 @@ int hfsplus_create_cat(u32 cnid, struct inode *dir,
250 if (err) 264 if (err)
251 return err; 265 return err;
252 266
253 hfsplus_cat_build_key(sb, fd.search_key, cnid, NULL); 267 hfsplus_cat_build_key_with_cnid(sb, fd.search_key, cnid);
254 entry_size = hfsplus_fill_cat_thread(sb, &entry, 268 entry_size = hfsplus_fill_cat_thread(sb, &entry,
255 S_ISDIR(inode->i_mode) ? 269 S_ISDIR(inode->i_mode) ?
256 HFSPLUS_FOLDER_THREAD : HFSPLUS_FILE_THREAD, 270 HFSPLUS_FOLDER_THREAD : HFSPLUS_FILE_THREAD,
257 dir->i_ino, str); 271 dir->i_ino, str);
272 if (unlikely(entry_size < 0)) {
273 err = entry_size;
274 goto err2;
275 }
276
258 err = hfs_brec_find(&fd, hfs_find_rec_by_key); 277 err = hfs_brec_find(&fd, hfs_find_rec_by_key);
259 if (err != -ENOENT) { 278 if (err != -ENOENT) {
260 if (!err) 279 if (!err)
@@ -265,7 +284,10 @@ int hfsplus_create_cat(u32 cnid, struct inode *dir,
265 if (err) 284 if (err)
266 goto err2; 285 goto err2;
267 286
268 hfsplus_cat_build_key(sb, fd.search_key, dir->i_ino, str); 287 err = hfsplus_cat_build_key(sb, fd.search_key, dir->i_ino, str);
288 if (unlikely(err))
289 goto err1;
290
269 entry_size = hfsplus_cat_build_record(&entry, cnid, inode); 291 entry_size = hfsplus_cat_build_record(&entry, cnid, inode);
270 err = hfs_brec_find(&fd, hfs_find_rec_by_key); 292 err = hfs_brec_find(&fd, hfs_find_rec_by_key);
271 if (err != -ENOENT) { 293 if (err != -ENOENT) {
@@ -288,7 +310,7 @@ int hfsplus_create_cat(u32 cnid, struct inode *dir,
288 return 0; 310 return 0;
289 311
290err1: 312err1:
291 hfsplus_cat_build_key(sb, fd.search_key, cnid, NULL); 313 hfsplus_cat_build_key_with_cnid(sb, fd.search_key, cnid);
292 if (!hfs_brec_find(&fd, hfs_find_rec_by_key)) 314 if (!hfs_brec_find(&fd, hfs_find_rec_by_key))
293 hfs_brec_remove(&fd); 315 hfs_brec_remove(&fd);
294err2: 316err2:
@@ -313,7 +335,7 @@ int hfsplus_delete_cat(u32 cnid, struct inode *dir, struct qstr *str)
313 if (!str) { 335 if (!str) {
314 int len; 336 int len;
315 337
316 hfsplus_cat_build_key(sb, fd.search_key, cnid, NULL); 338 hfsplus_cat_build_key_with_cnid(sb, fd.search_key, cnid);
317 err = hfs_brec_find(&fd, hfs_find_rec_by_key); 339 err = hfs_brec_find(&fd, hfs_find_rec_by_key);
318 if (err) 340 if (err)
319 goto out; 341 goto out;
@@ -329,7 +351,9 @@ int hfsplus_delete_cat(u32 cnid, struct inode *dir, struct qstr *str)
329 off + 2, len); 351 off + 2, len);
330 fd.search_key->key_len = cpu_to_be16(6 + len); 352 fd.search_key->key_len = cpu_to_be16(6 + len);
331 } else 353 } else
332 hfsplus_cat_build_key(sb, fd.search_key, dir->i_ino, str); 354 err = hfsplus_cat_build_key(sb, fd.search_key, dir->i_ino, str);
355 if (unlikely(err))
356 goto out;
333 357
334 err = hfs_brec_find(&fd, hfs_find_rec_by_key); 358 err = hfs_brec_find(&fd, hfs_find_rec_by_key);
335 if (err) 359 if (err)
@@ -360,7 +384,7 @@ int hfsplus_delete_cat(u32 cnid, struct inode *dir, struct qstr *str)
360 if (err) 384 if (err)
361 goto out; 385 goto out;
362 386
363 hfsplus_cat_build_key(sb, fd.search_key, cnid, NULL); 387 hfsplus_cat_build_key_with_cnid(sb, fd.search_key, cnid);
364 err = hfs_brec_find(&fd, hfs_find_rec_by_key); 388 err = hfs_brec_find(&fd, hfs_find_rec_by_key);
365 if (err) 389 if (err)
366 goto out; 390 goto out;
@@ -405,7 +429,11 @@ int hfsplus_rename_cat(u32 cnid,
405 dst_fd = src_fd; 429 dst_fd = src_fd;
406 430
407 /* find the old dir entry and read the data */ 431 /* find the old dir entry and read the data */
408 hfsplus_cat_build_key(sb, src_fd.search_key, src_dir->i_ino, src_name); 432 err = hfsplus_cat_build_key(sb, src_fd.search_key,
433 src_dir->i_ino, src_name);
434 if (unlikely(err))
435 goto out;
436
409 err = hfs_brec_find(&src_fd, hfs_find_rec_by_key); 437 err = hfs_brec_find(&src_fd, hfs_find_rec_by_key);
410 if (err) 438 if (err)
411 goto out; 439 goto out;
@@ -419,7 +447,11 @@ int hfsplus_rename_cat(u32 cnid,
419 type = be16_to_cpu(entry.type); 447 type = be16_to_cpu(entry.type);
420 448
421 /* create new dir entry with the data from the old entry */ 449 /* create new dir entry with the data from the old entry */
422 hfsplus_cat_build_key(sb, dst_fd.search_key, dst_dir->i_ino, dst_name); 450 err = hfsplus_cat_build_key(sb, dst_fd.search_key,
451 dst_dir->i_ino, dst_name);
452 if (unlikely(err))
453 goto out;
454
423 err = hfs_brec_find(&dst_fd, hfs_find_rec_by_key); 455 err = hfs_brec_find(&dst_fd, hfs_find_rec_by_key);
424 if (err != -ENOENT) { 456 if (err != -ENOENT) {
425 if (!err) 457 if (!err)
@@ -436,7 +468,11 @@ int hfsplus_rename_cat(u32 cnid,
436 dst_dir->i_mtime = dst_dir->i_ctime = CURRENT_TIME_SEC; 468 dst_dir->i_mtime = dst_dir->i_ctime = CURRENT_TIME_SEC;
437 469
438 /* finally remove the old entry */ 470 /* finally remove the old entry */
439 hfsplus_cat_build_key(sb, src_fd.search_key, src_dir->i_ino, src_name); 471 err = hfsplus_cat_build_key(sb, src_fd.search_key,
472 src_dir->i_ino, src_name);
473 if (unlikely(err))
474 goto out;
475
440 err = hfs_brec_find(&src_fd, hfs_find_rec_by_key); 476 err = hfs_brec_find(&src_fd, hfs_find_rec_by_key);
441 if (err) 477 if (err)
442 goto out; 478 goto out;
@@ -449,7 +485,7 @@ int hfsplus_rename_cat(u32 cnid,
449 src_dir->i_mtime = src_dir->i_ctime = CURRENT_TIME_SEC; 485 src_dir->i_mtime = src_dir->i_ctime = CURRENT_TIME_SEC;
450 486
451 /* remove old thread entry */ 487 /* remove old thread entry */
452 hfsplus_cat_build_key(sb, src_fd.search_key, cnid, NULL); 488 hfsplus_cat_build_key_with_cnid(sb, src_fd.search_key, cnid);
453 err = hfs_brec_find(&src_fd, hfs_find_rec_by_key); 489 err = hfs_brec_find(&src_fd, hfs_find_rec_by_key);
454 if (err) 490 if (err)
455 goto out; 491 goto out;
@@ -459,9 +495,14 @@ int hfsplus_rename_cat(u32 cnid,
459 goto out; 495 goto out;
460 496
461 /* create new thread entry */ 497 /* create new thread entry */
462 hfsplus_cat_build_key(sb, dst_fd.search_key, cnid, NULL); 498 hfsplus_cat_build_key_with_cnid(sb, dst_fd.search_key, cnid);
463 entry_size = hfsplus_fill_cat_thread(sb, &entry, type, 499 entry_size = hfsplus_fill_cat_thread(sb, &entry, type,
464 dst_dir->i_ino, dst_name); 500 dst_dir->i_ino, dst_name);
501 if (unlikely(entry_size < 0)) {
502 err = entry_size;
503 goto out;
504 }
505
465 err = hfs_brec_find(&dst_fd, hfs_find_rec_by_key); 506 err = hfs_brec_find(&dst_fd, hfs_find_rec_by_key);
466 if (err != -ENOENT) { 507 if (err != -ENOENT) {
467 if (!err) 508 if (!err)
diff --git a/fs/hfsplus/dir.c b/fs/hfsplus/dir.c
index 610a3260bef1..435bea231cc6 100644
--- a/fs/hfsplus/dir.c
+++ b/fs/hfsplus/dir.c
@@ -44,7 +44,10 @@ static struct dentry *hfsplus_lookup(struct inode *dir, struct dentry *dentry,
44 err = hfs_find_init(HFSPLUS_SB(sb)->cat_tree, &fd); 44 err = hfs_find_init(HFSPLUS_SB(sb)->cat_tree, &fd);
45 if (err) 45 if (err)
46 return ERR_PTR(err); 46 return ERR_PTR(err);
47 hfsplus_cat_build_key(sb, fd.search_key, dir->i_ino, &dentry->d_name); 47 err = hfsplus_cat_build_key(sb, fd.search_key, dir->i_ino,
48 &dentry->d_name);
49 if (unlikely(err < 0))
50 goto fail;
48again: 51again:
49 err = hfs_brec_read(&fd, &entry, sizeof(entry)); 52 err = hfs_brec_read(&fd, &entry, sizeof(entry));
50 if (err) { 53 if (err) {
@@ -97,9 +100,11 @@ again:
97 be32_to_cpu(entry.file.permissions.dev); 100 be32_to_cpu(entry.file.permissions.dev);
98 str.len = sprintf(name, "iNode%d", linkid); 101 str.len = sprintf(name, "iNode%d", linkid);
99 str.name = name; 102 str.name = name;
100 hfsplus_cat_build_key(sb, fd.search_key, 103 err = hfsplus_cat_build_key(sb, fd.search_key,
101 HFSPLUS_SB(sb)->hidden_dir->i_ino, 104 HFSPLUS_SB(sb)->hidden_dir->i_ino,
102 &str); 105 &str);
106 if (unlikely(err < 0))
107 goto fail;
103 goto again; 108 goto again;
104 } 109 }
105 } else if (!dentry->d_fsdata) 110 } else if (!dentry->d_fsdata)
@@ -145,7 +150,7 @@ static int hfsplus_readdir(struct file *file, struct dir_context *ctx)
145 err = -ENOMEM; 150 err = -ENOMEM;
146 goto out; 151 goto out;
147 } 152 }
148 hfsplus_cat_build_key(sb, fd.search_key, inode->i_ino, NULL); 153 hfsplus_cat_build_key_with_cnid(sb, fd.search_key, inode->i_ino);
149 err = hfs_brec_find(&fd, hfs_find_rec_by_key); 154 err = hfs_brec_find(&fd, hfs_find_rec_by_key);
150 if (err) 155 if (err)
151 goto out; 156 goto out;
diff --git a/fs/hfsplus/hfsplus_fs.h b/fs/hfsplus/hfsplus_fs.h
index eb5e059f481a..b0441d65fa54 100644
--- a/fs/hfsplus/hfsplus_fs.h
+++ b/fs/hfsplus/hfsplus_fs.h
@@ -443,8 +443,10 @@ int hfsplus_cat_case_cmp_key(const hfsplus_btree_key *k1,
443 const hfsplus_btree_key *k2); 443 const hfsplus_btree_key *k2);
444int hfsplus_cat_bin_cmp_key(const hfsplus_btree_key *k1, 444int hfsplus_cat_bin_cmp_key(const hfsplus_btree_key *k1,
445 const hfsplus_btree_key *k2); 445 const hfsplus_btree_key *k2);
446void hfsplus_cat_build_key(struct super_block *sb, hfsplus_btree_key *key, 446int hfsplus_cat_build_key(struct super_block *sb, hfsplus_btree_key *key,
447 u32 parent, struct qstr *str); 447 u32 parent, struct qstr *str);
448void hfsplus_cat_build_key_with_cnid(struct super_block *sb,
449 hfsplus_btree_key *key, u32 parent);
448void hfsplus_cat_set_perms(struct inode *inode, struct hfsplus_perm *perms); 450void hfsplus_cat_set_perms(struct inode *inode, struct hfsplus_perm *perms);
449int hfsplus_find_cat(struct super_block *sb, u32 cnid, 451int hfsplus_find_cat(struct super_block *sb, u32 cnid,
450 struct hfs_find_data *fd); 452 struct hfs_find_data *fd);
diff --git a/fs/hfsplus/super.c b/fs/hfsplus/super.c
index 4cf2024b87da..593af2fdcc2d 100644
--- a/fs/hfsplus/super.c
+++ b/fs/hfsplus/super.c
@@ -515,7 +515,9 @@ static int hfsplus_fill_super(struct super_block *sb, void *data, int silent)
515 err = hfs_find_init(sbi->cat_tree, &fd); 515 err = hfs_find_init(sbi->cat_tree, &fd);
516 if (err) 516 if (err)
517 goto out_put_root; 517 goto out_put_root;
518 hfsplus_cat_build_key(sb, fd.search_key, HFSPLUS_ROOT_CNID, &str); 518 err = hfsplus_cat_build_key(sb, fd.search_key, HFSPLUS_ROOT_CNID, &str);
519 if (unlikely(err < 0))
520 goto out_put_root;
519 if (!hfs_brec_read(&fd, &entry, sizeof(entry))) { 521 if (!hfs_brec_read(&fd, &entry, sizeof(entry))) {
520 hfs_find_exit(&fd); 522 hfs_find_exit(&fd);
521 if (entry.type != cpu_to_be16(HFSPLUS_FOLDER)) 523 if (entry.type != cpu_to_be16(HFSPLUS_FOLDER))