fs: Give dentry to inode_change_ok() instead of inode
[pandora-kernel.git] / fs / btrfs / inode.c
index 0a6b928..d84977d 100644 (file)
@@ -56,7 +56,7 @@
 #include "inode-map.h"
 
 struct btrfs_iget_args {
-       u64 ino;
+       struct btrfs_key *location;
        struct btrfs_root *root;
 };
 
@@ -88,6 +88,7 @@ static unsigned char btrfs_type_by_mode[S_IFMT >> S_SHIFT] = {
 };
 
 static int btrfs_setsize(struct inode *inode, loff_t newsize);
+static int btrfs_truncate_page(struct address_space *mapping, loff_t from);
 static int btrfs_truncate(struct inode *inode);
 static int btrfs_finish_ordered_io(struct inode *inode, u64 start, u64 end);
 static noinline int cow_file_range(struct inode *inode,
@@ -343,6 +344,7 @@ static noinline int compress_file_range(struct inode *inode,
        int i;
        int will_compress;
        int compress_type = root->fs_info->compress_type;
+       int redirty = 0;
 
        /* if this is a small write inside eof, kick off a defragbot */
        if (end <= BTRFS_I(inode)->disk_i_size && (end - start + 1) < 16 * 1024)
@@ -404,6 +406,17 @@ again:
                if (BTRFS_I(inode)->force_compress)
                        compress_type = BTRFS_I(inode)->force_compress;
 
+               /*
+                * we need to call clear_page_dirty_for_io on each
+                * page in the range.  Otherwise applications with the file
+                * mmap'd can wander in and change the page contents while
+                * we are compressing them.
+                *
+                * If the compression fails for any reason, we set the pages
+                * dirty again later on.
+                */
+               extent_range_clear_dirty_for_io(inode, start, end);
+               redirty = 1;
                ret = btrfs_compress_pages(compress_type,
                                           inode->i_mapping, start,
                                           total_compressed, pages,
@@ -541,6 +554,8 @@ cleanup_and_bail_uncompressed:
                        __set_page_dirty_nobuffers(locked_page);
                        /* unlocked later on in the async handlers */
                }
+               if (redirty)
+                       extent_range_redirty_for_io(inode, start, end);
                add_async_extent(async_cow, start, end - start + 1,
                                 0, NULL, 0, BTRFS_COMPRESS_NONE);
                *num_added += 1;
@@ -1112,8 +1127,14 @@ next_slot:
                num_bytes = 0;
                btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]);
 
-               if (found_key.objectid > ino ||
-                   found_key.type > BTRFS_EXTENT_DATA_KEY ||
+               if (found_key.objectid > ino)
+                       break;
+               if (WARN_ON_ONCE(found_key.objectid < ino) ||
+                   found_key.type < BTRFS_EXTENT_DATA_KEY) {
+                       path->slots[0]++;
+                       goto next_slot;
+               }
+               if (found_key.type > BTRFS_EXTENT_DATA_KEY ||
                    found_key.offset > end)
                        break;
 
@@ -1164,7 +1185,8 @@ next_slot:
                        nocow = 1;
                } else if (extent_type == BTRFS_FILE_EXTENT_INLINE) {
                        extent_end = found_key.offset +
-                               btrfs_file_extent_inline_len(leaf, fi);
+                               btrfs_file_extent_inline_len(leaf,
+                                                    path->slots[0], fi);
                        extent_end = ALIGN(extent_end, root->sectorsize);
                } else {
                        BUG_ON(1);
@@ -2977,6 +2999,47 @@ out:
        return err;
 }
 
