|
30 | 30 | int fscrypt_file_open(struct inode *inode, struct file *filp)
|
31 | 31 | {
|
32 | 32 | int err;
|
33 |
| - struct dentry *dir; |
| 33 | + struct dentry *dentry, *dentry_parent; |
| 34 | + struct inode *inode_parent; |
34 | 35 |
|
35 | 36 | err = fscrypt_require_key(inode);
|
36 | 37 | if (err)
|
37 | 38 | return err;
|
38 | 39 |
|
39 |
| - dir = dget_parent(file_dentry(filp)); |
40 |
| - if (IS_ENCRYPTED(d_inode(dir)) && |
41 |
| - !fscrypt_has_permitted_context(d_inode(dir), inode)) { |
| 40 | + dentry = file_dentry(filp); |
| 41 | + |
| 42 | + /* |
| 43 | + * Getting a reference to the parent dentry is needed for the actual |
| 44 | + * encryption policy comparison, but it's expensive on multi-core |
| 45 | + * systems. Since this function runs on unencrypted files too, start |
| 46 | + * with a lightweight RCU-mode check for the parent directory being |
| 47 | + * unencrypted (in which case it's fine for the child to be either |
| 48 | + * unencrypted, or encrypted with any policy). Only continue on to the |
| 49 | + * full policy check if the parent directory is actually encrypted. |
| 50 | + */ |
| 51 | + rcu_read_lock(); |
| 52 | + dentry_parent = READ_ONCE(dentry->d_parent); |
| 53 | + inode_parent = d_inode_rcu(dentry_parent); |
| 54 | + if (inode_parent != NULL && !IS_ENCRYPTED(inode_parent)) { |
| 55 | + rcu_read_unlock(); |
| 56 | + return 0; |
| 57 | + } |
| 58 | + rcu_read_unlock(); |
| 59 | + |
| 60 | + dentry_parent = dget_parent(dentry); |
| 61 | + if (!fscrypt_has_permitted_context(d_inode(dentry_parent), inode)) { |
42 | 62 | fscrypt_warn(inode,
|
43 | 63 | "Inconsistent encryption context (parent directory: %lu)",
|
44 |
| - d_inode(dir)->i_ino); |
| 64 | + d_inode(dentry_parent)->i_ino); |
45 | 65 | err = -EPERM;
|
46 | 66 | }
|
47 |
| - dput(dir); |
| 67 | + dput(dentry_parent); |
48 | 68 | return err;
|
49 | 69 | }
|
50 | 70 | EXPORT_SYMBOL_GPL(fscrypt_file_open);
|
|
0 commit comments