diff options
-rw-r--r-- | fs/f2fs/dir.c | 98 | ||||
-rw-r--r-- | fs/f2fs/f2fs.h | 8 | ||||
-rw-r--r-- | fs/f2fs/hash.c | 11 | ||||
-rw-r--r-- | fs/f2fs/inline.c | 4 | ||||
-rw-r--r-- | fs/f2fs/recovery.c | 12 | ||||
-rw-r--r-- | fs/f2fs/super.c | 6 |
6 files changed, 106 insertions, 33 deletions
diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c index 71fdf5076461..82b58d1f80eb 100644 --- a/fs/f2fs/dir.c +++ b/fs/f2fs/dir.c | |||
@@ -5,6 +5,7 @@ | |||
5 | * Copyright (c) 2012 Samsung Electronics Co., Ltd. | 5 | * Copyright (c) 2012 Samsung Electronics Co., Ltd. |
6 | * http://www.samsung.com/ | 6 | * http://www.samsung.com/ |
7 | */ | 7 | */ |
8 | #include <asm/unaligned.h> | ||
8 | #include <linux/fs.h> | 9 | #include <linux/fs.h> |
9 | #include <linux/f2fs_fs.h> | 10 | #include <linux/f2fs_fs.h> |
10 | #include <linux/sched/signal.h> | 11 | #include <linux/sched/signal.h> |
@@ -206,30 +207,55 @@ static struct f2fs_dir_entry *find_in_block(struct inode *dir, | |||
206 | /* | 207 | /* |
207 | * Test whether a case-insensitive directory entry matches the filename | 208 | * Test whether a case-insensitive directory entry matches the filename |
208 | * being searched for. | 209 | * being searched for. |
210 | * | ||
211 | * Returns 1 for a match, 0 for no match, and -errno on an error. | ||
209 | */ | 212 | */ |
210 | static bool f2fs_match_ci_name(const struct inode *dir, const struct qstr *name, | 213 | static int f2fs_match_ci_name(const struct inode *dir, const struct qstr *name, |
211 | const u8 *de_name, u32 de_name_len) | 214 | const u8 *de_name, u32 de_name_len) |
212 | { | 215 | { |
213 | const struct super_block *sb = dir->i_sb; | 216 | const struct super_block *sb = dir->i_sb; |
214 | const struct unicode_map *um = sb->s_encoding; | 217 | const struct unicode_map *um = sb->s_encoding; |
218 | struct fscrypt_str decrypted_name = FSTR_INIT(NULL, de_name_len); | ||
215 | struct qstr entry = QSTR_INIT(de_name, de_name_len); | 219 | struct qstr entry = QSTR_INIT(de_name, de_name_len); |
216 | int res; | 220 | int res; |
217 | 221 | ||
222 | if (IS_ENCRYPTED(dir)) { | ||
223 | const struct fscrypt_str encrypted_name = | ||
224 | FSTR_INIT((u8 *)de_name, de_name_len); | ||
225 | |||
226 | if (WARN_ON_ONCE(!fscrypt_has_encryption_key(dir))) | ||
227 | return -EINVAL; | ||
228 | |||
229 | decrypted_name.name = kmalloc(de_name_len, GFP_KERNEL); | ||
230 | if (!decrypted_name.name) | ||
231 | return -ENOMEM; | ||
232 | res = fscrypt_fname_disk_to_usr(dir, 0, 0, &encrypted_name, | ||
233 | &decrypted_name); | ||
234 | if (res < 0) | ||
235 | goto out; | ||
236 | entry.name = decrypted_name.name; | ||
237 | entry.len = decrypted_name.len; | ||
238 | } | ||
239 | |||
218 | res = utf8_strncasecmp_folded(um, name, &entry); | 240 | res = utf8_strncasecmp_folded(um, name, &entry); |
219 | if (res < 0) { | 241 | /* |
220 | /* | 242 | * In strict mode, ignore invalid names. In non-strict mode, |
221 | * In strict mode, ignore invalid names. In non-strict mode, | 243 | * fall back to treating them as opaque byte sequences. |
222 | * fall back to treating them as opaque byte sequences. | 244 | */ |
223 | */ | 245 | if (res < 0 && !sb_has_strict_encoding(sb)) { |
224 | if (sb_has_strict_encoding(sb) || name->len != entry.len) | 246 | res = name->len == entry.len && |
225 | return false; | 247 | memcmp(name->name, entry.name, name->len) == 0; |
226 | return !memcmp(name->name, entry.name, name->len); | 248 | } else { |
249 | /* utf8_strncasecmp_folded returns 0 on match */ | ||
250 | res = (res == 0); | ||
227 | } | 251 | } |
228 | return res == 0; | 252 | out: |
253 | kfree(decrypted_name.name); | ||
254 | return res; | ||
229 | } | 255 | } |
230 | #endif /* CONFIG_UNICODE */ | 256 | #endif /* CONFIG_UNICODE */ |
231 | 257 | ||
232 | static inline bool f2fs_match_name(const struct inode *dir, | 258 | static inline int f2fs_match_name(const struct inode *dir, |
233 | const struct f2fs_filename *fname, | 259 | const struct f2fs_filename *fname, |
234 | const u8 *de_name, u32 de_name_len) | 260 | const u8 *de_name, u32 de_name_len) |
235 | { | 261 | { |
@@ -256,6 +282,7 @@ struct f2fs_dir_entry *f2fs_find_target_dentry(const struct f2fs_dentry_ptr *d, | |||
256 | struct f2fs_dir_entry *de; | 282 | struct f2fs_dir_entry *de; |
257 | unsigned long bit_pos = 0; | 283 | unsigned long bit_pos = 0; |
258 | int max_len = 0; | 284 | int max_len = 0; |
285 | int res = 0; | ||
259 | 286 | ||
260 | if (max_slots) | 287 | if (max_slots) |
261 | *max_slots = 0; | 288 | *max_slots = 0; |
@@ -273,10 +300,15 @@ struct f2fs_dir_entry *f2fs_find_target_dentry(const struct f2fs_dentry_ptr *d, | |||
273 | continue; | 300 | continue; |
274 | } | 301 | } |
275 | 302 | ||
276 | if (de->hash_code == fname->hash && | 303 | if (de->hash_code == fname->hash) { |
277 | f2fs_match_name(d->inode, fname, d->filename[bit_pos], | 304 | res = f2fs_match_name(d->inode, fname, |
278 | le16_to_cpu(de->name_len))) | 305 | d->filename[bit_pos], |
279 | goto found; | 306 | le16_to_cpu(de->name_len)); |
307 | if (res < 0) | ||
308 | return ERR_PTR(res); | ||
309 | if (res) | ||
310 | goto found; | ||
311 | } | ||
280 | 312 | ||
281 | if (max_slots && max_len > *max_slots) | 313 | if (max_slots && max_len > *max_slots) |
282 | *max_slots = max_len; | 314 | *max_slots = max_len; |
@@ -326,7 +358,11 @@ static struct f2fs_dir_entry *find_in_level(struct inode *dir, | |||
326 | } | 358 | } |
327 | 359 | ||
328 | de = find_in_block(dir, dentry_page, fname, &max_slots); | 360 | de = find_in_block(dir, dentry_page, fname, &max_slots); |
329 | if (de) { | 361 | if (IS_ERR(de)) { |
362 | *res_page = ERR_CAST(de); | ||
363 | de = NULL; | ||
364 | break; | ||
365 | } else if (de) { | ||
330 | *res_page = dentry_page; | 366 | *res_page = dentry_page; |
331 | break; | 367 | break; |
332 | } | 368 | } |
@@ -448,17 +484,39 @@ void f2fs_set_link(struct inode *dir, struct f2fs_dir_entry *de, | |||
448 | f2fs_put_page(page, 1); | 484 | f2fs_put_page(page, 1); |
449 | } | 485 | } |
450 | 486 | ||
451 | static void init_dent_inode(const struct f2fs_filename *fname, | 487 | static void init_dent_inode(struct inode *dir, struct inode *inode, |
488 | const struct f2fs_filename *fname, | ||
452 | struct page *ipage) | 489 | struct page *ipage) |
453 | { | 490 | { |
454 | struct f2fs_inode *ri; | 491 | struct f2fs_inode *ri; |
455 | 492 | ||
493 | if (!fname) /* tmpfile case? */ | ||
494 | return; | ||
495 | |||
456 | f2fs_wait_on_page_writeback(ipage, NODE, true, true); | 496 | f2fs_wait_on_page_writeback(ipage, NODE, true, true); |
457 | 497 | ||
458 | /* copy name info. to this inode page */ | 498 | /* copy name info. to this inode page */ |
459 | ri = F2FS_INODE(ipage); | 499 | ri = F2FS_INODE(ipage); |
460 | ri->i_namelen = cpu_to_le32(fname->disk_name.len); | 500 | ri->i_namelen = cpu_to_le32(fname->disk_name.len); |
461 | memcpy(ri->i_name, fname->disk_name.name, fname->disk_name.len); | 501 | memcpy(ri->i_name, fname->disk_name.name, fname->disk_name.len); |
502 | if (IS_ENCRYPTED(dir)) { | ||
503 | file_set_enc_name(inode); | ||
504 | /* | ||
505 | * Roll-forward recovery doesn't have encryption keys available, | ||
506 | * so it can't compute the dirhash for encrypted+casefolded | ||
507 | * filenames. Append it to i_name if possible. Else, disable | ||
508 | * roll-forward recovery of the dentry (i.e., make fsync'ing the | ||
509 | * file force a checkpoint) by setting LOST_PINO. | ||
510 | */ | ||
511 | if (IS_CASEFOLDED(dir)) { | ||
512 | if (fname->disk_name.len + sizeof(f2fs_hash_t) <= | ||
513 | F2FS_NAME_LEN) | ||
514 | put_unaligned(fname->hash, (f2fs_hash_t *) | ||
515 | &ri->i_name[fname->disk_name.len]); | ||
516 | else | ||
517 | file_lost_pino(inode); | ||
518 | } | ||
519 | } | ||
462 | set_page_dirty(ipage); | 520 | set_page_dirty(ipage); |
463 | } | 521 | } |
464 | 522 | ||
@@ -541,11 +599,7 @@ struct page *f2fs_init_inode_metadata(struct inode *inode, struct inode *dir, | |||
541 | return page; | 599 | return page; |
542 | } | 600 | } |
543 | 601 | ||
544 | if (fname) { | 602 | init_dent_inode(dir, inode, fname, page); |
545 | init_dent_inode(fname, page); | ||
546 | if (IS_ENCRYPTED(dir)) | ||
547 | file_set_enc_name(inode); | ||
548 | } | ||
549 | 603 | ||
550 | /* | 604 | /* |
551 | * This file should be checkpointed during fsync. | 605 | * This file should be checkpointed during fsync. |
diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 01fd42843e49..c626fb03d5bc 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h | |||
@@ -454,9 +454,11 @@ struct f2fs_filename { | |||
454 | #ifdef CONFIG_UNICODE | 454 | #ifdef CONFIG_UNICODE |
455 | /* | 455 | /* |
456 | * For casefolded directories: the casefolded name, but it's left NULL | 456 | * For casefolded directories: the casefolded name, but it's left NULL |
457 | * if the original name is not valid Unicode or if the filesystem is | 457 | * if the original name is not valid Unicode, if the directory is both |
458 | * doing an internal operation where usr_fname is also NULL. In these | 458 | * casefolded and encrypted and its encryption key is unavailable, or if |
459 | * cases we fall back to treating the name as an opaque byte sequence. | 459 | * the filesystem is doing an internal operation where usr_fname is also |
460 | * NULL. In all these cases we fall back to treating the name as an | ||
461 | * opaque byte sequence. | ||
460 | */ | 462 | */ |
461 | struct fscrypt_str cf_name; | 463 | struct fscrypt_str cf_name; |
462 | #endif | 464 | #endif |
diff --git a/fs/f2fs/hash.c b/fs/f2fs/hash.c index de841aaf3c43..e3beac546c63 100644 --- a/fs/f2fs/hash.c +++ b/fs/f2fs/hash.c | |||
@@ -111,7 +111,9 @@ void f2fs_hash_filename(const struct inode *dir, struct f2fs_filename *fname) | |||
111 | * If the casefolded name is provided, hash it instead of the | 111 | * If the casefolded name is provided, hash it instead of the |
112 | * on-disk name. If the casefolded name is *not* provided, that | 112 | * on-disk name. If the casefolded name is *not* provided, that |
113 | * should only be because the name wasn't valid Unicode, so fall | 113 | * should only be because the name wasn't valid Unicode, so fall |
114 | * back to treating the name as an opaque byte sequence. | 114 | * back to treating the name as an opaque byte sequence. Note |
115 | * that to handle encrypted directories, the fallback must use | ||
116 | * usr_fname (plaintext) rather than disk_name (ciphertext). | ||
115 | */ | 117 | */ |
116 | WARN_ON_ONCE(!fname->usr_fname->name); | 118 | WARN_ON_ONCE(!fname->usr_fname->name); |
117 | if (fname->cf_name.name) { | 119 | if (fname->cf_name.name) { |
@@ -121,6 +123,13 @@ void f2fs_hash_filename(const struct inode *dir, struct f2fs_filename *fname) | |||
121 | name = fname->usr_fname->name; | 123 | name = fname->usr_fname->name; |
122 | len = fname->usr_fname->len; | 124 | len = fname->usr_fname->len; |
123 | } | 125 | } |
126 | if (IS_ENCRYPTED(dir)) { | ||
127 | struct qstr tmp = QSTR_INIT(name, len); | ||
128 | |||
129 | fname->hash = | ||
130 | cpu_to_le32(fscrypt_fname_siphash(dir, &tmp)); | ||
131 | return; | ||
132 | } | ||
124 | } | 133 | } |
125 | #endif | 134 | #endif |
126 | fname->hash = cpu_to_le32(TEA_hash_name(name, len)); | 135 | fname->hash = cpu_to_le32(TEA_hash_name(name, len)); |
diff --git a/fs/f2fs/inline.c b/fs/f2fs/inline.c index 70384e31788d..92e9852d316a 100644 --- a/fs/f2fs/inline.c +++ b/fs/f2fs/inline.c | |||
@@ -332,6 +332,10 @@ struct f2fs_dir_entry *f2fs_find_in_inline_dir(struct inode *dir, | |||
332 | make_dentry_ptr_inline(dir, &d, inline_dentry); | 332 | make_dentry_ptr_inline(dir, &d, inline_dentry); |
333 | de = f2fs_find_target_dentry(&d, fname, NULL); | 333 | de = f2fs_find_target_dentry(&d, fname, NULL); |
334 | unlock_page(ipage); | 334 | unlock_page(ipage); |
335 | if (IS_ERR(de)) { | ||
336 | *res_page = ERR_CAST(de); | ||
337 | de = NULL; | ||
338 | } | ||
335 | if (de) | 339 | if (de) |
336 | *res_page = ipage; | 340 | *res_page = ipage; |
337 | else | 341 | else |
diff --git a/fs/f2fs/recovery.c b/fs/f2fs/recovery.c index 4f12ade6410a..0947d36af1a8 100644 --- a/fs/f2fs/recovery.c +++ b/fs/f2fs/recovery.c | |||
@@ -5,6 +5,7 @@ | |||
5 | * Copyright (c) 2012 Samsung Electronics Co., Ltd. | 5 | * Copyright (c) 2012 Samsung Electronics Co., Ltd. |
6 | * http://www.samsung.com/ | 6 | * http://www.samsung.com/ |
7 | */ | 7 | */ |
8 | #include <asm/unaligned.h> | ||
8 | #include <linux/fs.h> | 9 | #include <linux/fs.h> |
9 | #include <linux/f2fs_fs.h> | 10 | #include <linux/f2fs_fs.h> |
10 | #include "f2fs.h" | 11 | #include "f2fs.h" |
@@ -128,7 +129,16 @@ static int init_recovered_filename(const struct inode *dir, | |||
128 | } | 129 | } |
129 | 130 | ||
130 | /* Compute the hash of the filename */ | 131 | /* Compute the hash of the filename */ |
131 | if (IS_CASEFOLDED(dir)) { | 132 | if (IS_ENCRYPTED(dir) && IS_CASEFOLDED(dir)) { |
133 | /* | ||
134 | * In this case the hash isn't computable without the key, so it | ||
135 | * was saved on-disk. | ||
136 | */ | ||
137 | if (fname->disk_name.len + sizeof(f2fs_hash_t) > F2FS_NAME_LEN) | ||
138 | return -EINVAL; | ||
139 | fname->hash = get_unaligned((f2fs_hash_t *) | ||
140 | &raw_inode->i_name[fname->disk_name.len]); | ||
141 | } else if (IS_CASEFOLDED(dir)) { | ||
132 | err = f2fs_init_casefolded_name(dir, fname); | 142 | err = f2fs_init_casefolded_name(dir, fname); |
133 | if (err) | 143 | if (err) |
134 | return err; | 144 | return err; |
diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 08e63c8caa1e..1c282c3da263 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c | |||
@@ -3398,12 +3398,6 @@ static int f2fs_setup_casefold(struct f2fs_sb_info *sbi) | |||
3398 | struct unicode_map *encoding; | 3398 | struct unicode_map *encoding; |
3399 | __u16 encoding_flags; | 3399 | __u16 encoding_flags; |
3400 | 3400 | ||
3401 | if (f2fs_sb_has_encrypt(sbi)) { | ||
3402 | f2fs_err(sbi, | ||
3403 | "Can't mount with encoding and encryption"); | ||
3404 | return -EINVAL; | ||
3405 | } | ||
3406 | |||
3407 | if (f2fs_sb_read_encoding(sbi->raw_super, &encoding_info, | 3401 | if (f2fs_sb_read_encoding(sbi->raw_super, &encoding_info, |
3408 | &encoding_flags)) { | 3402 | &encoding_flags)) { |
3409 | f2fs_err(sbi, | 3403 | f2fs_err(sbi, |