+static int truncate_inline_extent(struct btrfs_trans_handle *trans,
+                                 struct inode *inode,
+                                 struct btrfs_path *path,
+                                 struct btrfs_key *found_key,
+                                 const u64 item_end,
+                                 const u64 new_size)
+{
+       struct extent_buffer *leaf = path->nodes[0];
+       int slot = path->slots[0];
+       struct btrfs_file_extent_item *fi;
+       u32 size = (u32)(new_size - found_key->offset);
+       struct btrfs_root *root = BTRFS_I(inode)->root;
+
+       fi = btrfs_item_ptr(leaf, slot, struct btrfs_file_extent_item);
+
+       if (btrfs_file_extent_compression(leaf, fi) != BTRFS_COMPRESS_NONE) {
+               loff_t offset = new_size;
+
+               /*
+                * Zero out the remaining of the last page of our inline extent,
+                * instead of directly truncating our inline extent here - that
+                * would be much more complex (decompressing all the data, then
+                * compressing the truncated data, which might be bigger than
+                * the size of the inline extent, resize the extent, etc).
+                * We release the path because to get the page we might need to
+                * read the extent item from disk (data not in the page cache).
+                */
+               btrfs_release_path(path);
+               return btrfs_truncate_page(inode->i_mapping, offset);
+       }
+
+       btrfs_set_file_extent_ram_bytes(leaf, fi, size);
+       size = btrfs_file_extent_calc_inline_size(size);
+       btrfs_truncate_item(trans, root, path, size, 1);
+
+       if (root->ref_cows)
+               inode_sub_bytes(inode, item_end + 1 - new_size);
+
+       return 0;
+}
+
 /*
  * this can truncate away extent items, csum items and directory items.
  * It starts at a high offset and removes keys until it can't find
@@ -3081,7 +3144,7 @@ search_again:
                                    btrfs_file_extent_num_bytes(leaf, fi);
                        } else if (extent_type == BTRFS_FILE_EXTENT_INLINE) {
                                item_end += btrfs_file_extent_inline_len(leaf,
-                                                                        fi);
+                                                        path->slots[0], fi);
                        }
                        item_end--;
                }
@@ -3138,22 +3201,30 @@ search_again:
                         * special encodings
                         */
                        if (!del_item &&
-                           btrfs_file_extent_compression(leaf, fi) == 0 &&
                            btrfs_file_extent_encryption(leaf, fi) == 0 &&
                            btrfs_file_extent_other_encoding(leaf, fi) == 0) {
-                               u32 size = new_size - found_key.offset;
 
-                               if (root->ref_cows) {
-                                       inode_sub_bytes(inode, item_end + 1 -
-                                                       new_size);
+                               /*
+                                * Need to release path in order to truncate a
+                                * compressed extent. So delete any accumulated
+                                * extent items so far.
+                                */
+                               if (btrfs_file_extent_compression(leaf, fi) !=
+                                   BTRFS_COMPRESS_NONE && pending_del_nr) {
+                                       err = btrfs_del_items(trans, root, path,
+                                                             pending_del_slot,
+                                                             pending_del_nr);
+                                       BUG_ON(err);
+                                       pending_del_nr = 0;
                                }
-                               size =
-                                   btrfs_file_extent_calc_inline_size(size);
-                               ret = btrfs_truncate_item(trans, root, path,
-                                                         size, 1);
+
+                               err = truncate_inline_extent(trans, inode,
+                                                            path, &found_key,
+                                                            item_end,
+                                                            new_size);
+                               BUG_ON(err);
                        } else if (root->ref_cows) {
-                               inode_sub_bytes(inode, item_end + 1 -
-                                               found_key.offset);
+                               inode_sub_bytes(inode, item_end + 1 - new_size);
                        }
                }
 delete:
@@ -3462,7 +3533,7 @@ static int btrfs_setattr(struct dentry *dentry, struct iattr *attr)
        if (btrfs_root_readonly(root))
                return -EROFS;
 
-       err = inode_change_ok(inode, attr);
+       err = setattr_prepare(dentry, attr);
        if (err)
                return err;
 
@@ -3504,7 +3575,8 @@ void btrfs_evict_inode(struct inode *inode)
                goto no_delete;
        }
        /* do we really want it for ->i_nlink > 0 and zero btrfs_root_refs? */
