Message ID | 1449759879-2166-3-git-send-email-tytso@mit.edu |
---|---|
State | Superseded, archived |
Headers | show |
On Dec 10, 2015, at 8:04 AM, Theodore Ts'o <tytso@mit.edu> wrote: > > Add new ioctls which allow for the metadata of encrypted files (the > filename and the crypto policy) to be backed up. We can restore the > crypto policy, but for now we can't restore the encrypted filename > because messing with encrypted directories directly while bypassing > the VFS would get fairly tricky/nasty. Instead of exposing these via ioctls, why not use virtual xattrs (e.g. "trusted.crypt_meta" or similar) so that this can be backed up and restored using a normal backup tool instead of basically nothing that exists today. For reading the ciphertext, do existing tools such as tar and rsync allow reading with O_DIRECT with CIPHERTEXT_ACCESS? If not, one might consider to allow normal reads w/o the file key to return the ciphertext so that "tar --xattr" by some process with CAP_SYS_ADMIN but out any keys could be used to backup and restore the ciphertext in a sensible manner. That is no more risk than the same process reading the blocks from the disk device directly, but would be a lot more useful (i.e. it is practical to use normal backup tools for encrypted files). Cheers, Andreas > Signed-off-by: Theodore Ts'o <tytso@mit.edu> > --- > fs/ext4/crypto_key.c | 57 ++++++++++++++++++++++++++++++++++++++++++++- > fs/ext4/ext4.h | 9 ++++++++ > fs/ext4/ext4_crypto.h | 8 +++++++ > fs/ext4/ioctl.c | 64 +++++++++++++++++++++++++++++++++++++++++++++++++++ > fs/ext4/namei.c | 24 +++++++++++++++++++ > 5 files changed, 161 insertions(+), 1 deletion(-) > > diff --git a/fs/ext4/crypto_key.c b/fs/ext4/crypto_key.c > index c5882b3..039b22d 100644 > --- a/fs/ext4/crypto_key.c > +++ b/fs/ext4/crypto_key.c > @@ -14,7 +14,7 @@ > #include <linux/scatterlist.h> > #include <uapi/linux/keyctl.h> > > -#include "ext4.h" > +#include "ext4_jbd2.h" > #include "xattr.h" > > static void derive_crypt_complete(struct crypto_async_request *req, int rc) > @@ -270,3 +270,58 @@ int ext4_has_encryption_key(struct inode *inode) > > return (ei->i_crypt_info != NULL); > } > + > +int ext4_get_encryption_metadata(struct inode *inode, > + struct ext4_encrypted_metadata *mdata) > +{ > + int res; > + > + if (mdata->len < sizeof(struct ext4_encryption_context)) > + return -EINVAL; > + > + res = ext4_xattr_get(inode, EXT4_XATTR_INDEX_ENCRYPTION, > + EXT4_XATTR_NAME_ENCRYPTION_CONTEXT, > + &mdata->metadata, mdata->len); > + if (res < 0) > + return res; > + mdata->len = res; > + return 0; > +} > + > +int ext4_set_encryption_metadata(struct inode *inode, > + struct ext4_encrypted_metadata *mdata) > +{ > + struct ext4_encryption_context *ctx; > + handle_t *handle; > + int res; > + > + if (mdata->len != sizeof(struct ext4_encryption_context)) > + return -EINVAL; > + ctx = (struct ext4_encryption_context *) &mdata->metadata; > + if (ctx->format != EXT4_ENCRYPTION_CONTEXT_FORMAT_V1) > + return -EINVAL; > + > + res = ext4_convert_inline_data(inode); > + if (res) > + return res; > + > + handle = ext4_journal_start(inode, EXT4_HT_MISC, > + ext4_jbd2_credits_xattr(inode)); > + if (IS_ERR(handle)) > + return PTR_ERR(handle); > + res = ext4_xattr_set(inode, EXT4_XATTR_INDEX_ENCRYPTION, > + EXT4_XATTR_NAME_ENCRYPTION_CONTEXT, ctx, > + sizeof(struct ext4_encryption_context), 0); > + if (res < 0) > + goto errout; > + ext4_set_inode_flag(inode, EXT4_INODE_ENCRYPT); > + ext4_clear_inode_state(inode, EXT4_STATE_MAY_INLINE_DATA); > + res = ext4_mark_inode_dirty(handle, inode); > + if (res) > + EXT4_ERROR_INODE(inode, "Failed to mark inode dirty"); > + else > + res = ext4_get_encryption_info(inode); > +errout: > + ext4_journal_stop(handle); > + return res; > +} > diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h > index cf7a885..c569430 100644 > --- a/fs/ext4/ext4.h > +++ b/fs/ext4/ext4.h > @@ -617,6 +617,9 @@ enum { > #define EXT4_IOC_SET_ENCRYPTION_POLICY _IOR('f', 19, struct ext4_encryption_policy) > #define EXT4_IOC_GET_ENCRYPTION_PWSALT _IOW('f', 20, __u8[16]) > #define EXT4_IOC_GET_ENCRYPTION_POLICY _IOW('f', 21, struct ext4_encryption_policy) > +#define EXT4_IOC_GET_ENCRYPTION_METADATA _IOWR('f', 22, struct ext4_encrypted_metadata) > +#define EXT4_IOC_SET_ENCRYPTION_METADATA _IOR('f', 23, struct ext4_encrypted_metadata) > +#define EXT4_IOC_GET_ENCRYPTED_FILENAME _IOWR('f', 24, struct ext4_encrypted_metadata) > > #if defined(__KERNEL__) && defined(CONFIG_COMPAT) > /* > @@ -2311,6 +2314,10 @@ static inline void ext4_fname_free_filename(struct ext4_filename *fname) { } > void ext4_free_crypt_info(struct ext4_crypt_info *ci); > void ext4_free_encryption_info(struct inode *inode, struct ext4_crypt_info *ci); > int _ext4_get_encryption_info(struct inode *inode); > +int ext4_set_encryption_metadata(struct inode *inode, > + struct ext4_encrypted_metadata *mdata); > +int ext4_get_encryption_metadata(struct inode *inode, > + struct ext4_encrypted_metadata *mdata); > > #ifdef CONFIG_EXT4_FS_ENCRYPTION > int ext4_has_encryption_key(struct inode *inode); > @@ -2546,6 +2553,8 @@ extern int ext4_generic_delete_entry(handle_t *handle, > int buf_size, > int csum_size); > extern int ext4_empty_dir(struct inode *inode); > +extern int ext4_get_encrypted_filename(struct file *filp, > + struct ext4_encrypted_metadata *mdata); > > /* resize.c */ > extern int ext4_group_add(struct super_block *sb, > diff --git a/fs/ext4/ext4_crypto.h b/fs/ext4/ext4_crypto.h > index ac7d4e8..f267cd3 100644 > --- a/fs/ext4/ext4_crypto.h > +++ b/fs/ext4/ext4_crypto.h > @@ -156,4 +156,12 @@ static inline u32 encrypted_symlink_data_len(u32 l) > return (l + sizeof(struct ext4_encrypted_symlink_data) - 1); > } > > +/** > + * Structure used for communicating encrypted metadata with userspace > + */ > +struct ext4_encrypted_metadata { > + u32 len; > + char metadata[288]; > +}; > + > #endif /* _EXT4_CRYPTO_H */ > diff --git a/fs/ext4/ioctl.c b/fs/ext4/ioctl.c > index 5e872fd..afb51f5 100644 > --- a/fs/ext4/ioctl.c > +++ b/fs/ext4/ioctl.c > @@ -689,6 +689,67 @@ encryption_policy_out: > return -EOPNOTSUPP; > #endif > } > + case EXT4_IOC_GET_ENCRYPTION_METADATA: { > +#ifdef CONFIG_EXT4_FS_ENCRYPTION > + struct ext4_encrypted_metadata mdata; > + int err = 0; > + > + if (get_user(mdata.len, (u32 __user *) arg)) > + return -EFAULT; > + if (mdata.len > sizeof(mdata.metadata)) > + return -EINVAL; > + > + if (!ext4_encrypted_inode(inode)) > + return -ENOENT; > + err = ext4_get_encryption_metadata(inode, &mdata); > + if (err) > + return err; > + if (copy_to_user((void __user *)arg, &mdata, sizeof(mdata))) > + return -EFAULT; > + return 0; > +#else > + return -EOPNOTSUPP; > +#endif > + } > + case EXT4_IOC_SET_ENCRYPTION_METADATA: { > +#ifdef CONFIG_EXT4_FS_ENCRYPTION > + struct ext4_encrypted_metadata mdata; > + int err = 0; > + > + if (ext4_encrypted_inode(inode)) > + return -EINVAL; > + if (copy_from_user(&mdata, > + (struct ext4_encrypted_metadata __user *)arg, > + sizeof(mdata))) > + return -EFAULT; > + err = ext4_set_encryption_metadata(inode, &mdata); > + return err; > +#else > + return -EOPNOTSUPP; > +#endif > + } > + case EXT4_IOC_GET_ENCRYPTED_FILENAME: { > +#ifdef CONFIG_EXT4_FS_ENCRYPTION > + struct ext4_encrypted_metadata mdata; > + int err = 0; > + > + if (get_user(mdata.len, (u32 __user *) arg)) > + return -EFAULT; > + if (mdata.len > sizeof(mdata.metadata)) > + return -EINVAL; > + > + if (!ext4_encrypted_inode(inode)) > + return -ENOENT; > + err = ext4_get_encrypted_filename(filp, &mdata); > + if (err) > + return err; > + if (copy_to_user((void __user *)arg, &mdata, sizeof(mdata))) > + return -EFAULT; > + return 0; > +#else > + return -EOPNOTSUPP; > +#endif > + } > default: > return -ENOTTY; > } > @@ -755,6 +816,9 @@ long ext4_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) > case EXT4_IOC_SET_ENCRYPTION_POLICY: > case EXT4_IOC_GET_ENCRYPTION_PWSALT: > case EXT4_IOC_GET_ENCRYPTION_POLICY: > + case EXT4_IOC_GET_ENCRYPTION_METADATA: > + case EXT4_IOC_SET_ENCRYPTION_METADATA: > + case EXT4_IOC_GET_ENCRYPTED_FILENAME: > break; > default: > return -ENOIOCTLCMD; > diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c > index a969ab3..1cc4eef 100644 > --- a/fs/ext4/namei.c > +++ b/fs/ext4/namei.c > @@ -3861,3 +3861,27 @@ const struct inode_operations ext4_special_inode_operations = { > .get_acl = ext4_get_acl, > .set_acl = ext4_set_acl, > }; > + > +int ext4_get_encrypted_filename(struct file *filp, > + struct ext4_encrypted_metadata *mdata) > +{ > + struct dentry *dentry = filp->f_path.dentry; > + struct inode *dir = dentry->d_parent->d_inode; > + struct buffer_head *bh; > + struct ext4_dir_entry_2 *de; > + > + if (!dir || !ext4_encrypted_inode(dir)) > + return -EINVAL; > + > + bh = ext4_find_entry(dir, &dentry->d_name, &de, NULL); > + if (IS_ERR(bh)) > + return PTR_ERR(bh); > + if (de == NULL) > + return -ENOENT; > + > + if (mdata->len < de->name_len) > + return -ENOSPC; > + mdata->len = de->name_len; > + memcpy(mdata->metadata, de->name, de->name_len); > + return 0; > +} > -- > 2.5.0 > > -- > To unsubscribe from this list: send the line "unsubscribe linux-ext4" in > the body of a message to majordomo@vger.kernel.org > More majordomo info at http://vger.kernel.org/majordomo-info.html Cheers, Andreas
On Thu, Dec 10, 2015 at 04:48:56PM -0700, Andreas Dilger wrote: > > Instead of exposing these via ioctls, why not use virtual xattrs > (e.g. "trusted.crypt_meta" or similar) so that this can be backed > up and restored using a normal backup tool instead of basically > nothing that exists today. We're going to have to have an encrypted-aware backup tool anyway --- if for no other reason than to deal with the fact that if i_size is not a multiple of the cipher's block size, that we need to backup at least ROUND_UP(i_size, cipher_block_size) bytes, but we need to store the original i_size field somewhere else. The problem gets even worse to do encrypted backup *restores* because we need to be able to pass encrypted filenames that include NUL characters through the VFS, but the raw encrypted filename is different from the human-friendly encrypted filename which is what we want in the dentry cache. So I don't think it's worth the effort to try to make it possible to do backups using standard tools. Hmm, thinking about this some more, I might be able to support encrypted restores w/o having access to the key by using a magic ioctl that takes two file descriptors (one for the encrypted directory, and another one for the encrypted file, plus the encrypted filename), and do this bypassing the VFS layer entirely. It's something I may look at. But the point is this is even further away from an interface that could be hacked using xattrs. > For reading the ciphertext, do existing tools such as tar and rsync > allow reading with O_DIRECT with CIPHERTEXT_ACCESS? No. None of the standard tools understand that you might have to remount the file system with a special mount option. And neither tar nor rsync have an option so they will read their files using O_DIRECT. > If not, one might > consider to allow normal reads w/o the file key to return the > ciphertext so that "tar --xattr" by some process with CAP_SYS_ADMIN > but out any keys could be used to backup and restore the ciphertext > in a sensible manner. That is no more risk than the same process > reading the blocks from the disk device directly, but would be a lot > more useful (i.e. it is practical to use normal backup tools for > encrypted files). The reason why I'm using O_DIRECT is so we don't have to worry about potentially posioning the page cache with encrypted pages. In our current design, the page cache for an encrypted file always contains the plaintext. This is necessary so that mmap(2) will work correctly. So if user B is logged onto a machine where user A is active, and user A's keys are in the kernel, we don't determine access control descisions for user B based on whether or not the user has the appropriate keyring in a keyring accessible to them. We use Unix permissions to provide security isolation between two users which are logged in. In theory, when user A logs out, userspace should do the equivalent of "echo 3 > /proc/sys/vm/drop_caches" to drop the plaintext pages from the page cache. But I don't want the correctness of an encrypted backup to depend on that. Also, if someone wants to insist on doing an encrypted backup while the keys are present, that should work as well. That's why using O_DIRECT to bypass the page cache was a key part of the design, and why it's problematic to allow normal file reads to be used for this purpose. The bottom line is that it's impossible to use normal backup tools for encrypted files work correctly _anyway_ (because of the i_size / ciphertext block size rounding issue), and between wanting to allow for encrypted backups even if the user the keys are available, and the page cache poisioning issue, it's simpler all around to use specific ioctls. - Ted P.S. I actually have another use case in mind besides just encrypted backups, but (a) I'm not allowed to comment about features that might or might not show up in unreleased products, (b) you never know when pesky reporters from Android Central might be hanging about, and (c) being able to do encrypted backups is a feature which Dmitry Monakhov explicitly requested, and a mark of a good design is one that will work well for more than one use case. :-) -- To unsubscribe from this list: send the line "unsubscribe linux-ext4" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
diff --git a/fs/ext4/crypto_key.c b/fs/ext4/crypto_key.c index c5882b3..039b22d 100644 --- a/fs/ext4/crypto_key.c +++ b/fs/ext4/crypto_key.c @@ -14,7 +14,7 @@ #include <linux/scatterlist.h> #include <uapi/linux/keyctl.h> -#include "ext4.h" +#include "ext4_jbd2.h" #include "xattr.h" static void derive_crypt_complete(struct crypto_async_request *req, int rc) @@ -270,3 +270,58 @@ int ext4_has_encryption_key(struct inode *inode) return (ei->i_crypt_info != NULL); } + +int ext4_get_encryption_metadata(struct inode *inode, + struct ext4_encrypted_metadata *mdata) +{ + int res; + + if (mdata->len < sizeof(struct ext4_encryption_context)) + return -EINVAL; + + res = ext4_xattr_get(inode, EXT4_XATTR_INDEX_ENCRYPTION, + EXT4_XATTR_NAME_ENCRYPTION_CONTEXT, + &mdata->metadata, mdata->len); + if (res < 0) + return res; + mdata->len = res; + return 0; +} + +int ext4_set_encryption_metadata(struct inode *inode, + struct ext4_encrypted_metadata *mdata) +{ + struct ext4_encryption_context *ctx; + handle_t *handle; + int res; + + if (mdata->len != sizeof(struct ext4_encryption_context)) + return -EINVAL; + ctx = (struct ext4_encryption_context *) &mdata->metadata; + if (ctx->format != EXT4_ENCRYPTION_CONTEXT_FORMAT_V1) + return -EINVAL; + + res = ext4_convert_inline_data(inode); + if (res) + return res; + + handle = ext4_journal_start(inode, EXT4_HT_MISC, + ext4_jbd2_credits_xattr(inode)); + if (IS_ERR(handle)) + return PTR_ERR(handle); + res = ext4_xattr_set(inode, EXT4_XATTR_INDEX_ENCRYPTION, + EXT4_XATTR_NAME_ENCRYPTION_CONTEXT, ctx, + sizeof(struct ext4_encryption_context), 0); + if (res < 0) + goto errout; + ext4_set_inode_flag(inode, EXT4_INODE_ENCRYPT); + ext4_clear_inode_state(inode, EXT4_STATE_MAY_INLINE_DATA); + res = ext4_mark_inode_dirty(handle, inode); + if (res) + EXT4_ERROR_INODE(inode, "Failed to mark inode dirty"); + else + res = ext4_get_encryption_info(inode); +errout: + ext4_journal_stop(handle); + return res; +} diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index cf7a885..c569430 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -617,6 +617,9 @@ enum { #define EXT4_IOC_SET_ENCRYPTION_POLICY _IOR('f', 19, struct ext4_encryption_policy) #define EXT4_IOC_GET_ENCRYPTION_PWSALT _IOW('f', 20, __u8[16]) #define EXT4_IOC_GET_ENCRYPTION_POLICY _IOW('f', 21, struct ext4_encryption_policy) +#define EXT4_IOC_GET_ENCRYPTION_METADATA _IOWR('f', 22, struct ext4_encrypted_metadata) +#define EXT4_IOC_SET_ENCRYPTION_METADATA _IOR('f', 23, struct ext4_encrypted_metadata) +#define EXT4_IOC_GET_ENCRYPTED_FILENAME _IOWR('f', 24, struct ext4_encrypted_metadata) #if defined(__KERNEL__) && defined(CONFIG_COMPAT) /* @@ -2311,6 +2314,10 @@ static inline void ext4_fname_free_filename(struct ext4_filename *fname) { } void ext4_free_crypt_info(struct ext4_crypt_info *ci); void ext4_free_encryption_info(struct inode *inode, struct ext4_crypt_info *ci); int _ext4_get_encryption_info(struct inode *inode); +int ext4_set_encryption_metadata(struct inode *inode, + struct ext4_encrypted_metadata *mdata); +int ext4_get_encryption_metadata(struct inode *inode, + struct ext4_encrypted_metadata *mdata); #ifdef CONFIG_EXT4_FS_ENCRYPTION int ext4_has_encryption_key(struct inode *inode); @@ -2546,6 +2553,8 @@ extern int ext4_generic_delete_entry(handle_t *handle, int buf_size, int csum_size); extern int ext4_empty_dir(struct inode *inode); +extern int ext4_get_encrypted_filename(struct file *filp, + struct ext4_encrypted_metadata *mdata); /* resize.c */ extern int ext4_group_add(struct super_block *sb, diff --git a/fs/ext4/ext4_crypto.h b/fs/ext4/ext4_crypto.h index ac7d4e8..f267cd3 100644 --- a/fs/ext4/ext4_crypto.h +++ b/fs/ext4/ext4_crypto.h @@ -156,4 +156,12 @@ static inline u32 encrypted_symlink_data_len(u32 l) return (l + sizeof(struct ext4_encrypted_symlink_data) - 1); } +/** + * Structure used for communicating encrypted metadata with userspace + */ +struct ext4_encrypted_metadata { + u32 len; + char metadata[288]; +}; + #endif /* _EXT4_CRYPTO_H */ diff --git a/fs/ext4/ioctl.c b/fs/ext4/ioctl.c index 5e872fd..afb51f5 100644 --- a/fs/ext4/ioctl.c +++ b/fs/ext4/ioctl.c @@ -689,6 +689,67 @@ encryption_policy_out: return -EOPNOTSUPP; #endif } + case EXT4_IOC_GET_ENCRYPTION_METADATA: { +#ifdef CONFIG_EXT4_FS_ENCRYPTION + struct ext4_encrypted_metadata mdata; + int err = 0; + + if (get_user(mdata.len, (u32 __user *) arg)) + return -EFAULT; + if (mdata.len > sizeof(mdata.metadata)) + return -EINVAL; + + if (!ext4_encrypted_inode(inode)) + return -ENOENT; + err = ext4_get_encryption_metadata(inode, &mdata); + if (err) + return err; + if (copy_to_user((void __user *)arg, &mdata, sizeof(mdata))) + return -EFAULT; + return 0; +#else + return -EOPNOTSUPP; +#endif + } + case EXT4_IOC_SET_ENCRYPTION_METADATA: { +#ifdef CONFIG_EXT4_FS_ENCRYPTION + struct ext4_encrypted_metadata mdata; + int err = 0; + + if (ext4_encrypted_inode(inode)) + return -EINVAL; + if (copy_from_user(&mdata, + (struct ext4_encrypted_metadata __user *)arg, + sizeof(mdata))) + return -EFAULT; + err = ext4_set_encryption_metadata(inode, &mdata); + return err; +#else + return -EOPNOTSUPP; +#endif + } + case EXT4_IOC_GET_ENCRYPTED_FILENAME: { +#ifdef CONFIG_EXT4_FS_ENCRYPTION + struct ext4_encrypted_metadata mdata; + int err = 0; + + if (get_user(mdata.len, (u32 __user *) arg)) + return -EFAULT; + if (mdata.len > sizeof(mdata.metadata)) + return -EINVAL; + + if (!ext4_encrypted_inode(inode)) + return -ENOENT; + err = ext4_get_encrypted_filename(filp, &mdata); + if (err) + return err; + if (copy_to_user((void __user *)arg, &mdata, sizeof(mdata))) + return -EFAULT; + return 0; +#else + return -EOPNOTSUPP; +#endif + } default: return -ENOTTY; } @@ -755,6 +816,9 @@ long ext4_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) case EXT4_IOC_SET_ENCRYPTION_POLICY: case EXT4_IOC_GET_ENCRYPTION_PWSALT: case EXT4_IOC_GET_ENCRYPTION_POLICY: + case EXT4_IOC_GET_ENCRYPTION_METADATA: + case EXT4_IOC_SET_ENCRYPTION_METADATA: + case EXT4_IOC_GET_ENCRYPTED_FILENAME: break; default: return -ENOIOCTLCMD; diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c index a969ab3..1cc4eef 100644 --- a/fs/ext4/namei.c +++ b/fs/ext4/namei.c @@ -3861,3 +3861,27 @@ const struct inode_operations ext4_special_inode_operations = { .get_acl = ext4_get_acl, .set_acl = ext4_set_acl, }; + +int ext4_get_encrypted_filename(struct file *filp, + struct ext4_encrypted_metadata *mdata) +{ + struct dentry *dentry = filp->f_path.dentry; + struct inode *dir = dentry->d_parent->d_inode; + struct buffer_head *bh; + struct ext4_dir_entry_2 *de; + + if (!dir || !ext4_encrypted_inode(dir)) + return -EINVAL; + + bh = ext4_find_entry(dir, &dentry->d_name, &de, NULL); + if (IS_ERR(bh)) + return PTR_ERR(bh); + if (de == NULL) + return -ENOENT; + + if (mdata->len < de->name_len) + return -ENOSPC; + mdata->len = de->name_len; + memcpy(mdata->metadata, de->name, de->name_len); + return 0; +}
Add new ioctls which allow for the metadata of encrypted files (the filename and the crypto policy) to be backed up. We can restore the crypto policy, but for now we can't restore the encrypted filename because messing with encrypted directories directly while bypassing the VFS would get fairly tricky/nasty. Signed-off-by: Theodore Ts'o <tytso@mit.edu> --- fs/ext4/crypto_key.c | 57 ++++++++++++++++++++++++++++++++++++++++++++- fs/ext4/ext4.h | 9 ++++++++ fs/ext4/ext4_crypto.h | 8 +++++++ fs/ext4/ioctl.c | 64 +++++++++++++++++++++++++++++++++++++++++++++++++++ fs/ext4/namei.c | 24 +++++++++++++++++++ 5 files changed, 161 insertions(+), 1 deletion(-)