]> Git Repo - linux.git/commitdiff
Merge tag 'ext4_for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso...
authorLinus Torvalds <[email protected]>
Thu, 25 Jun 2015 21:06:55 +0000 (14:06 -0700)
committerLinus Torvalds <[email protected]>
Thu, 25 Jun 2015 21:06:55 +0000 (14:06 -0700)
Pull ext4 updates from Ted Ts'o:
 "A very large number of cleanups and bug fixes --- in particular for
  the ext4 encryption patches, which is a new feature added in the last
  merge window.  Also fix a number of long-standing xfstest failures.
  (Quota writes failing due to ENOSPC, a race between truncate and
  writepage in data=journalled mode that was causing generic/068 to
  fail, and other corner cases.)

  Also add support for FALLOC_FL_INSERT_RANGE, and improve jbd2
  performance eliminating locking when a buffer is modified more than
  once during a transaction (which is very common for allocation
  bitmaps, for example), in which case the state of the journalled
  buffer head doesn't need to change"

[ I renamed "ext4_follow_link()" to "ext4_encrypted_follow_link()" in
  the merge resolution, to make it clear that that function is _only_
  used for encrypted symlinks.  The function doesn't actually work for
  non-encrypted symlinks at all, and they use the generic helpers
                                         - Linus ]

* tag 'ext4_for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4: (52 commits)
  ext4: set lazytime on remount if MS_LAZYTIME is set by mount
  ext4: only call ext4_truncate when size <= isize
  ext4: make online defrag error reporting consistent
  ext4: minor cleanup of ext4_da_reserve_space()
  ext4: don't retry file block mapping on bigalloc fs with non-extent file
  ext4: prevent ext4_quota_write() from failing due to ENOSPC
  ext4: call sync_blockdev() before invalidate_bdev() in put_super()
  jbd2: speedup jbd2_journal_dirty_metadata()
  jbd2: get rid of open coded allocation retry loop
  ext4: improve warning directory handling messages
  jbd2: fix ocfs2 corrupt when updating journal superblock fails
  ext4: mballoc: avoid 20-argument function call
  ext4: wait for existing dio workers in ext4_alloc_file_blocks()
  ext4: recalculate journal credits as inode depth changes
  jbd2: use GFP_NOFS in jbd2_cleanup_journal_tail()
  ext4: use swap() in mext_page_double_lock()
  ext4: use swap() in memswap()
  ext4: fix race between truncate and __ext4_journalled_writepage()
  ext4 crypto: fail the mount if blocksize != pagesize
  ext4: Add support FALLOC_FL_INSERT_RANGE for fallocate
  ...

1  2 
fs/ext4/ext4.h
fs/ext4/inode.c
fs/ext4/namei.c
fs/ext4/symlink.c

diff --combined fs/ext4/ext4.h
index 0a3b72d1d458bd68834465a70fff3d323959f997,02a5762f19718314f9ff97f18171937633020b46..f5e9f04220c1d47814701590c825ccfbbb2c68ff
  #define ext_debug(fmt, ...)   no_printk(fmt, ##__VA_ARGS__)
  #endif
  