-       btrfs_wait_ordered_range(inode, 0, (u64)-1);
+       if (!special_file(inode->i_mode))
+               btrfs_wait_ordered_range(inode, 0, (u64)-1);
 
        if (root->fs_info->log_root_recovering) {
                BUG_ON(!list_empty(&BTRFS_I(inode)->i_orphan));
@@ -3833,7 +3905,9 @@ again:
 static int btrfs_init_locked_inode(struct inode *inode, void *p)
 {
        struct btrfs_iget_args *args = p;
-       inode->i_ino = args->ino;
+       inode->i_ino = args->location->objectid;
+       memcpy(&BTRFS_I(inode)->location, args->location,
+              sizeof(*args->location));
        BTRFS_I(inode)->root = args->root;
        btrfs_set_inode_space_info(args->root, inode);
        return 0;
@@ -3842,20 +3916,20 @@ static int btrfs_init_locked_inode(struct inode *inode, void *p)
 static int btrfs_find_actor(struct inode *inode, void *opaque)
 {
        struct btrfs_iget_args *args = opaque;
-       return args->ino == btrfs_ino(inode) &&
+       return args->location->objectid == BTRFS_I(inode)->location.objectid &&
                args->root == BTRFS_I(inode)->root;
 }
 
 static struct inode *btrfs_iget_locked(struct super_block *s,
-                                      u64 objectid,
+                                      struct btrfs_key *location,
                                       struct btrfs_root *root)
 {
        struct inode *inode;
        struct btrfs_iget_args args;
-       args.ino = objectid;
+       args.location = location;
        args.root = root;
 
-       inode = iget5_locked(s, objectid, btrfs_find_actor,
+       inode = iget5_locked(s, location->objectid, btrfs_find_actor,
                             btrfs_init_locked_inode,
                             (void *)&args);
        return inode;
@@ -3869,13 +3943,11 @@ struct inode *btrfs_iget(struct super_block *s, struct btrfs_key *location,
 {
        struct inode *inode;
 
-       inode = btrfs_iget_locked(s, location->objectid, root);
+       inode = btrfs_iget_locked(s, location, root);
        if (!inode)
                return ERR_PTR(-ENOMEM);
 
        if (inode->i_state & I_NEW) {
-               BTRFS_I(inode)->root = root;
-               memcpy(&BTRFS_I(inode)->location, location, sizeof(*location));
                btrfs_read_locked_inode(inode);
                if (!is_bad_inode(inode)) {
                        inode_tree_add(inode);
@@ -4039,6 +4111,7 @@ static int btrfs_real_readdir(struct file *filp, void *dirent,
        char *name_ptr;
        int name_len;
        int is_curr = 0;        /* filp->f_pos points to the current index? */
+       bool emitted;
 
        /* FIXME, use a real flag for deciding about the key type */
        if (root->fs_info->tree_root == root)
@@ -4081,6 +4154,7 @@ static int btrfs_real_readdir(struct file *filp, void *dirent,
        if (ret < 0)
                goto err;
 
+       emitted = false;
        while (1) {
                leaf = path->nodes[0];
                slot = path->slots[0];
@@ -4182,6 +4256,7 @@ skip:
 
                        if (over)
                                goto nopos;
+                       emitted = true;
                        di_len = btrfs_dir_name_len(leaf, di) +
                                 btrfs_dir_data_len(leaf, di) + sizeof(*di);
                        di_cur += di_len;
@@ -4195,11 +4270,20 @@ next:
                if (is_curr)
                        filp->f_pos++;
                ret = btrfs_readdir_delayed_dir_index(filp, dirent, filldir,
-                                                     &ins_list);
+                                                     &ins_list, &emitted);
                if (ret)
                        goto nopos;
        }
 
+       /*
+        * If we haven't emitted any dir entry, we must not touch filp->f_pos as
+        * it was was set to the termination value in previous call. We assume
+        * that "." and ".." were emitted if we reach this point and set the
+        * termination value as well for an empty directory.
+        */
+       if (filp->f_pos > 2 && !emitted)
+               goto nopos;
+
        /* Reached end of directory/root. Bump pos past the last item. */
        if (key_type == BTRFS_DIR_INDEX_KEY)
                /*
@@ -4590,10 +4674,6 @@ static int btrfs_add_nondir(struct btrfs_trans_handle *trans,
        int err = btrfs_add_link(trans, dir, inode,
                                 dentry->d_name.name, dentry->d_name.len,
                                 backref, index);
-       if (!err) {
-               d_instantiate(dentry, inode);
-               return 0;
-       }
        if (err > 0)
                err = -EEXIST;
        return err;
@@ -4655,6 +4735,7 @@ static int btrfs_mknod(struct inode *dir, struct dentry *dentry,
        else {
                init_special_inode(inode, inode->i_mode, rdev);
                btrfs_update_inode(trans, root, inode);
+               d_instantiate(dentry, inode);
        }
 out_unlock:
        nr = trans->blocks_used;
@@ -4722,6 +4803,7 @@ static int btrfs_create(struct inode *dir, struct dentry *dentry,
                inode->i_mapping->a_ops = &btrfs_aops;
                inode->i_mapping->backing_dev_info = &root->fs_info->bdi;
                BTRFS_I(inode)->io_tree.ops = &btrfs_extent_io_ops;
+               d_instantiate(dentry, inode);
        }
 out_unlock:
        nr = trans->blocks_used;
@@ -4779,6 +4861,7 @@ static int btrfs_link(struct dentry *old_dentry, struct inode *dir,
                struct dentry *parent = dentry->d_parent;
                err = btrfs_update_inode(trans, root, inode);
                BUG_ON(err);
+               d_instantiate(dentry, inode);
                btrfs_log_new_name(trans, inode, NULL, parent);
        }
 
@@ -5022,7 +5105,7 @@ again:
                       btrfs_file_extent_num_bytes(leaf, item);
        } else if (found_type == BTRFS_FILE_EXTENT_INLINE) {
                size_t size;
-               size = btrfs_file_extent_inline_len(leaf, item);
+               size = btrfs_file_extent_inline_len(leaf, path->slots[0], item);
                extent_end = (extent_start + size + root->sectorsize - 1) &
                        ~((u64)root->sectorsize - 1);
        }
@@ -5089,7 +5172,7 @@ again:
                        goto out;
                }
 
-               size = btrfs_file_extent_inline_len(leaf, item);
+               size = btrfs_file_extent_inline_len(leaf, path->slots[0], item);
                extent_offset = page_offset(page) + pg_offset - extent_start;
                copy_size = min_t(u64, PAGE_CACHE_SIZE - pg_offset,
                                size - extent_offset);
@@ -7245,6 +7328,8 @@ static int btrfs_symlink(struct inode *dir, struct dentry *dentry,
                drop_inode = 1;
 
 out_unlock:
+       if (!err)
+               d_instantiate(dentry, inode);
        nr = trans->blocks_used;
        btrfs_end_transaction_throttle(trans, root);
        if (drop_inode) {
@@ -7390,7 +7475,7 @@ static const struct file_operations btrfs_dir_file_operations = {
        .readdir        = btrfs_real_readdir,
        .unlocked_ioctl = btrfs_ioctl,
 #ifdef CONFIG_COMPAT
-       .compat_ioctl   = btrfs_ioctl,
+       .compat_ioctl   = btrfs_compat_ioctl,
 #endif
        .release        = btrfs_release_file,
        .fsync          = btrfs_sync_file,