]> err.no Git - linux-2.6/blobdiff - fs/ext4/inode.c
ext4: Add support for 48 bit inode i_blocks.
[linux-2.6] / fs / ext4 / inode.c
index b83f91edebd1ee659cdcc832b65a3d4ba46488be..bb89fe727bb1327cfe075493bc6f48c2e8d37e72 100644 (file)
@@ -105,7 +105,7 @@ int ext4_forget(handle_t *handle, int is_metadata, struct inode *inode,
  */
 static unsigned long blocks_for_truncate(struct inode *inode)
 {
-       unsigned long needed;
+       ext4_lblk_t needed;
 
        needed = inode->i_blocks >> (inode->i_sb->s_blocksize_bits - 9);
 
@@ -282,7 +282,8 @@ static int verify_chain(Indirect *from, Indirect *to)
  */
 
 static int ext4_block_to_path(struct inode *inode,
-                       long i_block, int offsets[4], int *boundary)
+                       ext4_lblk_t i_block,
+                       ext4_lblk_t offsets[4], int *boundary)
 {
        int ptrs = EXT4_ADDR_PER_BLOCK(inode->i_sb);
        int ptrs_bits = EXT4_ADDR_PER_BLOCK_BITS(inode->i_sb);
@@ -349,7 +350,8 @@ static int ext4_block_to_path(struct inode *inode,
  *     or when it reads all @depth-1 indirect blocks successfully and finds
  *     the whole chain, all way to the data (returns %NULL, *err == 0).
  */
-static Indirect *ext4_get_branch(struct inode *inode, int depth, int *offsets,
+static Indirect *ext4_get_branch(struct inode *inode, int depth,
+                                ext4_lblk_t  *offsets,
                                 Indirect chain[4], int *err)
 {
        struct super_block *sb = inode->i_sb;
@@ -445,7 +447,7 @@ static ext4_fsblk_t ext4_find_near(struct inode *inode, Indirect *ind)
  *     stores it in *@goal and returns zero.
  */
 
-static ext4_fsblk_t ext4_find_goal(struct inode *inode, long block,
+static ext4_fsblk_t ext4_find_goal(struct inode *inode, ext4_lblk_t block,
                Indirect chain[4], Indirect *partial)
 {
        struct ext4_block_alloc_info *block_i;
@@ -590,7 +592,7 @@ failed_out:
  */
 static int ext4_alloc_branch(handle_t *handle, struct inode *inode,
                        int indirect_blks, int *blks, ext4_fsblk_t goal,
-                       int *offsets, Indirect *branch)
+                       ext4_lblk_t *offsets, Indirect *branch)
 {
        int blocksize = inode->i_sb->s_blocksize;
        int i, n = 0;
@@ -680,7 +682,7 @@ failed:
  * chain to new block and return 0.
  */
 static int ext4_splice_branch(handle_t *handle, struct inode *inode,
-                       long block, Indirect *where, int num, int blks)
+                       ext4_lblk_t block, Indirect *where, int num, int blks)
 {
        int i;
        int err = 0;
@@ -784,12 +786,12 @@ err_out:
  * return < 0, error case.
  */
 int ext4_get_blocks_handle(handle_t *handle, struct inode *inode,
-               sector_t iblock, unsigned long maxblocks,
+               ext4_lblk_t iblock, unsigned long maxblocks,
                struct buffer_head *bh_result,
                int create, int extend_disksize)
 {
        int err = -EIO;
-       int offsets[4];
+       ext4_lblk_t offsets[4];
        Indirect chain[4];
        Indirect *partial;
        ext4_fsblk_t goal;
@@ -803,7 +805,8 @@ int ext4_get_blocks_handle(handle_t *handle, struct inode *inode,
 
        J_ASSERT(!(EXT4_I(inode)->i_flags & EXT4_EXTENTS_FL));
        J_ASSERT(handle != NULL || create == 0);
-       depth = ext4_block_to_path(inode,iblock,offsets,&blocks_to_boundary);
+       depth = ext4_block_to_path(inode, iblock, offsets,
+                                       &blocks_to_boundary);
 
        if (depth == 0)
                goto out;
@@ -996,7 +999,7 @@ get_block:
  * `handle' can be NULL if create is zero
  */
 struct buffer_head *ext4_getblk(handle_t *handle, struct inode *inode,
-                               long block, int create, int *errp)
+                               ext4_lblk_t block, int create, int *errp)
 {
        struct buffer_head dummy;
        int fatal = 0, err;
@@ -1027,7 +1030,7 @@ struct buffer_head *ext4_getblk(handle_t *handle, struct inode *inode,
                }
                if (buffer_new(&dummy)) {
                        J_ASSERT(create != 0);
-                       J_ASSERT(handle != 0);
+                       J_ASSERT(handle != NULL);
 
                        /*
                         * Now that we do not always journal data, we should
@@ -1063,7 +1066,7 @@ err:
 }
 
 struct buffer_head *ext4_bread(handle_t *handle, struct inode *inode,
-                              int block, int create, int *err)
+                              ext4_lblk_t block, int create, int *err)
 {
        struct buffer_head * bh;
 
@@ -1146,34 +1149,50 @@ static int do_journal_get_write_access(handle_t *handle,
        return ext4_journal_get_write_access(handle, bh);
 }
 
-static int ext4_prepare_write(struct file *file, struct page *page,
-                             unsigned from, unsigned to)
+static int ext4_write_begin(struct file *file, struct address_space *mapping,
+                               loff_t pos, unsigned len, unsigned flags,
+                               struct page **pagep, void **fsdata)
 {
-       struct inode *inode = page->mapping->host;
+       struct inode *inode = mapping->host;
        int ret, needed_blocks = ext4_writepage_trans_blocks(inode);
        handle_t *handle;
        int retries = 0;
+       struct page *page;
+       pgoff_t index;
+       unsigned from, to;
+
+       index = pos >> PAGE_CACHE_SHIFT;
+       from = pos & (PAGE_CACHE_SIZE - 1);
+       to = from + len;
 
 retry:
-       handle = ext4_journal_start(inode, needed_blocks);
-       if (IS_ERR(handle)) {
-               ret = PTR_ERR(handle);
-               goto out;
+       page = __grab_cache_page(mapping, index);
+       if (!page)
+               return -ENOMEM;
+       *pagep = page;
+
+       handle = ext4_journal_start(inode, needed_blocks);
+       if (IS_ERR(handle)) {
+               unlock_page(page);
+               page_cache_release(page);
+               ret = PTR_ERR(handle);
+               goto out;
        }
-       if (test_opt(inode->i_sb, NOBH) && ext4_should_writeback_data(inode))
-               ret = nobh_prepare_write(page, from, to, ext4_get_block);
-       else
-               ret = block_prepare_write(page, from, to, ext4_get_block);
-       if (ret)
-               goto prepare_write_failed;
 
-       if (ext4_should_journal_data(inode)) {
+       ret = block_write_begin(file, mapping, pos, len, flags, pagep, fsdata,
+                                                       ext4_get_block);
+
+       if (!ret && ext4_should_journal_data(inode)) {
                ret = walk_page_buffers(handle, page_buffers(page),
                                from, to, NULL, do_journal_get_write_access);
        }
-prepare_write_failed:
-       if (ret)
+
+       if (ret) {
                ext4_journal_stop(handle);
+               unlock_page(page);
+               page_cache_release(page);
+       }
+
        if (ret == -ENOSPC && ext4_should_retry_alloc(inode->i_sb, &retries))
                goto retry;
 out:
@@ -1185,12 +1204,12 @@ int ext4_journal_dirty_data(handle_t *handle, struct buffer_head *bh)
        int err = jbd2_journal_dirty_data(handle, bh);
        if (err)
                ext4_journal_abort_handle(__FUNCTION__, __FUNCTION__,
-                                               bh, handle,err);
+                                               bh, handle, err);
        return err;
 }
 
-/* For commit_write() in data=journal mode */
-static int commit_write_fn(handle_t *handle, struct buffer_head *bh)
+/* For write_end() in data=journal mode */
+static int write_end_fn(handle_t *handle, struct buffer_head *bh)
 {
        if (!buffer_mapped(bh) || buffer_freed(bh))
                return 0;
@@ -1198,6 +1217,29 @@ static int commit_write_fn(handle_t *handle, struct buffer_head *bh)
        return ext4_journal_dirty_metadata(handle, bh);
 }
 
+/*
+ * Generic write_end handler for ordered and writeback ext4 journal modes.
+ * We can't use generic_write_end, because that unlocks the page and we need to
+ * unlock the page after ext4_journal_stop, but ext4_journal_stop must run
+ * after block_write_end.
+ */
+static int ext4_generic_write_end(struct file *file,
+                               struct address_space *mapping,
+                               loff_t pos, unsigned len, unsigned copied,
+                               struct page *page, void *fsdata)
+{
+       struct inode *inode = file->f_mapping->host;
+
+       copied = block_write_end(file, mapping, pos, len, copied, page, fsdata);
+
+       if (pos+copied > inode->i_size) {
+               i_size_write(inode, pos+copied);
+               mark_inode_dirty(inode);
+       }
+
+       return copied;
+}
+
 /*
  * We need to pick up the new inode size which generic_commit_write gave us
  * `file' can be NULL - eg, when called from page_symlink().
@@ -1205,78 +1247,101 @@ static int commit_write_fn(handle_t *handle, struct buffer_head *bh)
  * ext4 never places buffers on inode->i_mapping->private_list.  metadata
  * buffers are managed internally.
  */
-static int ext4_ordered_commit_write(struct file *file, struct page *page,
-                            unsigned from, unsigned to)
+static int ext4_ordered_write_end(struct file *file,
+                               struct address_space *mapping,
+                               loff_t pos, unsigned len, unsigned copied,
+                               struct page *page, void *fsdata)
 {
        handle_t *handle = ext4_journal_current_handle();
-       struct inode *inode = page->mapping->host;
+       struct inode *inode = file->f_mapping->host;
+       unsigned from, to;
        int ret = 0, ret2;
 
+       from = pos & (PAGE_CACHE_SIZE - 1);
+       to = from + len;
+
        ret = walk_page_buffers(handle, page_buffers(page),
                from, to, NULL, ext4_journal_dirty_data);
 
        if (ret == 0) {
                /*
-                * generic_commit_write() will run mark_inode_dirty() if i_size
+                * generic_write_end() will run mark_inode_dirty() if i_size
                 * changes.  So let's piggyback the i_disksize mark_inode_dirty
                 * into that.
                 */
                loff_t new_i_size;
 
-               new_i_size = ((loff_t)page->index << PAGE_CACHE_SHIFT) + to;
+               new_i_size = pos + copied;
                if (new_i_size > EXT4_I(inode)->i_disksize)
                        EXT4_I(inode)->i_disksize = new_i_size;
-               ret = generic_commit_write(file, page, from, to);
+               copied = ext4_generic_write_end(file, mapping, pos, len, copied,
+                                                       page, fsdata);
+               if (copied < 0)
+                       ret = copied;
        }
        ret2 = ext4_journal_stop(handle);
        if (!ret)
                ret = ret2;
-       return ret;
+       unlock_page(page);
+       page_cache_release(page);
+
+       return ret ? ret : copied;
 }
 
-static int ext4_writeback_commit_write(struct file *file, struct page *page,
-                            unsigned from, unsigned to)
+static int ext4_writeback_write_end(struct file *file,
+                               struct address_space *mapping,
+                               loff_t pos, unsigned len, unsigned copied,
+                               struct page *page, void *fsdata)
 {
        handle_t *handle = ext4_journal_current_handle();
-       struct inode *inode = page->mapping->host;
+       struct inode *inode = file->f_mapping->host;
        int ret = 0, ret2;
        loff_t new_i_size;
 
-       new_i_size = ((loff_t)page->index << PAGE_CACHE_SHIFT) + to;
+       new_i_size = pos + copied;
        if (new_i_size > EXT4_I(inode)->i_disksize)
                EXT4_I(inode)->i_disksize = new_i_size;
 
-       if (test_opt(inode->i_sb, NOBH) && ext4_should_writeback_data(inode))
-               ret = nobh_commit_write(file, page, from, to);
-       else
-               ret = generic_commit_write(file, page, from, to);
+       copied = ext4_generic_write_end(file, mapping, pos, len, copied,
+                                                       page, fsdata);
+       if (copied < 0)
+               ret = copied;
 
        ret2 = ext4_journal_stop(handle);
        if (!ret)
                ret = ret2;
-       return ret;
+       unlock_page(page);
+       page_cache_release(page);
+
+       return ret ? ret : copied;
 }
 
-static int ext4_journalled_commit_write(struct file *file,
-                       struct page *page, unsigned from, unsigned to)
+static int ext4_journalled_write_end(struct file *file,
+                               struct address_space *mapping,
+                               loff_t pos, unsigned len, unsigned copied,
+                               struct page *page, void *fsdata)
 {
        handle_t *handle = ext4_journal_current_handle();
-       struct inode *inode = page->mapping->host;
+       struct inode *inode = mapping->host;
        int ret = 0, ret2;
        int partial = 0;
-       loff_t pos;
+       unsigned from, to;
 
-       /*
-        * Here we duplicate the generic_commit_write() functionality
-        */
-       pos = ((loff_t)page->index << PAGE_CACHE_SHIFT) + to;
+       from = pos & (PAGE_CACHE_SIZE - 1);
+       to = from + len;
+
+       if (copied < len) {
+               if (!PageUptodate(page))
+                       copied = 0;
+               page_zero_new_buffers(page, from+copied, to);
+       }
 
        ret = walk_page_buffers(handle, page_buffers(page), from,
-                               to, &partial, commit_write_fn);
+                               to, &partial, write_end_fn);
        if (!partial)
                SetPageUptodate(page);
-       if (pos > inode->i_size)
-               i_size_write(inode, pos);
+       if (pos+copied > inode->i_size)
+               i_size_write(inode, pos+copied);
        EXT4_I(inode)->i_state |= EXT4_STATE_JDATA;
        if (inode->i_size > EXT4_I(inode)->i_disksize) {
                EXT4_I(inode)->i_disksize = inode->i_size;
@@ -1284,10 +1349,14 @@ static int ext4_journalled_commit_write(struct file *file,
                if (!ret)
                        ret = ret2;
        }
+
        ret2 = ext4_journal_stop(handle);
        if (!ret)
                ret = ret2;
-       return ret;
+       unlock_page(page);
+       page_cache_release(page);
+
+       return ret ? ret : copied;
 }
 
 /*
@@ -1545,7 +1614,7 @@ static int ext4_journalled_writepage(struct page *page,
                        PAGE_CACHE_SIZE, NULL, do_journal_get_write_access);
 
                err = walk_page_buffers(handle, page_buffers(page), 0,
-                               PAGE_CACHE_SIZE, NULL, commit_write_fn);
+                               PAGE_CACHE_SIZE, NULL, write_end_fn);
                if (ret == 0)
                        ret = err;
                EXT4_I(inode)->i_state |= EXT4_STATE_JDATA;
@@ -1705,8 +1774,8 @@ static const struct address_space_operations ext4_ordered_aops = {
        .readpages      = ext4_readpages,
        .writepage      = ext4_ordered_writepage,
        .sync_page      = block_sync_page,
-       .prepare_write  = ext4_prepare_write,
-       .commit_write   = ext4_ordered_commit_write,
+       .write_begin    = ext4_write_begin,
+       .write_end      = ext4_ordered_write_end,
        .bmap           = ext4_bmap,
        .invalidatepage = ext4_invalidatepage,
        .releasepage    = ext4_releasepage,
@@ -1719,8 +1788,8 @@ static const struct address_space_operations ext4_writeback_aops = {
        .readpages      = ext4_readpages,
        .writepage      = ext4_writeback_writepage,
        .sync_page      = block_sync_page,
-       .prepare_write  = ext4_prepare_write,
-       .commit_write   = ext4_writeback_commit_write,
+       .write_begin    = ext4_write_begin,
+       .write_end      = ext4_writeback_write_end,
        .bmap           = ext4_bmap,
        .invalidatepage = ext4_invalidatepage,
        .releasepage    = ext4_releasepage,
@@ -1733,8 +1802,8 @@ static const struct address_space_operations ext4_journalled_aops = {
        .readpages      = ext4_readpages,
        .writepage      = ext4_journalled_writepage,
        .sync_page      = block_sync_page,
-       .prepare_write  = ext4_prepare_write,
-       .commit_write   = ext4_journalled_commit_write,
+       .write_begin    = ext4_write_begin,
+       .write_end      = ext4_journalled_write_end,
        .set_page_dirty = ext4_journalled_set_page_dirty,
        .bmap           = ext4_bmap,
        .invalidatepage = ext4_invalidatepage,
@@ -1762,11 +1831,11 @@ int ext4_block_truncate_page(handle_t *handle, struct page *page,
 {
        ext4_fsblk_t index = from >> PAGE_CACHE_SHIFT;
        unsigned offset = from & (PAGE_CACHE_SIZE-1);
-       unsigned blocksize, iblock, length, pos;
+       unsigned blocksize, length, pos;
+       ext4_lblk_t iblock;
        struct inode *inode = mapping->host;
        struct buffer_head *bh;
        int err = 0;
-       void *kaddr;
 
        blocksize = inode->i_sb->s_blocksize;
        length = blocksize - (offset & (blocksize - 1));
@@ -1778,10 +1847,7 @@ int ext4_block_truncate_page(handle_t *handle, struct page *page,
         */
        if (!page_has_buffers(page) && test_opt(inode->i_sb, NOBH) &&
             ext4_should_writeback_data(inode) && PageUptodate(page)) {
-               kaddr = kmap_atomic(page, KM_USER0);
-               memset(kaddr + offset, 0, length);
-               flush_dcache_page(page);
-               kunmap_atomic(kaddr, KM_USER0);
+               zero_user_page(page, offset, length, KM_USER0);
                set_page_dirty(page);
                goto unlock;
        }
@@ -1834,10 +1900,7 @@ int ext4_block_truncate_page(handle_t *handle, struct page *page,
                        goto unlock;
        }
 
-       kaddr = kmap_atomic(page, KM_USER0);
-       memset(kaddr + offset, 0, length);
-       flush_dcache_page(page);
-       kunmap_atomic(kaddr, KM_USER0);
+       zero_user_page(page, offset, length, KM_USER0);
 
        BUFFER_TRACE(bh, "zeroed end of block");
 
@@ -1905,7 +1968,7 @@ static inline int all_zeroes(__le32 *p, __le32 *q)
  *                     (no partially truncated stuff there).  */
 
 static Indirect *ext4_find_shared(struct inode *inode, int depth,
-                       int offsets[4], Indirect chain[4], __le32 *top)
+                       ext4_lblk_t offsets[4], Indirect chain[4], __le32 *top)
 {
        Indirect *partial, *p;
        int k, err;
@@ -1989,11 +2052,11 @@ static void ext4_clear_blocks(handle_t *handle, struct inode *inode,
        for (p = first; p < last; p++) {
                u32 nr = le32_to_cpu(*p);
                if (nr) {
-                       struct buffer_head *bh;
+                       struct buffer_head *tbh;
 
                        *p = 0;
-                       bh = sb_find_get_block(inode->i_sb, nr);
-                       ext4_forget(handle, 0, inode, bh, nr);
+                       tbh = sb_find_get_block(inode->i_sb, nr);
+                       ext4_forget(handle, 0, inode, tbh, nr);
                }
        }
 
@@ -2230,12 +2293,12 @@ void ext4_truncate(struct inode *inode)
        __le32 *i_data = ei->i_data;
        int addr_per_block = EXT4_ADDR_PER_BLOCK(inode->i_sb);
        struct address_space *mapping = inode->i_mapping;
-       int offsets[4];
+       ext4_lblk_t offsets[4];
        Indirect chain[4];
        Indirect *partial;
        __le32 nr = 0;
        int n;
-       long last_block;
+       ext4_lblk_t last_block;
        unsigned blocksize = inode->i_sb->s_blocksize;
        struct page *page;
 
@@ -2261,8 +2324,10 @@ void ext4_truncate(struct inode *inode)
                        return;
        }
 
-       if (EXT4_I(inode)->i_flags & EXT4_EXTENTS_FL)
-               return ext4_ext_truncate(inode, page);
+       if (EXT4_I(inode)->i_flags & EXT4_EXTENTS_FL) {
+               ext4_ext_truncate(inode, page);
+               return;
+       }
 
        handle = start_transaction(inode);
        if (IS_ERR(handle)) {
@@ -2401,7 +2466,8 @@ out_stop:
 static ext4_fsblk_t ext4_get_inode_block(struct super_block *sb,
                unsigned long ino, struct ext4_iloc *iloc)
 {
-       unsigned long desc, group_desc, block_group;
+       unsigned long desc, group_desc;
+       ext4_group_t block_group;
        unsigned long offset;
        ext4_fsblk_t block;
        struct buffer_head *bh;
@@ -2488,7 +2554,7 @@ static int __ext4_get_inode_loc(struct inode *inode,
                        struct ext4_group_desc *desc;
                        int inodes_per_buffer;
                        int inode_offset, i;
-                       int block_group;
+                       ext4_group_t block_group;
                        int start;
 
                        block_group = (inode->i_ino - 1) /
@@ -2601,6 +2667,22 @@ void ext4_get_inode_flags(struct ext4_inode_info *ei)
        if (flags & S_DIRSYNC)
                ei->i_flags |= EXT4_DIRSYNC_FL;
 }
+static blkcnt_t ext4_inode_blocks(struct ext4_inode *raw_inode,
+                                       struct ext4_inode_info *ei)
+{
+       blkcnt_t i_blocks ;
+       struct super_block *sb = ei->vfs_inode.i_sb;
+
+       if (EXT4_HAS_RO_COMPAT_FEATURE(sb,
+                               EXT4_FEATURE_RO_COMPAT_HUGE_FILE)) {
+               /* we are using combined 48 bit field */
+               i_blocks = ((u64)le16_to_cpu(raw_inode->i_blocks_high)) << 32 |
+                                       le32_to_cpu(raw_inode->i_blocks_lo);
+               return i_blocks;
+       } else {
+               return le32_to_cpu(raw_inode->i_blocks_lo);
+       }
+}
 
 void ext4_read_inode(struct inode * inode)
 {
@@ -2628,7 +2710,6 @@ void ext4_read_inode(struct inode * inode)
                inode->i_gid |= le16_to_cpu(raw_inode->i_gid_high) << 16;
        }
        inode->i_nlink = le16_to_cpu(raw_inode->i_links_count);
-       inode->i_size = le32_to_cpu(raw_inode->i_size);
 
        ei->i_state = 0;
        ei->i_dir_start_lookup = 0;
@@ -2650,24 +2731,15 @@ void ext4_read_inode(struct inode * inode)
                 * recovery code: that's fine, we're about to complete
                 * the process of deleting those. */
        }
-       inode->i_blocks = le32_to_cpu(raw_inode->i_blocks);
        ei->i_flags = le32_to_cpu(raw_inode->i_flags);
-#ifdef EXT4_FRAGMENTS
-       ei->i_faddr = le32_to_cpu(raw_inode->i_faddr);
-       ei->i_frag_no = raw_inode->i_frag;
-       ei->i_frag_size = raw_inode->i_fsize;
-#endif
-       ei->i_file_acl = le32_to_cpu(raw_inode->i_file_acl);
+       inode->i_blocks = ext4_inode_blocks(raw_inode, ei);
+       ei->i_file_acl = le32_to_cpu(raw_inode->i_file_acl_lo);
        if (EXT4_SB(inode->i_sb)->s_es->s_creator_os !=
-           cpu_to_le32(EXT4_OS_HURD))
+           cpu_to_le32(EXT4_OS_HURD)) {
                ei->i_file_acl |=
                        ((__u64)le16_to_cpu(raw_inode->i_file_acl_high)) << 32;
-       if (!S_ISREG(inode->i_mode)) {
-               ei->i_dir_acl = le32_to_cpu(raw_inode->i_dir_acl);
-       } else {
-               inode->i_size |=
-                       ((__u64)le32_to_cpu(raw_inode->i_size_high)) << 32;
        }
+       inode->i_size = ext4_isize(raw_inode);
        ei->i_disksize = inode->i_size;
        inode->i_generation = le32_to_cpu(raw_inode->i_generation);
        ei->i_block_group = iloc.block_group;
@@ -2743,6 +2815,43 @@ bad_inode:
        return;
 }
 
+static int ext4_inode_blocks_set(handle_t *handle,
+                               struct ext4_inode *raw_inode,
+                               struct ext4_inode_info *ei)
+{
+       struct inode *inode = &(ei->vfs_inode);
+       u64 i_blocks = inode->i_blocks;
+       struct super_block *sb = inode->i_sb;
+       int err = 0;
+
+       if (i_blocks <= ~0U) {
+               /*
+                * i_blocks can be represnted in a 32 bit variable
+                * as multiple of 512 bytes
+                */
+               raw_inode->i_blocks_lo   = cpu_to_le32((u32)i_blocks);
+               raw_inode->i_blocks_high = 0;
+       } else if (i_blocks <= 0xffffffffffffULL) {
+               /*
+                * i_blocks can be represented in a 48 bit variable
+                * as multiple of 512 bytes
+                */
+               err = ext4_update_rocompat_feature(handle, sb,
+                                           EXT4_FEATURE_RO_COMPAT_HUGE_FILE);
+               if (err)
+                       goto  err_out;
+               /* i_block is stored in the split  48 bit fields */
+               raw_inode->i_blocks_lo   = cpu_to_le32((u32)i_blocks);
+               raw_inode->i_blocks_high = cpu_to_le16(i_blocks >> 32);
+       } else {
+               ext4_error(sb, __FUNCTION__,
+                               "Wrong inode i_blocks count  %llu\n",
+                               (unsigned long long)inode->i_blocks);
+       }
+err_out:
+       return err;
+}
+
 /*
  * Post the struct inode info into an on-disk inode location in the
  * buffer-cache.  This gobbles the caller's reference to the
@@ -2791,52 +2900,42 @@ static int ext4_do_update_inode(handle_t *handle,
                raw_inode->i_gid_high = 0;
        }
        raw_inode->i_links_count = cpu_to_le16(inode->i_nlink);
-       raw_inode->i_size = cpu_to_le32(ei->i_disksize);
 
        EXT4_INODE_SET_XTIME(i_ctime, inode, raw_inode);
        EXT4_INODE_SET_XTIME(i_mtime, inode, raw_inode);
        EXT4_INODE_SET_XTIME(i_atime, inode, raw_inode);
        EXT4_EINODE_SET_XTIME(i_crtime, ei, raw_inode);
 
-       raw_inode->i_blocks = cpu_to_le32(inode->i_blocks);
+       if (ext4_inode_blocks_set(handle, raw_inode, ei))
+               goto out_brelse;
        raw_inode->i_dtime = cpu_to_le32(ei->i_dtime);
        raw_inode->i_flags = cpu_to_le32(ei->i_flags);
-#ifdef EXT4_FRAGMENTS
-       raw_inode->i_faddr = cpu_to_le32(ei->i_faddr);
-       raw_inode->i_frag = ei->i_frag_no;
-       raw_inode->i_fsize = ei->i_frag_size;
-#endif
        if (EXT4_SB(inode->i_sb)->s_es->s_creator_os !=
            cpu_to_le32(EXT4_OS_HURD))
                raw_inode->i_file_acl_high =
                        cpu_to_le16(ei->i_file_acl >> 32);
-       raw_inode->i_file_acl = cpu_to_le32(ei->i_file_acl);
-       if (!S_ISREG(inode->i_mode)) {
-               raw_inode->i_dir_acl = cpu_to_le32(ei->i_dir_acl);
-       } else {
-               raw_inode->i_size_high =
-                       cpu_to_le32(ei->i_disksize >> 32);
-               if (ei->i_disksize > 0x7fffffffULL) {
-                       struct super_block *sb = inode->i_sb;
-                       if (!EXT4_HAS_RO_COMPAT_FEATURE(sb,
-                                       EXT4_FEATURE_RO_COMPAT_LARGE_FILE) ||
-                           EXT4_SB(sb)->s_es->s_rev_level ==
-                                       cpu_to_le32(EXT4_GOOD_OLD_REV)) {
-                              /* If this is the first large file
-                               * created, add a flag to the superblock.
-                               */
-                               err = ext4_journal_get_write_access(handle,
-                                               EXT4_SB(sb)->s_sbh);
-                               if (err)
-                                       goto out_brelse;
-                               ext4_update_dynamic_rev(sb);
-                               EXT4_SET_RO_COMPAT_FEATURE(sb,
+       raw_inode->i_file_acl_lo = cpu_to_le32(ei->i_file_acl);
+       ext4_isize_set(raw_inode, ei->i_disksize);
+       if (ei->i_disksize > 0x7fffffffULL) {
+               struct super_block *sb = inode->i_sb;
+               if (!EXT4_HAS_RO_COMPAT_FEATURE(sb,
+                               EXT4_FEATURE_RO_COMPAT_LARGE_FILE) ||
+                               EXT4_SB(sb)->s_es->s_rev_level ==
+                               cpu_to_le32(EXT4_GOOD_OLD_REV)) {
+                       /* If this is the first large file
+                        * created, add a flag to the superblock.
+                        */
+                       err = ext4_journal_get_write_access(handle,
+                                       EXT4_SB(sb)->s_sbh);
+                       if (err)
+                               goto out_brelse;
+                       ext4_update_dynamic_rev(sb);
+                       EXT4_SET_RO_COMPAT_FEATURE(sb,
                                        EXT4_FEATURE_RO_COMPAT_LARGE_FILE);
-                               sb->s_dirt = 1;
-                               handle->h_sync = 1;
-                               err = ext4_journal_dirty_metadata(handle,
-                                               EXT4_SB(sb)->s_sbh);
-                       }
+                       sb->s_dirt = 1;
+                       handle->h_sync = 1;
+                       err = ext4_journal_dirty_metadata(handle,
+                                       EXT4_SB(sb)->s_sbh);
                }
        }
        raw_inode->i_generation = cpu_to_le32(inode->i_generation);
@@ -2910,7 +3009,7 @@ int ext4_write_inode(struct inode *inode, int wait)
                return 0;
 
        if (ext4_journal_current_handle()) {
-               jbd_debug(0, "called recursively, non-PF_MEMALLOC!\n");
+               jbd_debug(1, "called recursively, non-PF_MEMALLOC!\n");
                dump_stack();
                return -EIO;
        }
@@ -3105,6 +3204,41 @@ ext4_reserve_inode_write(handle_t *handle, struct inode *inode,
        return err;
 }
 
+/*
+ * Expand an inode by new_extra_isize bytes.
+ * Returns 0 on success or negative error number on failure.
+ */
+static int ext4_expand_extra_isize(struct inode *inode,
+                                  unsigned int new_extra_isize,
+                                  struct ext4_iloc iloc,
+                                  handle_t *handle)
+{
+       struct ext4_inode *raw_inode;
+       struct ext4_xattr_ibody_header *header;
+       struct ext4_xattr_entry *entry;
+
+       if (EXT4_I(inode)->i_extra_isize >= new_extra_isize)
+               return 0;
+
+       raw_inode = ext4_raw_inode(&iloc);
+
+       header = IHDR(inode, raw_inode);
+       entry = IFIRST(header);
+
+       /* No extended attributes present */
+       if (!(EXT4_I(inode)->i_state & EXT4_STATE_XATTR) ||
+               header->h_magic != cpu_to_le32(EXT4_XATTR_MAGIC)) {
+               memset((void *)raw_inode + EXT4_GOOD_OLD_INODE_SIZE, 0,
+                       new_extra_isize);
+               EXT4_I(inode)->i_extra_isize = new_extra_isize;
+               return 0;
+       }
+
+       /* try to expand with EAs present */
+       return ext4_expand_extra_isize_ea(inode, new_extra_isize,
+                                         raw_inode, handle);
+}
+
 /*
  * What we do here is to mark the in-core inode as clean with respect to inode
  * dirtiness (it may still be data-dirty).
@@ -3129,10 +3263,40 @@ ext4_reserve_inode_write(handle_t *handle, struct inode *inode,
 int ext4_mark_inode_dirty(handle_t *handle, struct inode *inode)
 {
        struct ext4_iloc iloc;
-       int err;
+       struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb);
+       static unsigned int mnt_count;
+       int err, ret;
 
        might_sleep();
        err = ext4_reserve_inode_write(handle, inode, &iloc);
+       if (EXT4_I(inode)->i_extra_isize < sbi->s_want_extra_isize &&
+           !(EXT4_I(inode)->i_state & EXT4_STATE_NO_EXPAND)) {
+               /*
+                * We need extra buffer credits since we may write into EA block
+                * with this same handle. If journal_extend fails, then it will
+                * only result in a minor loss of functionality for that inode.
+                * If this is felt to be critical, then e2fsck should be run to
+                * force a large enough s_min_extra_isize.
+                */
+               if ((jbd2_journal_extend(handle,
+                            EXT4_DATA_TRANS_BLOCKS(inode->i_sb))) == 0) {
+                       ret = ext4_expand_extra_isize(inode,
+                                                     sbi->s_want_extra_isize,
+                                                     iloc, handle);
+                       if (ret) {
+                               EXT4_I(inode)->i_state |= EXT4_STATE_NO_EXPAND;
+                               if (mnt_count !=
+                                       le16_to_cpu(sbi->s_es->s_mnt_count)) {
+                                       ext4_warning(inode->i_sb, __FUNCTION__,
+                                       "Unable to expand inode %lu. Delete"
+                                       " some EAs or run e2fsck.",
+                                       inode->i_ino);
+                                       mnt_count =
+                                         le16_to_cpu(sbi->s_es->s_mnt_count);
+                               }
+                       }
+               }
+       }
        if (!err)
                err = ext4_mark_iloc_dirty(handle, inode, &iloc);
        return err;
@@ -3221,7 +3385,7 @@ int ext4_change_inode_journal_flag(struct inode *inode, int val)
         */
 
        journal = EXT4_JOURNAL(inode);
-       if (is_journal_aborted(journal) || IS_RDONLY(inode))
+       if (is_journal_aborted(journal))
                return -EROFS;
 
        jbd2_journal_lock_updates(journal);