- #define EXT4_ERROR_INODE(inode, fmt, a...) \
-       ext4_error_inode((inode), __func__, __LINE__, 0, (fmt), ## a)
- #define EXT4_ERROR_INODE_BLOCK(inode, block, fmt, a...)                       \
-       ext4_error_inode((inode), __func__, __LINE__, (block), (fmt), ## a)
- #define EXT4_ERROR_FILE(file, block, fmt, a...)                               \
-       ext4_error_file((file), __func__, __LINE__, (block), (fmt), ## a)
  /* data type for block offset of block group */
  typedef int ext4_grpblk_t;
  
@@@ -90,6 -81,11 +81,11 @@@ typedef __u32 ext4_lblk_t
  /* data type for block group number */
  typedef unsigned int ext4_group_t;
  
+ enum SHIFT_DIRECTION {
+       SHIFT_LEFT = 0,
+       SHIFT_RIGHT,
+ };
  /*
   * Flags used in mballoc's allocation_context flags field.
   *
@@@ -911,7 -907,6 +907,6 @@@ struct ext4_inode_info 
  
        /* on-disk additional length */
        __u16 i_extra_isize;
-       char i_crypt_policy_flags;
  
        /* Indicate the inline data space. */
        u16 i_inline_off;
  
  #ifdef CONFIG_EXT4_FS_ENCRYPTION
        /* Encryption params */
-       struct ext4_encryption_key i_encryption_key;
+       struct ext4_crypt_info *i_crypt_info;
  #endif
  };
  
@@@ -1374,12 -1369,6 +1369,6 @@@ struct ext4_sb_info 
        struct ratelimit_state s_err_ratelimit_state;
        struct ratelimit_state s_warning_ratelimit_state;
        struct ratelimit_state s_msg_ratelimit_state;
- #ifdef CONFIG_EXT4_FS_ENCRYPTION
-       /* Encryption */
-       uint32_t s_file_encryption_mode;
-       uint32_t s_dir_encryption_mode;
- #endif
  };
  
  static inline struct ext4_sb_info *EXT4_SB(struct super_block *sb)
@@@ -1838,6 -1827,17 +1827,17 @@@ struct dx_hash_inf
   */
  #define HASH_NB_ALWAYS                1
  
+ struct ext4_filename {
+       const struct qstr *usr_fname;
+       struct ext4_str disk_name;
+       struct dx_hash_info hinfo;
+ #ifdef CONFIG_EXT4_FS_ENCRYPTION
+       struct ext4_str crypto_buf;
+ #endif
+ };
+ #define fname_name(p) ((p)->disk_name.name)
+ #define fname_len(p)  ((p)->disk_name.len)
  
  /*
   * Describe an inode's exact location on disk and in memory
@@@ -2054,6 -2054,7 +2054,7 @@@ int ext4_get_policy(struct inode *inode
                    struct ext4_encryption_policy *policy);
  
  /* crypto.c */
+ extern struct kmem_cache *ext4_crypt_info_cachep;
  bool ext4_valid_contents_enc_mode(uint32_t mode);
  uint32_t ext4_validate_encryption_key_size(uint32_t mode, uint32_t size);
  extern struct workqueue_struct *ext4_read_workqueue;
@@@ -2085,57 -2086,84 +2086,84 @@@ static inline int ext4_sb_has_crypto(st
  /* crypto_fname.c */
  bool ext4_valid_filenames_enc_mode(uint32_t mode);
  u32 ext4_fname_crypto_round_up(u32 size, u32 blksize);
- int ext4_fname_crypto_alloc_buffer(struct ext4_fname_crypto_ctx *ctx,
+ unsigned ext4_fname_encrypted_size(struct inode *inode, u32 ilen);
+ int ext4_fname_crypto_alloc_buffer(struct inode *inode,
                                   u32 ilen, struct ext4_str *crypto_str);
- int _ext4_fname_disk_to_usr(struct ext4_fname_crypto_ctx *ctx,
+ int _ext4_fname_disk_to_usr(struct inode *inode,
                            struct dx_hash_info *hinfo,
                            const struct ext4_str *iname,
                            struct ext4_str *oname);
- int ext4_fname_disk_to_usr(struct ext4_fname_crypto_ctx *ctx,
+ int ext4_fname_disk_to_usr(struct inode *inode,
                           struct dx_hash_info *hinfo,
                           const struct ext4_dir_entry_2 *de,
                           struct ext4_str *oname);
- int ext4_fname_usr_to_disk(struct ext4_fname_crypto_ctx *ctx,
+ int ext4_fname_usr_to_disk(struct inode *inode,
                           const struct qstr *iname,
                           struct ext4_str *oname);
- int ext4_fname_usr_to_hash(struct ext4_fname_crypto_ctx *ctx,
-                          const struct qstr *iname,
-                          struct dx_hash_info *hinfo);
- int ext4_fname_crypto_namelen_on_disk(struct ext4_fname_crypto_ctx *ctx,
-                                     u32 namelen);
- int ext4_fname_match(struct ext4_fname_crypto_ctx *ctx, struct ext4_str *cstr,
-                    int len, const char * const name,
-                    struct ext4_dir_entry_2 *de);
  #ifdef CONFIG_EXT4_FS_ENCRYPTION
- void ext4_put_fname_crypto_ctx(struct ext4_fname_crypto_ctx **ctx);
- struct ext4_fname_crypto_ctx *ext4_get_fname_crypto_ctx(struct inode *inode,
-                                                       u32 max_len);
  void ext4_fname_crypto_free_buffer(struct ext4_str *crypto_str);
+ int ext4_fname_setup_filename(struct inode *dir, const struct qstr *iname,
+                             int lookup, struct ext4_filename *fname);
+ void ext4_fname_free_filename(struct ext4_filename *fname);
  #else
  static inline
- void ext4_put_fname_crypto_ctx(struct ext4_fname_crypto_ctx **ctx) { }
- static inline
- struct ext4_fname_crypto_ctx *ext4_get_fname_crypto_ctx(struct inode *inode,
-                                                       u32 max_len)
+ int ext4_setup_fname_crypto(struct inode *inode)
  {
-       return NULL;
+       return 0;
  }
  static inline void ext4_fname_crypto_free_buffer(struct ext4_str *p) { }
+ static inline int ext4_fname_setup_filename(struct inode *dir,
+                                    const struct qstr *iname,
+                                    int lookup, struct ext4_filename *fname)
+ {
+       fname->usr_fname = iname;
+       fname->disk_name.name = (unsigned char *) iname->name;
+       fname->disk_name.len = iname->len;
+       return 0;
+ }
+ static inline void ext4_fname_free_filename(struct ext4_filename *fname) { }
  #endif
  
  
  /* crypto_key.c */
- int ext4_generate_encryption_key(struct inode *inode);
+ 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);
  
  #ifdef CONFIG_EXT4_FS_ENCRYPTION
  int ext4_has_encryption_key(struct inode *inode);
+ static inline int ext4_get_encryption_info(struct inode *inode)
+ {
+       struct ext4_crypt_info *ci = EXT4_I(inode)->i_crypt_info;
+       if (!ci ||
+           (ci->ci_keyring_key &&
+            (ci->ci_keyring_key->flags & ((1 << KEY_FLAG_INVALIDATED) |
+                                          (1 << KEY_FLAG_REVOKED) |
+                                          (1 << KEY_FLAG_DEAD)))))
+               return _ext4_get_encryption_info(inode);
+       return 0;
+ }
+ static inline struct ext4_crypt_info *ext4_encryption_info(struct inode *inode)
+ {
+       return EXT4_I(inode)->i_crypt_info;
+ }
  #else
  static inline int ext4_has_encryption_key(struct inode *inode)
  {
        return 0;
  }
+ static inline int ext4_get_encryption_info(struct inode *inode)
+ {
+       return 0;
+ }
+ static inline struct ext4_crypt_info *ext4_encryption_info(struct inode *inode)
+ {
+       return NULL;
+ }
  #endif
  
  
@@@ -2156,14 -2184,13 +2184,13 @@@ extern void ext4_htree_free_dir_info(st
  extern int ext4_find_dest_de(struct inode *dir, struct inode *inode,
                             struct buffer_head *bh,
                             void *buf, int buf_size,
-                            const char *name, int namelen,
+                            struct ext4_filename *fname,
                             struct ext4_dir_entry_2 **dest_de);
  int ext4_insert_dentry(struct inode *dir,
-                       struct inode *inode,
-                       struct ext4_dir_entry_2 *de,
-                       int buf_size,
-                      const struct qstr *iname,
-                       const char *name, int namelen);
+                      struct inode *inode,
+                      struct ext4_dir_entry_2 *de,
+                      int buf_size,
+                      struct ext4_filename *fname);
  static inline void ext4_update_dx_flag(struct inode *inode)
  {
        if (!EXT4_HAS_COMPAT_FEATURE(inode->i_sb,
@@@ -2317,13 -2344,14 +2344,14 @@@ extern int ext4_orphan_add(handle_t *, 
  extern int ext4_orphan_del(handle_t *, struct inode *);
  extern int ext4_htree_fill_tree(struct file *dir_file, __u32 start_hash,
                                __u32 start_minor_hash, __u32 *next_hash);
- extern int search_dir(struct buffer_head *bh,
-                     char *search_buf,
-                     int buf_size,
-                     struct inode *dir,
-                     const struct qstr *d_name,
-                     unsigned int offset,
-                     struct ext4_dir_entry_2 **res_dir);
+ extern int ext4_search_dir(struct buffer_head *bh,
+                          char *search_buf,
+                          int buf_size,
+                          struct inode *dir,
+                          struct ext4_filename *fname,
+                          const struct qstr *d_name,
+                          unsigned int offset,
+                          struct ext4_dir_entry_2 **res_dir);
  extern int ext4_generic_delete_entry(handle_t *handle,
                                     struct inode *dir,
                                     struct ext4_dir_entry_2 *de_del,
@@@ -2368,6 -2396,9 +2396,9 @@@ void __ext4_abort(struct super_block *
  extern __printf(4, 5)
  void __ext4_warning(struct super_block *, const char *, unsigned int,
                    const char *, ...);
+ extern __printf(4, 5)
+ void __ext4_warning_inode(const struct inode *inode, const char *function,
+                         unsigned int line, const char *fmt, ...);
  extern __printf(3, 4)
  void __ext4_msg(struct super_block *, const char *, const char *, ...);
  extern void __dump_mmp_msg(struct super_block *, struct mmp_struct *mmp,
@@@ -2378,6 -2409,15 +2409,15 @@@ void __ext4_grp_locked_error(const cha
                             unsigned long, ext4_fsblk_t,
                             const char *, ...);
  
+ #define EXT4_ERROR_INODE(inode, fmt, a...) \
+       ext4_error_inode((inode), __func__, __LINE__, 0, (fmt), ## a)
+ #define EXT4_ERROR_INODE_BLOCK(inode, block, fmt, a...)                       \
+       ext4_error_inode((inode), __func__, __LINE__, (block), (fmt), ## a)
+ #define EXT4_ERROR_FILE(file, block, fmt, a...)                               \
+       ext4_error_file((file), __func__, __LINE__, (block), (fmt), ## a)
  #ifdef CONFIG_PRINTK
  
  #define ext4_error_inode(inode, func, line, block, fmt, ...)          \
        __ext4_abort(sb, __func__, __LINE__, fmt, ##__VA_ARGS__)
  #define ext4_warning(sb, fmt, ...)                                    \
        __ext4_warning(sb, __func__, __LINE__, fmt, ##__VA_ARGS__)
+ #define ext4_warning_inode(inode, fmt, ...)                           \
+       __ext4_warning_inode(inode, __func__, __LINE__, fmt, ##__VA_ARGS__)
  #define ext4_msg(sb, level, fmt, ...)                         \
        __ext4_msg(sb, level, fmt, ##__VA_ARGS__)
  #define dump_mmp_msg(sb, mmp, msg)                                    \
@@@ -2425,6 -2467,11 +2467,11 @@@ do {                                                                  
        no_printk(fmt, ##__VA_ARGS__);                                  \
        __ext4_warning(sb, "", 0, " ");                                 \
  } while (0)
+ #define ext4_warning_inode(inode, fmt, ...)                           \
+ do {                                                                  \
+       no_printk(fmt, ##__VA_ARGS__);                                  \
+       __ext4_warning_inode(inode, "", 0, " ");                        \
+ } while (0)
  #define ext4_msg(sb, level, fmt, ...)                                 \
  do {                                                                  \
        no_printk(fmt, ##__VA_ARGS__);                                  \
@@@ -2768,7 -2815,9 +2815,9 @@@ extern int ext4_da_write_inline_data_be
  extern int ext4_da_write_inline_data_end(struct inode *inode, loff_t pos,
                                         unsigned len, unsigned copied,
                                         struct page *page);
- extern int ext4_try_add_inline_entry(handle_t *handle, struct dentry *dentry,
+ extern int ext4_try_add_inline_entry(handle_t *handle,
+                                    struct ext4_filename *fname,
+                                    struct dentry *dentry,
                                     struct inode *inode);
  extern int ext4_try_create_inline_dir(handle_t *handle,
                                      struct inode *parent,
@@@ -2782,6 -2831,7 +2831,7 @@@ extern int htree_inlinedir_to_tree(stru
                                   __u32 start_hash, __u32 start_minor_hash,
                                   int *has_inline_data);
  extern struct buffer_head *ext4_find_inline_entry(struct inode *dir,
+                                       struct ext4_filename *fname,
                                        const struct qstr *d_name,
                                        struct ext4_dir_entry_2 **res_dir,
                                        int *has_inline_data);
@@@ -2847,7 -2897,6 +2897,7 @@@ extern int ext4_mpage_readpages(struct 
                                unsigned nr_pages);
  
  /* symlink.c */
 +extern const struct inode_operations ext4_encrypted_symlink_inode_operations;
  extern const struct inode_operations ext4_symlink_inode_operations;
  extern const struct inode_operations ext4_fast_symlink_inode_operations;
  
@@@ -2913,6 -2962,7 +2963,7 @@@ extern int ext4_fiemap(struct inode *in
                        __u64 start, __u64 len);
  extern int ext4_ext_precache(struct inode *inode);
  extern int ext4_collapse_range(struct inode *inode, loff_t offset, loff_t len);
+ extern int ext4_insert_range(struct inode *inode, loff_t offset, loff_t len);
  extern int ext4_swap_extents(handle_t *handle, struct inode *inode1,
                                struct inode *inode2, ext4_lblk_t lblk1,
                             ext4_lblk_t lblk2,  ext4_lblk_t count,
diff --combined fs/ext4/inode.c
index 5168c9b568809d81f66cd4813bc25cba5d669fc9,e057c6fcc227c200a044c8406c7a6023c522f2bf..f8a8d4ee7459f9a82af3a3d489cc5265915da1a9
@@@ -731,18 -731,18 +731,18 @@@ int ext4_get_block(struct inode *inode
   * `handle' can be NULL if create is zero
   */
  struct buffer_head *ext4_getblk(handle_t *handle, struct inode *inode,
-                               ext4_lblk_t block, int create)
+                               ext4_lblk_t block, int map_flags)
  {
        struct ext4_map_blocks map;
        struct buffer_head *bh;
+       int create = map_flags & EXT4_GET_BLOCKS_CREATE;
        int err;
  
        J_ASSERT(handle != NULL || create == 0);
  
        map.m_lblk = block;
        map.m_len = 1;
-       err = ext4_map_blocks(handle, inode, &map,
-                             create ? EXT4_GET_BLOCKS_CREATE : 0);
+       err = ext4_map_blocks(handle, inode, &map, map_flags);
  
        if (err == 0)
                return create ? ERR_PTR(-ENOSPC) : NULL;
@@@ -788,11 -788,11 +788,11 @@@ errout
  }
  
  struct buffer_head *ext4_bread(handle_t *handle, struct inode *inode,
-                              ext4_lblk_t block, int create)
+                              ext4_lblk_t block, int map_flags)
  {
        struct buffer_head *bh;
  
-       bh = ext4_getblk(handle, inode, block, create);
+       bh = ext4_getblk(handle, inode, block, map_flags);
        if (IS_ERR(bh))
                return bh;
        if (!bh || buffer_uptodate(bh))
@@@ -1261,13 -1261,12 +1261,12 @@@ static int ext4_journalled_write_end(st
  }
  
  /*
-  * Reserve a single cluster located at lblock
+  * Reserve space for a single cluster
   */
- static int ext4_da_reserve_space(struct inode *inode, ext4_lblk_t lblock)
+ static int ext4_da_reserve_space(struct inode *inode)
  {
        struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb);
        struct ext4_inode_info *ei = EXT4_I(inode);
-       unsigned int md_needed;
        int ret;
  
        /*
        if (ret)
                return ret;
  
-       /*
-        * recalculate the amount of metadata blocks to reserve
-        * in order to allocate nrblocks
-        * worse case is one extent per block
-        */
        spin_lock(&ei->i_block_reservation_lock);
-       /*
-        * ext4_calc_metadata_amount() has side effects, which we have
-        * to be prepared undo if we fail to claim space.
-        */
-       md_needed = 0;
-       trace_ext4_da_reserve_space(inode, 0);
        if (ext4_claim_free_clusters(sbi, 1, 0)) {
                spin_unlock(&ei->i_block_reservation_lock);
                dquot_release_reservation_block(inode, EXT4_C2B(sbi, 1));
                return -ENOSPC;
        }
        ei->i_reserved_data_blocks++;
+       trace_ext4_da_reserve_space(inode);
        spin_unlock(&ei->i_block_reservation_lock);
  
        return 0;       /* success */
@@@ -1566,9 -1554,9 +1554,9 @@@ add_delayed
                 * then we don't need to reserve it again. However we still need
                 * to reserve metadata for every block we're going to write.
                 */
-               if (EXT4_SB(inode->i_sb)->s_cluster_ratio <= 1 ||
+               if (EXT4_SB(inode->i_sb)->s_cluster_ratio == 1 ||
                    !ext4_find_delalloc_cluster(inode, map->m_lblk)) {
-                       ret = ext4_da_reserve_space(inode, iblock);
+                       ret = ext4_da_reserve_space(inode);
                        if (ret) {
                                /* not enough space to reserve */
                                retval = ret;
@@@ -1701,19 -1689,32 +1689,32 @@@ static int __ext4_journalled_writepage(
                ext4_walk_page_buffers(handle, page_bufs, 0, len,
                                       NULL, bget_one);
        }
-       /* As soon as we unlock the page, it can go away, but we have
-        * references to buffers so we are safe */
+       /*
+        * We need to release the page lock before we start the
+        * journal, so grab a reference so the page won't disappear
+        * out from under us.
+        */
+       get_page(page);
        unlock_page(page);
  
        handle = ext4_journal_start(inode, EXT4_HT_WRITE_PAGE,
                                    ext4_writepage_trans_blocks(inode));
        if (IS_ERR(handle)) {
                ret = PTR_ERR(handle);
-               goto out;
+               put_page(page);
+               goto out_no_pagelock;
        }
        BUG_ON(!ext4_handle_valid(handle));
  
+       lock_page(page);
+       put_page(page);
+       if (page->mapping != mapping) {
+               /* The page got truncated from under us */
+               ext4_journal_stop(handle);
+               ret = 0;
+               goto out;
+       }
        if (inline_data) {
                BUFFER_TRACE(inode_bh, "get write access");
                ret = ext4_journal_get_write_access(handle, inode_bh);
                                       NULL, bput_one);
        ext4_set_inode_state(inode, EXT4_STATE_JDATA);
  out:
+       unlock_page(page);
+ out_no_pagelock:
        brelse(inode_bh);
        return ret;
  }
@@@ -4213,11 -4216,8 +4216,11 @@@ struct inode *ext4_iget(struct super_bl
                inode->i_op = &ext4_dir_inode_operations;
                inode->i_fop = &ext4_dir_operations;
        } else if (S_ISLNK(inode->i_mode)) {
 -              if (ext4_inode_is_fast_symlink(inode) &&
 -                  !ext4_encrypted_inode(inode)) {
 +              if (ext4_encrypted_inode(inode)) {
 +                      inode->i_op = &ext4_encrypted_symlink_inode_operations;
 +                      ext4_set_aops(inode);
 +              } else if (ext4_inode_is_fast_symlink(inode)) {
 +                      inode->i_link = (char *)ei->i_data;
                        inode->i_op = &ext4_fast_symlink_inode_operations;
                        nd_terminate_link(ei->i_data, inode->i_size,
                                sizeof(ei->i_data) - 1);
@@@ -4681,8 -4681,10 +4684,10 @@@ int ext4_setattr(struct dentry *dentry
                ext4_journal_stop(handle);
        }
  
-       if (attr->ia_valid & ATTR_SIZE && attr->ia_size != inode->i_size) {
+       if (attr->ia_valid & ATTR_SIZE) {
                handle_t *handle;
+               loff_t oldsize = inode->i_size;
+               int shrink = (attr->ia_size <= inode->i_size);
  
                if (!(ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS))) {
                        struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb);
                        if (attr->ia_size > sbi->s_bitmap_maxbytes)
                                return -EFBIG;
                }
+               if (!S_ISREG(inode->i_mode))
+                       return -EINVAL;
  
                if (IS_I_VERSION(inode) && attr->ia_size != inode->i_size)
                        inode_inc_iversion(inode);
  
-               if (S_ISREG(inode->i_mode) &&
+               if (ext4_should_order_data(inode) &&
                    (attr->ia_size < inode->i_size)) {
-                       if (ext4_should_order_data(inode)) {
-                               error = ext4_begin_ordered_truncate(inode,
+                       error = ext4_begin_ordered_truncate(inode,
                                                            attr->ia_size);
-                               if (error)
-                                       goto err_out;
-                       }
+                       if (error)
+                               goto err_out;
+               }
+               if (attr->ia_size != inode->i_size) {
                        handle = ext4_journal_start(inode, EXT4_HT_INODE, 3);
                        if (IS_ERR(handle)) {
                                error = PTR_ERR(handle);
                                goto err_out;
                        }
-                       if (ext4_handle_valid(handle)) {
+                       if (ext4_handle_valid(handle) && shrink) {
                                error = ext4_orphan_add(handle, inode);
                                orphan = 1;
                        }
                        up_write(&EXT4_I(inode)->i_data_sem);
                        ext4_journal_stop(handle);
                        if (error) {
-                               ext4_orphan_del(NULL, inode);
+                               if (orphan)
+                                       ext4_orphan_del(NULL, inode);
                                goto err_out;
                        }
-               } else {
-                       loff_t oldsize = inode->i_size;
-                       i_size_write(inode, attr->ia_size);
-                       pagecache_isize_extended(inode, oldsize, inode->i_size);
                }
+               if (!shrink)
+                       pagecache_isize_extended(inode, oldsize, inode->i_size);
  
                /*
                 * Blocks are going to be removed from the inode. Wait
                 * in data=journal mode to make pages freeable.
                 */
                truncate_pagecache(inode, inode->i_size);
+               if (shrink)
+                       ext4_truncate(inode);
        }
-       /*
-        * We want to call ext4_truncate() even if attr->ia_size ==
-        * inode->i_size for cases like truncation of fallocated space
-        */
-       if (attr->ia_valid & ATTR_SIZE)
-               ext4_truncate(inode);
  
        if (!rc) {
                setattr_copy(inode, attr);
diff --combined fs/ext4/namei.c
index 5fdb9f6aa869445ca9893751393e4822b2e0ed63,e230b31251f7f18de48e70f64f794d36c71ecb5e..011dcfb5cce37688d2cc6153526f5741204a66d9
@@@ -61,7 -61,7 +61,7 @@@ static struct buffer_head *ext4_append(
  
        *block = inode->i_size >> inode->i_sb->s_blocksize_bits;
  
-       bh = ext4_bread(handle, inode, *block, 1);
+       bh = ext4_bread(handle, inode, *block, EXT4_GET_BLOCKS_CREATE);
        if (IS_ERR(bh))
                return bh;
        inode->i_size += inode->i_sb->s_blocksize;
@@@ -84,12 -84,13 +84,13 @@@ typedef enum 
  } dirblock_type_t;
  
  #define ext4_read_dirblock(inode, block, type) \
-       __ext4_read_dirblock((inode), (block), (type), __LINE__)
+       __ext4_read_dirblock((inode), (block), (type), __func__, __LINE__)
  
  static struct buffer_head *__ext4_read_dirblock(struct inode *inode,
-                                             ext4_lblk_t block,
-                                             dirblock_type_t type,
-                                             unsigned int line)
+                                               ext4_lblk_t block,
+                                               dirblock_type_t type,
+                                               const char *func,
+                                               unsigned int line)
  {
        struct buffer_head *bh;
        struct ext4_dir_entry *dirent;
  
        bh = ext4_bread(NULL, inode, block, 0);
        if (IS_ERR(bh)) {
-               __ext4_warning(inode->i_sb, __func__, line,
-                              "error %ld reading directory block "
-                              "(ino %lu, block %lu)", PTR_ERR(bh), inode->i_ino,
-                              (unsigned long) block);
+               __ext4_warning(inode->i_sb, func, line,
+                              "inode #%lu: lblock %lu: comm %s: "
+                              "error %ld reading directory block",
+                              inode->i_ino, (unsigned long)block,
+                              current->comm, PTR_ERR(bh));
  
                return bh;
        }
        if (!bh) {
-               ext4_error_inode(inode, __func__, line, block, "Directory hole found");
+               ext4_error_inode(inode, func, line, block,
+                                "Directory hole found");
                return ERR_PTR(-EIO);
        }
        dirent = (struct ext4_dir_entry *) bh->b_data;
                        is_dx_block = 1;
        }
        if (!is_dx_block && type == INDEX) {
-               ext4_error_inode(inode, __func__, line, block,
+               ext4_error_inode(inode, func, line, block,
                       "directory leaf block found instead of index block");
                return ERR_PTR(-EIO);
        }
                if (ext4_dx_csum_verify(inode, dirent))
                        set_buffer_verified(bh);
                else {
-                       ext4_error_inode(inode, __func__, line, block,
-                               "Directory index failed checksum");
+                       ext4_error_inode(inode, func, line, block,
+                                        "Directory index failed checksum");
                        brelse(bh);
                        return ERR_PTR(-EIO);
                }
                if (ext4_dirent_csum_verify(inode, dirent))
                        set_buffer_verified(bh);
                else {
-                       ext4_error_inode(inode, __func__, line, block,
-                               "Directory block failed checksum");
+                       ext4_error_inode(inode, func, line, block,
+                                        "Directory block failed checksum");
                        brelse(bh);
                        return ERR_PTR(-EIO);
                }
@@@ -248,7 -251,7 +251,7 @@@ static void dx_set_count(struct dx_entr
  static void dx_set_limit(struct dx_entry *entries, unsigned value);
  static unsigned dx_root_limit(struct inode *dir, unsigned infosize);
  static unsigned dx_node_limit(struct inode *dir);
- static struct dx_frame *dx_probe(const struct qstr *d_name,
+ static struct dx_frame *dx_probe(struct ext4_filename *fname,
                                 struct inode *dir,
                                 struct dx_hash_info *hinfo,
                                 struct dx_frame *frame);
@@@ -267,10 -270,10 +270,10 @@@ static int ext4_htree_next_block(struc
                                 struct dx_frame *frames,
                                 __u32 *start_hash);
  static struct buffer_head * ext4_dx_find_entry(struct inode *dir,
-               const struct qstr *d_name,
+               struct ext4_filename *fname,
                struct ext4_dir_entry_2 **res_dir);
- static int ext4_dx_add_entry(handle_t *handle, struct dentry *dentry,
-                            struct inode *inode);
+ static int ext4_dx_add_entry(handle_t *handle, struct ext4_filename *fname,
+                            struct dentry *dentry, struct inode *inode);
  
  /* checksumming functions */
  void initialize_dirent_tail(struct ext4_dir_entry_tail *t,
@@@ -327,10 -330,14 +330,14 @@@ static __le32 ext4_dirent_csum(struct i
        return cpu_to_le32(csum);
  }
  
- static void warn_no_space_for_csum(struct inode *inode)
+ #define warn_no_space_for_csum(inode)                                 \
+       __warn_no_space_for_csum((inode), __func__, __LINE__)
+ static void __warn_no_space_for_csum(struct inode *inode, const char *func,
+                                    unsigned int line)
  {
-       ext4_warning(inode->i_sb, "no space in directory inode %lu leaf for "
-                    "checksum.  Please run e2fsck -D.", inode->i_ino);
+       __ext4_warning_inode(inode, func, line,
+               "No space for directory leaf checksum. Please run e2fsck -D.");
  }
  
  int ext4_dirent_csum_verify(struct inode *inode, struct ext4_dir_entry *dirent)
@@@ -607,17 -614,15 +614,15 @@@ static struct stats dx_show_leaf(struc
                                char *name;
                                struct ext4_str fname_crypto_str
                                        = {.name = NULL, .len = 0};
-                               struct ext4_fname_crypto_ctx *ctx = NULL;
-                               int res;
+                               int res = 0;
  
                                name  = de->name;
                                len = de->name_len;
-                               ctx = ext4_get_fname_crypto_ctx(dir,
-                                                               EXT4_NAME_LEN);
-                               if (IS_ERR(ctx)) {
-                                       printk(KERN_WARNING "Error acquiring"
-                                       " crypto ctxt--skipping crypto\n");
-                                       ctx = NULL;
+                               if (ext4_encrypted_inode(inode))
+                                       res = ext4_get_encryption_info(dir);
+                               if (res) {
+                                       printk(KERN_WARNING "Error setting up"
+                                              " fname crypto: %d\n", res);
                                }
                                if (ctx == NULL) {
                                        /* Directory is not encrypted */
                                                        "allocating crypto "
                                                        "buffer--skipping "
                                                        "crypto\n");
-                                               ext4_put_fname_crypto_ctx(&ctx);
                                                ctx = NULL;
                                        }
                                        res = ext4_fname_disk_to_usr(ctx, NULL, de,
                                        printk("%*.s:(E)%x.%u ", len, name,
                                               h.hash, (unsigned) ((char *) de
                                                                   - base));
-                                       ext4_put_fname_crypto_ctx(&ctx);
                                        ext4_fname_crypto_free_buffer(
                                                &fname_crypto_str);
                                }
@@@ -724,7 -727,7 +727,7 @@@ struct stats dx_show_entries(struct dx_
   * back to userspace.
   */
  static struct dx_frame *
- dx_probe(const struct qstr *d_name, struct inode *dir,
+ dx_probe(struct ext4_filename *fname, struct inode *dir,
         struct dx_hash_info *hinfo, struct dx_frame *frame_in)
  {
        unsigned count, indirect;
        if (root->info.hash_version != DX_HASH_TEA &&
            root->info.hash_version != DX_HASH_HALF_MD4 &&
            root->info.hash_version != DX_HASH_LEGACY) {
-               ext4_warning(dir->i_sb, "Unrecognised inode hash code %d",
-                            root->info.hash_version);
+               ext4_warning_inode(dir, "Unrecognised inode hash code %u",
+                                  root->info.hash_version);
                goto fail;
        }
+       if (fname)
+               hinfo = &fname->hinfo;
        hinfo->hash_version = root->info.hash_version;
        if (hinfo->hash_version <= DX_HASH_TEA)
                hinfo->hash_version += EXT4_SB(dir->i_sb)->s_hash_unsigned;
        hinfo->seed = EXT4_SB(dir->i_sb)->s_hash_seed;
- #ifdef CONFIG_EXT4_FS_ENCRYPTION
-       if (d_name) {
-               struct ext4_fname_crypto_ctx *ctx = NULL;
-               int res;
-               /* Check if the directory is encrypted */
-               ctx = ext4_get_fname_crypto_ctx(dir, EXT4_NAME_LEN);
-               if (IS_ERR(ctx)) {
-                       ret_err = ERR_PTR(PTR_ERR(ctx));
-                       goto fail;
-               }
-               res = ext4_fname_usr_to_hash(ctx, d_name, hinfo);
-               if (res < 0) {
-                       ret_err = ERR_PTR(res);
-                       goto fail;
-               }
-               ext4_put_fname_crypto_ctx(&ctx);
-       }
- #else
-       if (d_name)
-               ext4fs_dirhash(d_name->name, d_name->len, hinfo);
- #endif
+       if (fname && fname_name(fname))
+               ext4fs_dirhash(fname_name(fname), fname_len(fname), hinfo);
        hash = hinfo->hash;
  
        if (root->info.unused_flags & 1) {
-               ext4_warning(dir->i_sb, "Unimplemented inode hash flags: %#06x",
-                            root->info.unused_flags);
+               ext4_warning_inode(dir, "Unimplemented hash flags: %#06x",
+                                  root->info.unused_flags);
                goto fail;
        }
  
-       if ((indirect = root->info.indirect_levels) > 1) {
-               ext4_warning(dir->i_sb, "Unimplemented inode hash depth: %#06x",
-                            root->info.indirect_levels);
+       indirect = root->info.indirect_levels;
+       if (indirect > 1) {
+               ext4_warning_inode(dir, "Unimplemented hash depth: %#06x",
+                                  root->info.indirect_levels);
                goto fail;
        }
  
-       entries = (struct dx_entry *) (((char *)&root->info) +
-                                      root->info.info_length);
+       entries = (struct dx_entry *)(((char *)&root->info) +
+                                     root->info.info_length);
  
        if (dx_get_limit(entries) != dx_root_limit(dir,
                                                   root->info.info_length)) {
-               ext4_warning(dir->i_sb, "dx entry: limit != root limit");
+               ext4_warning_inode(dir, "dx entry: limit %u != root limit %u",
+                                  dx_get_limit(entries),
+                                  dx_root_limit(dir, root->info.info_length));
                goto fail;
        }
  
        while (1) {
                count = dx_get_count(entries);
                if (!count || count > dx_get_limit(entries)) {
-                       ext4_warning(dir->i_sb,
-                                    "dx entry: no count or count > limit");
+                       ext4_warning_inode(dir,
+                                          "dx entry: count %u beyond limit %u",
+                                          count, dx_get_limit(entries));
                        goto fail;
                }
  
                p = entries + 1;
                q = entries + count - 1;
                while (p <= q) {
-                       m = p + (q - p)/2;
+                       m = p + (q - p) / 2;
                        dxtrace(printk("."));
                        if (dx_get_hash(m) > hash)
                                q = m - 1;
                }
  
                at = p - 1;
-               dxtrace(printk(" %x->%u\n", at == entries? 0: dx_get_hash(at), dx_get_block(at)));
+               dxtrace(printk(" %x->%u\n", at == entries ? 0 : dx_get_hash(at),
+                              dx_get_block(at)));
                frame->entries = entries;
                frame->at = at;
                if (!indirect--)
                }
                entries = ((struct dx_node *) frame->bh->b_data)->entries;
  
-               if (dx_get_limit(entries) != dx_node_limit (dir)) {
-                       ext4_warning(dir->i_sb,
-                                    "dx entry: limit != node limit");
+               if (dx_get_limit(entries) != dx_node_limit(dir)) {
+                       ext4_warning_inode(dir,
+                               "dx entry: limit %u != node limit %u",
+                               dx_get_limit(entries), dx_node_limit(dir));
                        goto fail;
                }
        }
@@@ -858,18 -849,17 +849,17 @@@ fail
        }
  
        if (ret_err == ERR_PTR(ERR_BAD_DX_DIR))
-               ext4_warning(dir->i_sb,
-                            "Corrupt dir inode %lu, running e2fsck is "
-                            "recommended.", dir->i_ino);
+               ext4_warning_inode(dir,
+                       "Corrupt directory, running e2fsck is recommended");
        return ret_err;
  }
  
- static void dx_release (struct dx_frame *frames)
+ static void dx_release(struct dx_frame *frames)
  {
        if (frames[0].bh == NULL)
                return;
  
-       if (((struct dx_root *) frames[0].bh->b_data)->info.indirect_levels)
+       if (((struct dx_root *)frames[0].bh->b_data)->info.indirect_levels)
                brelse(frames[1].bh);
        brelse(frames[0].bh);
  }
@@@ -962,7 -952,6 +952,6 @@@ static int htree_dirblock_to_tree(struc
        struct buffer_head *bh;
        struct ext4_dir_entry_2 *de, *top;
        int err = 0, count = 0;
-       struct ext4_fname_crypto_ctx *ctx = NULL;
        struct ext4_str fname_crypto_str = {.name = NULL, .len = 0}, tmp_str;
  
        dxtrace(printk(KERN_INFO "In htree dirblock_to_tree: block %lu\n",
                                           EXT4_DIR_REC_LEN(0));
  #ifdef CONFIG_EXT4_FS_ENCRYPTION
        /* Check if the directory is encrypted */
-       ctx = ext4_get_fname_crypto_ctx(dir, EXT4_NAME_LEN);
-       if (IS_ERR(ctx)) {
-               err = PTR_ERR(ctx);
-               brelse(bh);
-               return err;
-       }
-       if (ctx != NULL) {
-               err = ext4_fname_crypto_alloc_buffer(ctx, EXT4_NAME_LEN,
+       if (ext4_encrypted_inode(dir)) {
+               err = ext4_get_encryption_info(dir);
+               if (err < 0) {
+                       brelse(bh);
+                       return err;
+               }
+               err = ext4_fname_crypto_alloc_buffer(dir, EXT4_NAME_LEN,
                                                     &fname_crypto_str);
                if (err < 0) {
-                       ext4_put_fname_crypto_ctx(&ctx);
                        brelse(bh);
                        return err;
                }
                        continue;
                if (de->inode == 0)
                        continue;
-               if (ctx == NULL) {
-                       /* Directory is not encrypted */
+               if (!ext4_encrypted_inode(dir)) {
                        tmp_str.name = de->name;
                        tmp_str.len = de->name_len;
                        err = ext4_htree_store_dirent(dir_file,
                                   hinfo->hash, hinfo->minor_hash, de,
                                   &tmp_str);
                } else {
+                       int save_len = fname_crypto_str.len;
                        /* Directory is encrypted */
-                       err = ext4_fname_disk_to_usr(ctx, hinfo, de,
+                       err = ext4_fname_disk_to_usr(dir, hinfo, de,
                                                     &fname_crypto_str);
                        if (err < 0) {
                                count = err;
                        err = ext4_htree_store_dirent(dir_file,
                                   hinfo->hash, hinfo->minor_hash, de,
                                        &fname_crypto_str);
+                       fname_crypto_str.len = save_len;
                }
                if (err != 0) {
                        count = err;
  errout:
        brelse(bh);
  #ifdef CONFIG_EXT4_FS_ENCRYPTION
-       ext4_put_fname_crypto_ctx(&ctx);
        ext4_fname_crypto_free_buffer(&fname_crypto_str);
  #endif
        return count;
@@@ -1155,12 -1143,13 +1143,13 @@@ errout
  
  static inline int search_dirblock(struct buffer_head *bh,
                                  struct inode *dir,
+                                 struct ext4_filename *fname,
                                  const struct qstr *d_name,
                                  unsigned int offset,
                                  struct ext4_dir_entry_2 **res_dir)
  {
-       return search_dir(bh, bh->b_data, dir->i_sb->s_blocksize, dir,
-                         d_name, offset, res_dir);
+       return ext4_search_dir(bh, bh->b_data, dir->i_sb->s_blocksize, dir,
+                              fname, d_name, offset, res_dir);
  }
  
  /*
@@@ -1242,54 -1231,54 +1231,54 @@@ static void dx_insert_block(struct dx_f
   * `len <= EXT4_NAME_LEN' is guaranteed by caller.
   * `de != NULL' is guaranteed by caller.
   */
- static inline int ext4_match(struct ext4_fname_crypto_ctx *ctx,
-                            struct ext4_str *fname_crypto_str,
-                            int len, const char * const name,
+ static inline int ext4_match(struct ext4_filename *fname,
                             struct ext4_dir_entry_2 *de)
  {
-       int res;
+       const void *name = fname_name(fname);
+       u32 len = fname_len(fname);
  
        if (!de->inode)
                return 0;
  
  #ifdef CONFIG_EXT4_FS_ENCRYPTION
-       if (ctx)
-               return ext4_fname_match(ctx, fname_crypto_str, len, name, de);
+       if (unlikely(!name)) {
+               if (fname->usr_fname->name[0] == '_') {
+                       int ret;
+                       if (de->name_len < 16)
+                               return 0;
+                       ret = memcmp(de->name + de->name_len - 16,
+                                    fname->crypto_buf.name + 8, 16);
+                       return (ret == 0) ? 1 : 0;
+               }
+               name = fname->crypto_buf.name;
+               len = fname->crypto_buf.len;
+       }
  #endif
-       if (len != de->name_len)
+       if (de->name_len != len)
                return 0;
-       res = memcmp(name, de->name, len);
-       return (res == 0) ? 1 : 0;
+       return (memcmp(de->name, name, len) == 0) ? 1 : 0;
  }
  
  /*
   * Returns 0 if not found, -1 on failure, and 1 on success
   */
- int search_dir(struct buffer_head *bh, char *search_buf, int buf_size,
-              struct inode *dir, const struct qstr *d_name,
-              unsigned int offset, struct ext4_dir_entry_2 **res_dir)
+ int ext4_search_dir(struct buffer_head *bh, char *search_buf, int buf_size,
+                   struct inode *dir, struct ext4_filename *fname,
+                   const struct qstr *d_name,
+                   unsigned int offset, struct ext4_dir_entry_2 **res_dir)
  {
        struct ext4_dir_entry_2 * de;
        char * dlimit;
        int de_len;
-       const char *name = d_name->name;
-       int namelen = d_name->len;
-       struct ext4_fname_crypto_ctx *ctx = NULL;
-       struct ext4_str fname_crypto_str = {.name = NULL, .len = 0};
        int res;
  
-       ctx = ext4_get_fname_crypto_ctx(dir, EXT4_NAME_LEN);
-       if (IS_ERR(ctx))
-               return -1;
        de = (struct ext4_dir_entry_2 *)search_buf;
        dlimit = search_buf + buf_size;
        while ((char *) de < dlimit) {
                /* this code is executed quadratically often */
                /* do minimal checking `by hand' */
                if ((char *) de + de->name_len <= dlimit) {
-                       res = ext4_match(ctx, &fname_crypto_str, namelen,
-                                        name, de);
+                       res = ext4_match(fname, de);
                        if (res < 0) {
                                res = -1;
                                goto return_result;
  
        res = 0;
  return_result:
-       ext4_put_fname_crypto_ctx(&ctx);
-       ext4_fname_crypto_free_buffer(&fname_crypto_str);
        return res;
  }
  
@@@ -1370,7 -1357,8 +1357,8 @@@ static struct buffer_head * ext4_find_e
                                   buffer */
        int num = 0;
        ext4_lblk_t  nblocks;
-       int i, namelen;
+       int i, namelen, retval;
+       struct ext4_filename fname;
  
        *res_dir = NULL;
        sb = dir->i_sb;
        if (namelen > EXT4_NAME_LEN)
                return NULL;
  
+       retval = ext4_fname_setup_filename(dir, d_name, 1, &fname);
+       if (retval)
+               return ERR_PTR(retval);
        if (ext4_has_inline_data(dir)) {
                int has_inline_data = 1;
-               ret = ext4_find_inline_entry(dir, d_name, res_dir,
+               ret = ext4_find_inline_entry(dir, &fname, d_name, res_dir,
                                             &has_inline_data);
                if (has_inline_data) {
                        if (inlined)
                                *inlined = 1;
-                       return ret;
+                       goto cleanup_and_exit;
                }
        }
  
                goto restart;
        }
        if (is_dx(dir)) {
-               bh = ext4_dx_find_entry(dir, d_name, res_dir);
+               ret = ext4_dx_find_entry(dir, &fname, res_dir);
                /*
                 * On success, or if the error was file not found,
                 * return.  Otherwise, fall back to doing a search the
                 * old fashioned way.
                 */
-               if (!IS_ERR(bh) || PTR_ERR(bh) != ERR_BAD_DX_DIR)
-                       return bh;
+               if (!IS_ERR(ret) || PTR_ERR(ret) != ERR_BAD_DX_DIR)
+                       goto cleanup_and_exit;
                dxtrace(printk(KERN_DEBUG "ext4_find_entry: dx failed, "
                               "falling back\n"));
        }
@@@ -1438,8 -1430,10 +1430,10 @@@ restart
                                num++;
                                bh = ext4_getblk(NULL, dir, b++, 0);
                                if (unlikely(IS_ERR(bh))) {
-                                       if (ra_max == 0)
-                                               return bh;
+                                       if (ra_max == 0) {
+                                               ret = bh;
+                                               goto cleanup_and_exit;
+                                       }
                                        break;
                                }
                                bh_use[ra_max] = bh;
                        goto next;
                }
                set_buffer_verified(bh);
-               i = search_dirblock(bh, dir, d_name,
+               i = search_dirblock(bh, dir, &fname, d_name,
                            block << EXT4_BLOCK_SIZE_BITS(sb), res_dir);
                if (i == 1) {
                        EXT4_I(dir)->i_dir_start_lookup = block;
@@@ -1500,15 -1494,17 +1494,17 @@@ cleanup_and_exit
        /* Clean up the read-ahead blocks */
        for (; ra_ptr < ra_max; ra_ptr++)
                brelse(bh_use[ra_ptr]);
+       ext4_fname_free_filename(&fname);
        return ret;
  }
  
- static struct buffer_head * ext4_dx_find_entry(struct inode *dir, const struct qstr *d_name,
-                      struct ext4_dir_entry_2 **res_dir)
+ static struct buffer_head * ext4_dx_find_entry(struct inode *dir,
+                       struct ext4_filename *fname,
+                       struct ext4_dir_entry_2 **res_dir)
  {
        struct super_block * sb = dir->i_sb;
-       struct dx_hash_info     hinfo;
        struct dx_frame frames[2], *frame;
+       const struct qstr *d_name = fname->usr_fname;
        struct buffer_head *bh;
        ext4_lblk_t block;
        int retval;
  #ifdef CONFIG_EXT4_FS_ENCRYPTION
        *res_dir = NULL;
  #endif
-       frame = dx_probe(d_name, dir, &hinfo, frames);
+       frame = dx_probe(fname, dir, NULL, frames);
        if (IS_ERR(frame))
                return (struct buffer_head *) frame;
        do {
                if (IS_ERR(bh))
                        goto errout;
  
-               retval = search_dirblock(bh, dir, d_name,
+               retval = search_dirblock(bh, dir, fname, d_name,
                                         block << EXT4_BLOCK_SIZE_BITS(sb),
                                         res_dir);
                if (retval == 1)
                }
  
                /* Check to see if we should continue to search */
-               retval = ext4_htree_next_block(dir, hinfo.hash, frame,
+               retval = ext4_htree_next_block(dir, fname->hinfo.hash, frame,
                                               frames, NULL);
                if (retval < 0) {
-                       ext4_warning(sb,
-                            "error %d reading index page in directory #%lu",
-                            retval, dir->i_ino);
+                       ext4_warning_inode(dir,
+                               "error %d reading directory index block",
+                               retval);
                        bh = ERR_PTR(retval);
                        goto errout;
                }
@@@ -1796,32 -1792,16 +1792,16 @@@ journal_error
  int ext4_find_dest_de(struct inode *dir, struct inode *inode,
                      struct buffer_head *bh,
                      void *buf, int buf_size,
-                     const char *name, int namelen,
+                     struct ext4_filename *fname,
                      struct ext4_dir_entry_2 **dest_de)
  {
        struct ext4_dir_entry_2 *de;
-       unsigned short reclen = EXT4_DIR_REC_LEN(namelen);
+       unsigned short reclen = EXT4_DIR_REC_LEN(fname_len(fname));
        int nlen, rlen;
        unsigned int offset = 0;
        char *top;
-       struct ext4_fname_crypto_ctx *ctx = NULL;
-       struct ext4_str fname_crypto_str = {.name = NULL, .len = 0};
        int res;
  
-       ctx = ext4_get_fname_crypto_ctx(dir, EXT4_NAME_LEN);
-       if (IS_ERR(ctx))
-               return -1;
-       if (ctx != NULL) {
-               /* Calculate record length needed to store the entry */
-               res = ext4_fname_crypto_namelen_on_disk(ctx, namelen);
-               if (res < 0) {
-                       ext4_put_fname_crypto_ctx(&ctx);
-                       return res;
-               }
-               reclen = EXT4_DIR_REC_LEN(res);
-       }
        de = (struct ext4_dir_entry_2 *)buf;
        top = buf + buf_size - reclen;
        while ((char *) de <= top) {
                        goto return_result;
                }
                /* Provide crypto context and crypto buffer to ext4 match */
-               res = ext4_match(ctx, &fname_crypto_str, namelen, name, de);
+               res = ext4_match(fname, de);
                if (res < 0)
                        goto return_result;
                if (res > 0) {
                res = 0;
        }
  return_result:
-       ext4_put_fname_crypto_ctx(&ctx);
-       ext4_fname_crypto_free_buffer(&fname_crypto_str);
        return res;
  }
  
@@@ -1862,39 -1840,10 +1840,10 @@@ int ext4_insert_dentry(struct inode *di
                       struct inode *inode,
                       struct ext4_dir_entry_2 *de,
                       int buf_size,
-                      const struct qstr *iname,
-                      const char *name, int namelen)
+                      struct ext4_filename *fname)
  {
  
        int nlen, rlen;
-       struct ext4_fname_crypto_ctx *ctx = NULL;
-       struct ext4_str fname_crypto_str = {.name = NULL, .len = 0};
-       struct ext4_str tmp_str;
-       int res;
-       ctx = ext4_get_fname_crypto_ctx(dir, EXT4_NAME_LEN);
-       if (IS_ERR(ctx))
-               return -EIO;
-       /* By default, the input name would be written to the disk */
-       tmp_str.name = (unsigned char *)name;
-       tmp_str.len = namelen;
-       if (ctx != NULL) {
-               /* Directory is encrypted */
-               res = ext4_fname_crypto_alloc_buffer(ctx, EXT4_NAME_LEN,
-                                                    &fname_crypto_str);
-               if (res < 0) {
-                       ext4_put_fname_crypto_ctx(&ctx);
-                       return -ENOMEM;
-               }
-               res = ext4_fname_usr_to_disk(ctx, iname, &fname_crypto_str);
-               if (res < 0) {
-                       ext4_put_fname_crypto_ctx(&ctx);
-                       ext4_fname_crypto_free_buffer(&fname_crypto_str);
-                       return res;
-               }
-               tmp_str.name = fname_crypto_str.name;
-               tmp_str.len = fname_crypto_str.len;
-       }
  
        nlen = EXT4_DIR_REC_LEN(de->name_len);
        rlen = ext4_rec_len_from_disk(de->rec_len, buf_size);
        de->file_type = EXT4_FT_UNKNOWN;
        de->inode = cpu_to_le32(inode->i_ino);
        ext4_set_de_type(inode->i_sb, de, inode->i_mode);
-       de->name_len = tmp_str.len;
-       memcpy(de->name, tmp_str.name, tmp_str.len);
-       ext4_put_fname_crypto_ctx(&ctx);
-       ext4_fname_crypto_free_buffer(&fname_crypto_str);
+       de->name_len = fname_len(fname);
+       memcpy(de->name, fname_name(fname), fname_len(fname));
        return 0;
  }
  
   * space.  It will return -ENOSPC if no space is available, and -EIO
   * and -EEXIST if directory entry already exists.
   */
- static int add_dirent_to_buf(handle_t *handle, struct dentry *dentry,
+ static int add_dirent_to_buf(handle_t *handle, struct ext4_filename *fname,
+                            struct inode *dir,
                             struct inode *inode, struct ext4_dir_entry_2 *de,
                             struct buffer_head *bh)
  {
-       struct inode    *dir = d_inode(dentry->d_parent);
-       const char      *name = dentry->d_name.name;
-       int             namelen = dentry->d_name.len;
        unsigned int    blocksize = dir->i_sb->s_blocksize;
        int             csum_size = 0;
        int             err;
                csum_size = sizeof(struct ext4_dir_entry_tail);
  
        if (!de) {
-               err = ext4_find_dest_de(dir, inode,
-                                       bh, bh->b_data, blocksize - csum_size,
-                                       name, namelen, &de);
+               err = ext4_find_dest_de(dir, inode, bh, bh->b_data,
+                                       blocksize - csum_size, fname, &de);
                if (err)
                        return err;
        }
  
        /* By now the buffer is marked for journaling. Due to crypto operations,
         * the following function call may fail */
-       err = ext4_insert_dentry(dir, inode, de, blocksize, &dentry->d_name,
-                                name, namelen);
+       err = ext4_insert_dentry(dir, inode, de, blocksize, fname);
        if (err < 0)
                return err;
  
   * This converts a one block unindexed directory to a 3 block indexed
   * directory, and adds the dentry to the indexed directory.
   */
- static int make_indexed_dir(handle_t *handle, struct dentry *dentry,
+ static int make_indexed_dir(handle_t *handle, struct ext4_filename *fname,
+                           struct dentry *dentry,
                            struct inode *inode, struct buffer_head *bh)
  {
        struct inode    *dir = d_inode(dentry->d_parent);
- #ifdef CONFIG_EXT4_FS_ENCRYPTION
-       struct ext4_fname_crypto_ctx *ctx = NULL;
-       int res;
- #else
-       const char      *name = dentry->d_name.name;
-       int             namelen = dentry->d_name.len;
- #endif
        struct buffer_head *bh2;
        struct dx_root  *root;
        struct dx_frame frames[2], *frame;
        unsigned        len;
        int             retval;
        unsigned        blocksize;
-       struct dx_hash_info hinfo;
        ext4_lblk_t  block;
        struct fake_dirent *fde;
        int csum_size = 0;
  
- #ifdef CONFIG_EXT4_FS_ENCRYPTION
-       ctx = ext4_get_fname_crypto_ctx(dir, EXT4_NAME_LEN);
-       if (IS_ERR(ctx))
-               return PTR_ERR(ctx);
- #endif
        if (ext4_has_metadata_csum(inode->i_sb))
                csum_size = sizeof(struct ext4_dir_entry_tail);
  
        dx_set_limit(entries, dx_root_limit(dir, sizeof(root->info)));
  
        /* Initialize as for dx_probe */
-       hinfo.hash_version = root->info.hash_version;
-       if (hinfo.hash_version <= DX_HASH_TEA)
-               hinfo.hash_version += EXT4_SB(dir->i_sb)->s_hash_unsigned;
-       hinfo.seed = EXT4_SB(dir->i_sb)->s_hash_seed;
- #ifdef CONFIG_EXT4_FS_ENCRYPTION
-       res = ext4_fname_usr_to_hash(ctx, &dentry->d_name, &hinfo);
-       if (res < 0) {
-               ext4_put_fname_crypto_ctx(&ctx);
-               ext4_mark_inode_dirty(handle, dir);
-               brelse(bh);
-               return res;
-       }
-       ext4_put_fname_crypto_ctx(&ctx);
- #else
-       ext4fs_dirhash(name, namelen, &hinfo);
- #endif
+       fname->hinfo.hash_version = root->info.hash_version;
+       if (fname->hinfo.hash_version <= DX_HASH_TEA)
+               fname->hinfo.hash_version += EXT4_SB(dir->i_sb)->s_hash_unsigned;
+       fname->hinfo.seed = EXT4_SB(dir->i_sb)->s_hash_seed;
+       ext4fs_dirhash(fname_name(fname), fname_len(fname), &fname->hinfo);
        memset(frames, 0, sizeof(frames));
        frame = frames;
        frame->entries = entries;
        if (retval)
                goto out_frames;        
  
-       de = do_split(handle,dir, &bh, frame, &hinfo);
+       de = do_split(handle,dir, &bh, frame, &fname->hinfo);
        if (IS_ERR(de)) {
                retval = PTR_ERR(de);
                goto out_frames;
        }
        dx_release(frames);
  
-       retval = add_dirent_to_buf(handle, dentry, inode, de, bh);
+       retval = add_dirent_to_buf(handle, fname, dir, inode, de, bh);
        brelse(bh);
        return retval;
  out_frames:
@@@ -2147,6 -2066,7 +2066,7 @@@ static int ext4_add_entry(handle_t *han
        struct ext4_dir_entry_2 *de;
        struct ext4_dir_entry_tail *t;
        struct super_block *sb;
+       struct ext4_filename fname;
        int     retval;
        int     dx_fallback=0;
        unsigned blocksize;
        if (!dentry->d_name.len)
                return -EINVAL;
  
+       retval = ext4_fname_setup_filename(dir, &dentry->d_name, 0, &fname);
+       if (retval)
+               return retval;
        if (ext4_has_inline_data(dir)) {
-               retval = ext4_try_add_inline_entry(handle, dentry, inode);
+               retval = ext4_try_add_inline_entry(handle, &fname,
+                                                  dentry, inode);
                if (retval < 0)
-                       return retval;
+                       goto out;
                if (retval == 1) {
                        retval = 0;
                        goto out;
        }
  
        if (is_dx(dir)) {
-               retval = ext4_dx_add_entry(handle, dentry, inode);
+               retval = ext4_dx_add_entry(handle, &fname, dentry, inode);
                if (!retval || (retval != ERR_BAD_DX_DIR))
                        goto out;
                ext4_clear_inode_flag(dir, EXT4_INODE_INDEX);
        blocks = dir->i_size >> sb->s_blocksize_bits;
        for (block = 0; block < blocks; block++) {
                bh = ext4_read_dirblock(dir, block, DIRENT);
-               if (IS_ERR(bh))
-                       return PTR_ERR(bh);
-               retval = add_dirent_to_buf(handle, dentry, inode, NULL, bh);
+               if (IS_ERR(bh)) {
+                       retval = PTR_ERR(bh);
+                       bh = NULL;
+                       goto out;
+               }
+               retval = add_dirent_to_buf(handle, &fname, dir, inode,
+                                          NULL, bh);
                if (retval != -ENOSPC)
                        goto out;
  
                if (blocks == 1 && !dx_fallback &&
                    EXT4_HAS_COMPAT_FEATURE(sb, EXT4_FEATURE_COMPAT_DIR_INDEX)) {
-                       retval = make_indexed_dir(handle, dentry, inode, bh);
+                       retval = make_indexed_dir(handle, &fname, dentry,
+                                                 inode, bh);
                        bh = NULL; /* make_indexed_dir releases bh */
                        goto out;
                }
                brelse(bh);
        }
        bh = ext4_append(handle, dir, &block);
-       if (IS_ERR(bh))
-               return PTR_ERR(bh);
+       if (IS_ERR(bh)) {
+               retval = PTR_ERR(bh);
+               bh = NULL;
+               goto out;
+       }
        de = (struct ext4_dir_entry_2 *) bh->b_data;
        de->inode = 0;
        de->rec_len = ext4_rec_len_to_disk(blocksize - csum_size, blocksize);
                initialize_dirent_tail(t, blocksize);
        }
  
-       retval = add_dirent_to_buf(handle, dentry, inode, de, bh);
+       retval = add_dirent_to_buf(handle, &fname, dir, inode, de, bh);
  out:
+       ext4_fname_free_filename(&fname);
        brelse(bh);
        if (retval == 0)
                ext4_set_inode_state(inode, EXT4_STATE_NEWENTRY);
  /*
   * Returns 0 for success, or a negative error value
   */
- static int ext4_dx_add_entry(handle_t *handle, struct dentry *dentry,
-                            struct inode *inode)
+ static int ext4_dx_add_entry(handle_t *handle, struct ext4_filename *fname,
+                            struct dentry *dentry, struct inode *inode)
  {
        struct dx_frame frames[2], *frame;
        struct dx_entry *entries, *at;
-       struct dx_hash_info hinfo;
        struct buffer_head *bh;
        struct inode *dir = d_inode(dentry->d_parent);
        struct super_block *sb = dir->i_sb;
        struct ext4_dir_entry_2 *de;
        int err;
  
-       frame = dx_probe(&dentry->d_name, dir, &hinfo, frames);
+       frame = dx_probe(fname, dir, NULL, frames);
        if (IS_ERR(frame))
                return PTR_ERR(frame);
        entries = frame->entries;
        if (err)
                goto journal_error;
  
-       err = add_dirent_to_buf(handle, dentry, inode, NULL, bh);
+       err = add_dirent_to_buf(handle, fname, dir, inode, NULL, bh);
        if (err != -ENOSPC)
                goto cleanup;
  
  
                if (levels && (dx_get_count(frames->entries) ==
                               dx_get_limit(frames->entries))) {
-                       ext4_warning(sb, "Directory index full!");
+                       ext4_warning_inode(dir, "Directory index full!");
                        err = -ENOSPC;
                        goto cleanup;
                }
                        goto cleanup;
                }
        }
-       de = do_split(handle, dir, &bh, frame, &hinfo);
+       de = do_split(handle, dir, &bh, frame, &fname->hinfo);
        if (IS_ERR(de)) {
                err = PTR_ERR(de);
                goto cleanup;
        }
-       err = add_dirent_to_buf(handle, dentry, inode, de, bh);
+       err = add_dirent_to_buf(handle, fname, dir, inode, de, bh);
        goto cleanup;
  
  journal_error:
@@@ -2517,20 -2449,7 +2449,7 @@@ retry
                inode->i_op = &ext4_file_inode_operations;
                inode->i_fop = &ext4_file_operations;
                ext4_set_aops(inode);
-               err = 0;
- #ifdef CONFIG_EXT4_FS_ENCRYPTION
-               if (!err && (ext4_encrypted_inode(dir) ||
-                            DUMMY_ENCRYPTION_ENABLED(EXT4_SB(dir->i_sb)))) {
-                       err = ext4_inherit_context(dir, inode);
-                       if (err) {
-                               clear_nlink(inode);
-                               unlock_new_inode(inode);
-                               iput(inode);
-                       }
-               }
- #endif
-               if (!err)
-                       err = ext4_add_nondir(handle, dentry, inode);
+               err = ext4_add_nondir(handle, dentry, inode);
                if (!err && IS_DIRSYNC(dir))
                        ext4_handle_sync(handle);
        }
@@@ -2711,14 -2630,6 +2630,6 @@@ retry
        err = ext4_init_new_dir(handle, dir, inode);
        if (err)
                goto out_clear_inode;
- #ifdef CONFIG_EXT4_FS_ENCRYPTION
-       if (ext4_encrypted_inode(dir) ||
-           DUMMY_ENCRYPTION_ENABLED(EXT4_SB(dir->i_sb))) {
-               err = ext4_inherit_context(dir, inode);
-               if (err)
-                       goto out_clear_inode;
-       }
- #endif
        err = ext4_mark_inode_dirty(handle, inode);
        if (!err)
                err = ext4_add_entry(handle, dentry, inode);
@@@ -2779,12 -2690,9 +2690,9 @@@ int ext4_empty_dir(struct inode *inode
        de = (struct ext4_dir_entry_2 *) bh->b_data;
        de1 = ext4_next_entry(de, sb->s_blocksize);
        if (le32_to_cpu(de->inode) != inode->i_ino ||
-                       !le32_to_cpu(de1->inode) ||
-                       strcmp(".", de->name) ||
-                       strcmp("..", de1->name)) {
-               ext4_warning(inode->i_sb,
-                            "bad directory (dir #%lu) - no `.' or `..'",
-                            inode->i_ino);
+                       le32_to_cpu(de1->inode) == 0 ||
+                       strcmp(".", de->name) || strcmp("..", de1->name)) {
+               ext4_warning_inode(inode, "directory missing '.' and/or '..'");
                brelse(bh);
                return 1;
        }
@@@ -3037,8 -2945,9 +2945,9 @@@ static int ext4_rmdir(struct inode *dir
        if (retval)
                goto end_rmdir;
        if (!EXT4_DIR_LINK_EMPTY(inode))
-               ext4_warning(inode->i_sb,
-                            "empty directory has too many links (%d)",
+               ext4_warning_inode(inode,
+                            "empty directory '%.*s' has too many links (%u)",
+                            dentry->d_name.len, dentry->d_name.name,
                             inode->i_nlink);
        inode->i_version++;
        clear_nlink(inode);
@@@ -3098,10 -3007,9 +3007,9 @@@ static int ext4_unlink(struct inode *di
        if (IS_DIRSYNC(dir))
                ext4_handle_sync(handle);
  
-       if (!inode->i_nlink) {
-               ext4_warning(inode->i_sb,
-                            "Deleting nonexistent file (%lu), %d",
-                            inode->i_ino, inode->i_nlink);
+       if (inode->i_nlink == 0) {
+               ext4_warning_inode(inode, "Deleting file '%.*s' with no links",
+                                  dentry->d_name.len, dentry->d_name.name);
                set_nlink(inode, 1);
        }
        retval = ext4_delete_entry(handle, dir, de, bh);
@@@ -3140,10 -3048,23 +3048,23 @@@ static int ext4_symlink(struct inode *d
  
        encryption_required = (ext4_encrypted_inode(dir) ||
                               DUMMY_ENCRYPTION_ENABLED(EXT4_SB(dir->i_sb)));
-       if (encryption_required)
-               disk_link.len = encrypted_symlink_data_len(len) + 1;
-       if (disk_link.len > dir->i_sb->s_blocksize)
-               return -ENAMETOOLONG;
+       if (encryption_required) {
+               err = ext4_get_encryption_info(dir);
+               if (err)
+                       return err;
+               if (ext4_encryption_info(dir) == NULL)
+                       return -EPERM;
+               disk_link.len = (ext4_fname_encrypted_size(dir, len) +
+                                sizeof(struct ext4_encrypted_symlink_data));
+               sd = kzalloc(disk_link.len, GFP_KERNEL);
+               if (!sd)
+                       return -ENOMEM;
+       }
+       if (disk_link.len > dir->i_sb->s_blocksize) {
+               err = -ENAMETOOLONG;
+               goto err_free_sd;
+       }
  
        dquot_initialize(dir);
  
        if (IS_ERR(inode)) {
                if (handle)
                        ext4_journal_stop(handle);
-               return PTR_ERR(inode);
+               err = PTR_ERR(inode);
+               goto err_free_sd;
        }
  
        if (encryption_required) {
-               struct ext4_fname_crypto_ctx *ctx = NULL;
                struct qstr istr;
                struct ext4_str ostr;
  
-               sd = kzalloc(disk_link.len, GFP_NOFS);
-               if (!sd) {
-                       err = -ENOMEM;
-                       goto err_drop_inode;
-               }
-               err = ext4_inherit_context(dir, inode);
-               if (err)
-                       goto err_drop_inode;
-               ctx = ext4_get_fname_crypto_ctx(inode,
-                                               inode->i_sb->s_blocksize);
-               if (IS_ERR_OR_NULL(ctx)) {
-                       /* We just set the policy, so ctx should not be NULL */
-                       err = (ctx == NULL) ? -EIO : PTR_ERR(ctx);
-                       goto err_drop_inode;
-               }
                istr.name = (const unsigned char *) symname;
                istr.len = len;
                ostr.name = sd->encrypted_path;
-               err = ext4_fname_usr_to_disk(ctx, &istr, &ostr);
-               ext4_put_fname_crypto_ctx(&ctx);
+               ostr.len = disk_link.len;
+               err = ext4_fname_usr_to_disk(inode, &istr, &ostr);
                if (err < 0)
                        goto err_drop_inode;
                sd->len = cpu_to_le16(ostr.len);
                disk_link.name = (char *) sd;
 +              inode->i_op = &ext4_encrypted_symlink_inode_operations;
        }
  
        if ((disk_link.len > EXT4_N_BLOCKS * 4)) {
 -              inode->i_op = &ext4_symlink_inode_operations;
 +              if (!encryption_required)
 +                      inode->i_op = &ext4_symlink_inode_operations;
                ext4_set_aops(inode);
                /*
                 * We cannot call page_symlink() with transaction started
        } else {
                /* clear the extent format for fast symlink */
                ext4_clear_inode_flag(inode, EXT4_INODE_EXTENTS);
 -              inode->i_op = encryption_required ?
 -                      &ext4_symlink_inode_operations :
 -                      &ext4_fast_symlink_inode_operations;
 +              if (!encryption_required) {
 +                      inode->i_op = &ext4_fast_symlink_inode_operations;
 +                      inode->i_link = (char *)&EXT4_I(inode)->i_data;
 +              }
                memcpy((char *)&EXT4_I(inode)->i_data, disk_link.name,
                       disk_link.len);
                inode->i_size = disk_link.len - 1;
  err_drop_inode:
        if (handle)
                ext4_journal_stop(handle);
-       kfree(sd);
        clear_nlink(inode);
        unlock_new_inode(inode);
        iput(inode);
+ err_free_sd:
+       kfree(sd);
        return err;
  }
  
@@@ -3490,9 -3394,9 +3397,9 @@@ static void ext4_rename_delete(handle_
        }
  
        if (retval) {
-               ext4_warning(ent->dir->i_sb,
-                               "Deleting old file (%lu), %d, error=%d",
-                               ent->dir->i_ino, ent->dir->i_nlink, retval);
+               ext4_warning_inode(ent->dir,
+                                  "Deleting old file: nlink %d, error=%d",
+                                  ent->dir->i_nlink, retval);
        }
  }
  
@@@ -3762,6 -3666,15 +3669,15 @@@ static int ext4_cross_rename(struct ino
        u8 new_file_type;
        int retval;
  
+       if ((ext4_encrypted_inode(old_dir) ||
+            ext4_encrypted_inode(new_dir)) &&
+           (old_dir != new_dir) &&
+           (!ext4_is_child_context_consistent_with_parent(new_dir,
+                                                          old.inode) ||
+            !ext4_is_child_context_consistent_with_parent(old_dir,
+                                                          new.inode)))
+               return -EPERM;
        dquot_initialize(old.dir);
        dquot_initialize(new.dir);
  
diff --combined fs/ext4/symlink.c
index ba5bd18a9825242fdfc9e8ee4cb05f1cc7cd05f7,68e915aac0fe73dc33052675b5cb2d29072ac8df..c677f2c1044b6ab444d13a57a2582c88581d77de
  #include "xattr.h"
  
  #ifdef CONFIG_EXT4_FS_ENCRYPTION
- static const char *ext4_follow_link(struct dentry *dentry, void **cookie)
 -static void *ext4_follow_link(struct dentry *dentry, struct nameidata *nd)
++static const char *ext4_encrypted_follow_link(struct dentry *dentry, void **cookie)
  {
        struct page *cpage = NULL;
        char *caddr, *paddr = NULL;
        struct ext4_str cstr, pstr;
        struct inode *inode = d_inode(dentry);
-       struct ext4_fname_crypto_ctx *ctx = NULL;
        struct ext4_encrypted_symlink_data *sd;
        loff_t size = min_t(loff_t, i_size_read(inode), PAGE_SIZE - 1);
        int res;
        u32 plen, max_size = inode->i_sb->s_blocksize;
  
-       ctx = ext4_get_fname_crypto_ctx(inode, inode->i_sb->s_blocksize);
-       if (IS_ERR(ctx))
-               return ERR_CAST(ctx);
 -      if (!ext4_encrypted_inode(inode))
 -              return page_follow_link_light(dentry, nd);
 -
+       res = ext4_get_encryption_info(inode);
+       if (res)
+               return ERR_PTR(res);
  
        if (ext4_inode_is_fast_symlink(inode)) {
                caddr = (char *) EXT4_I(inode)->i_data;
                max_size = sizeof(EXT4_I(inode)->i_data);
        } else {
                cpage = read_mapping_page(inode->i_mapping, 0, NULL);
-               if (IS_ERR(cpage)) {
-                       ext4_put_fname_crypto_ctx(&ctx);
+               if (IS_ERR(cpage))
 -                      return cpage;
 +                      return ERR_CAST(cpage);
-               }
                caddr = kmap(cpage);
                caddr[size] = 0;
        }
                goto errout;
        }
        pstr.name = paddr;
-       res = _ext4_fname_disk_to_usr(ctx, NULL, &cstr, &pstr);
+       pstr.len = plen;
+       res = _ext4_fname_disk_to_usr(inode, NULL, &cstr, &pstr);
        if (res < 0)
                goto errout;
        /* Null-terminate the name */
        if (res <= plen)
                paddr[res] = '\0';
-       ext4_put_fname_crypto_ctx(&ctx);
 -      nd_set_link(nd, paddr);
        if (cpage) {
                kunmap(cpage);
                page_cache_release(cpage);
        }
 -      return NULL;
 +      return *cookie = paddr;
  errout:
-       ext4_put_fname_crypto_ctx(&ctx);
        if (cpage) {
                kunmap(cpage);
                page_cache_release(cpage);
        return ERR_PTR(res);
  }
  
 -static void ext4_put_link(struct dentry *dentry, struct nameidata *nd,
 -                        void *cookie)
 -{
 -      struct page *page = cookie;
 -
 -      if (!page) {
 -              kfree(nd_get_link(nd));
 -      } else {
 -              kunmap(page);
 -              page_cache_release(page);
 -      }
 -}
 +const struct inode_operations ext4_encrypted_symlink_inode_operations = {
 +      .readlink       = generic_readlink,
-       .follow_link    = ext4_follow_link,
++      .follow_link    = ext4_encrypted_follow_link,
 +      .put_link       = kfree_put_link,
 +      .setattr        = ext4_setattr,
 +      .setxattr       = generic_setxattr,
 +      .getxattr       = generic_getxattr,
 +      .listxattr      = ext4_listxattr,
 +      .removexattr    = generic_removexattr,
 +};
  #endif
  
 -static void *ext4_follow_fast_link(struct dentry *dentry, struct nameidata *nd)
 -{
 -      struct ext4_inode_info *ei = EXT4_I(d_inode(dentry));
 -      nd_set_link(nd, (char *) ei->i_data);
 -      return NULL;
 -}
 -
  const struct inode_operations ext4_symlink_inode_operations = {
        .readlink       = generic_readlink,
 -#ifdef CONFIG_EXT4_FS_ENCRYPTION
 -      .follow_link    = ext4_follow_link,
 -      .put_link       = ext4_put_link,
 -#else
        .follow_link    = page_follow_link_light,
        .put_link       = page_put_link,
 -#endif
        .setattr        = ext4_setattr,
        .setxattr       = generic_setxattr,
        .getxattr       = generic_getxattr,
  
  const struct inode_operations ext4_fast_symlink_inode_operations = {
        .readlink       = generic_readlink,
 -      .follow_link    = ext4_follow_fast_link,
 +      .follow_link    = simple_follow_link,
        .setattr        = ext4_setattr,
        .setxattr       = generic_setxattr,
        .getxattr       = generic_getxattr,
This page took 0.140515 seconds and 4 git commands to generate.