diff --git a/modify/mirror.c b/modify/mirror.c
index f17dc9a7..c89927f6 100644
--- a/modify/mirror.c
+++ b/modify/mirror.c
@@ -199,9 +199,177 @@ static int modify_logical(struct btrfs_fs_info *fs_info, u64 logical, u64 len,
 	return 0;
 }
 
+struct root_ino_offset {
+	u64 root;
+	u64 ino;
+	u64 offset;
+	bool set;
+};
+
+static int modify_root_ino_offset(struct btrfs_fs_info *fs_info,
+				  struct root_ino_offset *dest,
+				  u64 length, int stripe)
+{
+	struct btrfs_root *root;
+	struct btrfs_key key;
+	struct btrfs_path path;
+	u32 sectorsize = fs_info->tree_root->sectorsize;
+	u64 cur = dest->offset;
+	int ret;
+
+	if (!is_fstree(dest->root)) {
+		error("rootid %llu is not a valid subvolume id", dest->root);
+		return -EINVAL;
+	}
+	if (!IS_ALIGNED(dest->offset, sectorsize)) {
+		error("offset %llu is not aligned to sectorsize %u",
+			dest->offset, sectorsize);
+		return -EINVAL;
+	}
+
+	key.objectid = dest->root;
+	key.type = BTRFS_ROOT_ITEM_KEY;
+	key.offset = (u64)-1;
+
+	root = btrfs_read_fs_root(fs_info, &key);
+	if (IS_ERR(root)) {
+		ret = PTR_ERR(root);
+		error("failed to read out root %llu: %s",
+			dest->root, strerror(-ret));
+		return ret;
+	}
+
+	btrfs_init_path(&path);
+	key.objectid = dest->ino;
+	key.type = BTRFS_EXTENT_DATA_KEY;
+	key.offset = cur;
+
+	ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0);
+	if (ret < 0)
+		goto out;
+	if (ret > 0) {
+		ret = btrfs_previous_item(root, &path, dest->ino,
+					  BTRFS_EXTENT_DATA_KEY);
+		if (ret < 0)
+			goto out;
+		if (ret > 0) {
+			error("root %llu ino %llu offset %llu not found",
+				dest->root, dest->ino, dest->offset);
+			ret = -ENOENT;
+			goto out;
+		}
+	}
+	while (cur < dest->offset + length) {
+		struct extent_buffer *leaf = path.nodes[0];
+		struct btrfs_file_extent_item *fi;
+		int slot = path.slots[0];
+		u64 corrupt_start;
+		u64 corrupt_len;
+
+		btrfs_item_key_to_cpu(leaf, &key, slot);
+		if (key.objectid != dest->ino ||
+		    key.type != BTRFS_EXTENT_DATA_KEY)
+			goto out;
+
+		fi = btrfs_item_ptr(leaf, slot, struct btrfs_file_extent_item);
+		/* Skip inline extent */
+		if (btrfs_file_extent_type(leaf, fi) ==
+				BTRFS_FILE_EXTENT_INLINE) {
+			cur = key.offset + sectorsize;
+			goto next;
+		}
+
+		/* Skip unrelated extent */
+		if (key.offset + btrfs_file_extent_num_bytes(leaf, fi) <=
+				dest->offset) {
+			cur = key.offset + btrfs_file_extent_num_bytes(leaf,
+					fi);
+			goto next;
+		}
+
+		/* Skip hole or prealloc extent */
+		if (btrfs_file_extent_disk_num_bytes(leaf, fi) == 0 ||
+		    btrfs_file_extent_type(leaf, fi) ==
+				BTRFS_FILE_EXTENT_PREALLOC) {
+			cur = key.offset + btrfs_file_extent_num_bytes(leaf,
+						fi);
+			goto next;
+		}
+
+		/* For compressed extent, corrupt all on-disk data */
+		if (btrfs_file_extent_compression(leaf, fi) !=
+			BTRFS_COMPRESS_NONE) {
+			ret = modify_logical(fs_info,
+				btrfs_file_extent_disk_bytenr(leaf, fi),
+				btrfs_file_extent_disk_num_bytes(leaf, fi),
+				stripe);
+			if (ret < 0)
+				goto out;
+			cur = key.offset +
+				btrfs_file_extent_num_bytes(leaf, fi);
+			goto next;
+		}
+
+		/* Regular plain extents, corrupt given range */
+		corrupt_start = btrfs_file_extent_disk_bytenr(leaf, fi) +
+			cur - key.offset + btrfs_file_extent_offset(leaf, fi);
+		corrupt_len = min(dest->offset + length, key.offset +
+				btrfs_file_extent_num_bytes(leaf, fi)) - cur;
+		ret = modify_logical(fs_info, corrupt_start, corrupt_len, stripe);
+		if (ret < 0)
+			goto out;
+		cur += corrupt_len;
+
+next:
+		ret = btrfs_next_item(root, &path);
+		if (ret < 0)
+			goto out;
+		if (ret > 0) {
+			ret = 0;
+			goto out;
+		}
+	}
+out:
+	btrfs_release_path(&path);
+	return ret;
+}
+
+static void parse_root_ino_offset(struct root_ino_offset *dest, char *optarg)
+{
+	char *this_char;
+	char *save_ptr = NULL;
+	int i = 0;
+
+	for (this_char = strtok_r(optarg, ",", &save_ptr);
+	     this_char != NULL;
+	     this_char = strtok_r(NULL, ",", &save_ptr)) {
+		switch (i) {
+		case 0:
+			dest->root = arg_strtou64(this_char);
+			break;
+		case 1:
+			dest->ino = arg_strtou64(this_char);
+			break;
+		case 2:
+			dest->offset = arg_strtou64(this_char);
+			break;
+		default:
+			goto error;
+		}
+		i++;
+	}
+error:
+	if (i != 3) {
+		error("--root-ino-offset must be specified in number,number,number form");
+		exit(1);
+	}
+	dest->set = true;
+}
+
 int modify_mirror(int argc, char **argv)
 {
 	struct btrfs_fs_info *fs_info;
+	struct root_ino_offset dest = { 0 };
 	char *device;
 	u64 length = (u64)-1;
 	u64 logical = (u64)-1;
@@ -211,13 +379,16 @@ int modify_mirror(int argc, char **argv)
 	while (1) {
 		int c;
 		enum { GETOPT_VAL_LOGICAL = 257, GETOPT_VAL_LENGTH,
-			GETOPT_VAL_STRIPE };
+			GETOPT_VAL_STRIPE, GETOPT_VAL_ROOT_INO_OFFSET };
 		static const struct option long_options[] = {
 			{ "logical", required_argument, NULL,
 				GETOPT_VAL_LOGICAL },
 			{ "length", required_argument, NULL,
 				GETOPT_VAL_LENGTH },
-			{ "stripe", required_argument, NULL, GETOPT_VAL_STRIPE }
+			{ "stripe", required_argument, NULL, GETOPT_VAL_STRIPE },
+			{ "root-ino-offset", required_argument, NULL,
+				GETOPT_VAL_ROOT_INO_OFFSET},
+			{ NULL, 0, NULL, 0 }
 		};
 
 		c = getopt_long(argc, argv, "", long_options, NULL);
@@ -233,6 +404,9 @@ int modify_mirror(int argc, char **argv)
 		case GETOPT_VAL_STRIPE:
 			stripe = strtostripe(optarg);
 			break;
+		case GETOPT_VAL_ROOT_INO_OFFSET:
+			parse_root_ino_offset(&dest, optarg);
+			break;
 		case '?':
 		case 'h':
 			usage(modify_mirror_usage);
@@ -252,9 +426,13 @@ int modify_mirror(int argc, char **argv)
 		error("%s is currently mounted, aborting", device);
 		return -EINVAL;
 	}
-	if (logical == (u64)-1) {
-		error("--logical must be specified");
-		return -EINVAL;
+	if (logical == (u64)-1 && !dest.set) {
+		error("--logical or --root-ino-offset must be specified");
+		return 1;
+	}
+	if (logical != (u64)-1 && dest.set) {
+		error("--logical conflicts with --root-ino-offset");
+		return 1;
 	}
 	if (stripe == STRIPE_UNINITILIZED) {
 		printf("--stripe not specified, fallback to 0 (1st stripe)\n");
@@ -271,7 +449,10 @@ int modify_mirror(int argc, char **argv)
 			fs_info->tree_root->sectorsize);
 		length = fs_info->tree_root->sectorsize;
 	}
-	ret = modify_logical(fs_info, logical, length, stripe);
+	if (logical != (u64)-1)
+		ret = modify_logical(fs_info, logical, length, stripe);
+	else
+		ret = modify_root_ino_offset(fs_info, &dest, length, stripe);
 	if (ret < 0)
 		error("failed to modify btrfs: %s", strerror(-ret));
 	else
