summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorQu Wenruo <quwenruo@cn.fujitsu.com>2015-01-02 15:12:30 +0800
committerDavid Sterba <dsterba@suse.cz>2015-02-02 14:16:38 +0100
commitaae2c8421bb39014ed606b7bd9f37641c27d5a52 (patch)
treeb78b33e993cfb5cea578eb77472f2cf9578c75dc
parent0ddf63c09f2e24cd8f94e662c93055099fff2e2c (diff)
btrfs-progs: Add btrfs_get_extent() and btrfs_punch_hole().
Add btrfs_get_extent() and btrfs_punch_hole() for btrfs-progs. Btrfs_get_extent() will act much like kernel one, return the first extent that covers the given range. The difference will be that progs btrfs_get_extent() can't handle no-holes feature, which means caller should handle it carefully. Btrfs_punch_hole() will punch a hole in given range of given inode, however it differs from kernel one since it won't zero any page or drop any extents if there is any extent in the hole range. These functions are mainly used for later I_ERR_FILE_EXTENT_DISCOUNT repair function. Signed-off-by: Qu Wenruo <quwenruo@cn.fujitsu.com> Signed-off-by: David Sterba <dsterba@suse.cz>
-rw-r--r--Makefile.in2
-rw-r--r--ctree.h9
-rw-r--r--file.c162
3 files changed, 172 insertions, 1 deletions
diff --git a/Makefile.in b/Makefile.in
index c692985b..cb1c9a3c 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -33,7 +33,7 @@ objects = ctree.o disk-io.o radix-tree.o extent-tree.o print-tree.o \
extent-cache.o extent_io.o volumes.o utils.o repair.o \
qgroup.o raid6.o free-space-cache.o list_sort.o props.o \
ulist.o qgroup-verify.o backref.o string-table.o task-utils.o \
- inode.o
+ inode.o file.o
cmds_objects = cmds-subvolume.o cmds-filesystem.o cmds-device.o cmds-scrub.o \
cmds-inspect.o cmds-balance.o cmds-send.o cmds-receive.o \
cmds-quota.o cmds-qgroup.o cmds-replace.o cmds-check.o \
diff --git a/ctree.h b/ctree.h
index a8b99422..2d2988b1 100644
--- a/ctree.h
+++ b/ctree.h
@@ -2468,4 +2468,13 @@ int btrfs_add_orphan_item(struct btrfs_trans_handle *trans,
u64 ino);
int btrfs_mkdir(struct btrfs_trans_handle *trans, struct btrfs_root *root,
char *name, int namelen, u64 parent_ino, u64 *ino, int mode);
+
+/* file.c */
+int btrfs_get_extent(struct btrfs_trans_handle *trans,
+ struct btrfs_root *root,
+ struct btrfs_path *path,
+ u64 ino, u64 offset, u64 len, int ins_len);
+int btrfs_punch_hole(struct btrfs_trans_handle *trans,
+ struct btrfs_root *root,
+ u64 ino, u64 offset, u64 len);
#endif
diff --git a/file.c b/file.c
new file mode 100644
index 00000000..0e9253ee
--- /dev/null
+++ b/file.c
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2014 Fujitsu. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License v2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 021110-1307, USA.
+ */
+
+#include <sys/stat.h>
+#include "ctree.h"
+#include "transaction.h"
+#include "kerncompat.h"
+
+/*
+ * Get the first file extent that covers (part of) the given range
+ * Unlike kernel using extent_map to handle hole even no-hole is enabled,
+ * progs don't have such infrastructure, so caller should do extra care
+ * for no-hole.
+ *
+ * return 0 for found, and path points to the file extent.
+ * return >0 for not found, and path points to the insert position.
+ * return <0 for error.
+ */
+int btrfs_get_extent(struct btrfs_trans_handle *trans,
+ struct btrfs_root *root,
+ struct btrfs_path *path,
+ u64 ino, u64 offset, u64 len, int ins_len)
+{
+ struct btrfs_key key;
+ struct btrfs_key found_key;
+ struct btrfs_file_extent_item *fi_item;
+ u64 end = 0;
+ int ret = 0;
+ int not_found = 1;
+
+ key.objectid = ino;
+ key.type = BTRFS_EXTENT_DATA_KEY;
+ key.offset = offset;
+
+ ret = btrfs_search_slot(trans, root, &key, path, ins_len,
+ ins_len ? 1 : 0);
+ if (ret <= 0)
+ goto out;
+ if (ret > 0) {
+ /* Check preivous file extent */
+ ret = btrfs_previous_item(root, path, ino,
+ BTRFS_EXTENT_DATA_KEY);
+ if (ret < 0)
+ goto out;
+ if (ret > 0)
+ goto check_next;
+ }
+ btrfs_item_key_to_cpu(path->nodes[0], &found_key, path->slots[0]);
+ if (found_key.objectid != ino ||
+ found_key.type != BTRFS_EXTENT_DATA_KEY)
+ goto check_next;
+
+ fi_item = btrfs_item_ptr(path->nodes[0], path->slots[0],
+ struct btrfs_file_extent_item);
+ end = found_key.offset +
+ btrfs_file_extent_ram_bytes(path->nodes[0], fi_item);
+ /*
+ * existing file extent
+ * |--------| |----|
+ * |-------|
+ * offset + len
+ * OR
+ * |---------------|
+ * |-------|
+ */
+ if (end > offset) {
+ not_found = 0;
+ goto out;
+ }
+check_next:
+ ret = btrfs_next_item(root, path);
+ if (ret)
+ goto out;
+
+ btrfs_item_key_to_cpu(path->nodes[0], &found_key, path->slots[0]);
+ if (found_key.objectid != ino ||
+ found_key.type != BTRFS_EXTENT_DATA_KEY) {
+ ret = 1;
+ goto out;
+ }
+ if (found_key.offset < offset + len)
+ /*
+ * existing file extent
+ * |---| |------|
+ * |-------|
+ * offset + len
+ */
+ not_found = 0;
+ else
+ /*
+ * existing file extent
+ * |----| |----|
+ * |----|
+ * offset + len
+ */
+ not_found = 1;
+
+ /*
+ * To keep the search hehavior consistent with search_slot(),
+ * we need to go back to the prev leaf's nritem slot if
+ * we are at the first slot of the leaf.
+ */
+ if (path->slots[0] == 0) {
+ ret = btrfs_prev_leaf(root, path);
+ /* Not possible */
+ if (ret)
+ goto out;
+ path->slots[0] = btrfs_header_nritems(path->nodes[0]);
+ }
+
+out:
+ if (ret == 0)
+ ret = not_found;
+ return ret;
+}
+
+/*
+ * Punch hole ranged [offset,len) for the file given by ino and root.
+ *
+ * Unlink kernel punch_hole, which will not zero/free existing extent,
+ * instead it will return -EEXIST if there is any extents in the hole
+ * range.
+ */
+int btrfs_punch_hole(struct btrfs_trans_handle *trans,
+ struct btrfs_root *root,
+ u64 ino, u64 offset, u64 len)
+{
+ struct btrfs_path *path;
+ int ret = 0;
+
+ path = btrfs_alloc_path();
+ if (!path)
+ return -ENOMEM;
+
+ ret = btrfs_get_extent(NULL, root, path, ino, offset, len, 0);
+ if (ret < 0)
+ goto out;
+ if (ret == 0) {
+ ret = -EEXIST;
+ goto out;
+ }
+
+ ret = btrfs_insert_file_extent(trans, root, ino, offset, 0, 0, len);
+out:
+ btrfs_free_path(path);
+ return ret;
+}