From patchwork Wed Jun 24 16:03:11 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Marek_Beh=C3=BAn?= X-Patchwork-Id: 1316364 X-Patchwork-Delegate: trini@ti.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=lists.denx.de (client-ip=85.214.62.61; helo=phobos.denx.de; envelope-from=u-boot-bounces@lists.denx.de; receiver=) Authentication-Results: ozlabs.org; dmarc=pass (p=none dis=none) header.from=nic.cz Authentication-Results: ozlabs.org; dkim=pass (1024-bit key; secure) header.d=nic.cz header.i=@nic.cz header.a=rsa-sha256 header.s=default header.b=NgbHyJVF; dkim-atps=neutral Received: from phobos.denx.de (phobos.denx.de [85.214.62.61]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 49sSk62CJSz9s1x for ; Thu, 25 Jun 2020 02:08:30 +1000 (AEST) Received: from h2850616.stratoserver.net (localhost [IPv6:::1]) by phobos.denx.de (Postfix) with ESMTP id B7767820E8; Wed, 24 Jun 2020 18:05:52 +0200 (CEST) Authentication-Results: phobos.denx.de; dmarc=pass (p=none dis=none) header.from=nic.cz Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=u-boot-bounces@lists.denx.de Authentication-Results: phobos.denx.de; dkim=pass (1024-bit key; secure) header.d=nic.cz header.i=@nic.cz header.b="NgbHyJVF"; dkim-atps=neutral Received: by phobos.denx.de (Postfix, from userid 109) id 28328820A2; Wed, 24 Jun 2020 18:04:19 +0200 (CEST) X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on phobos.denx.de X-Spam-Level: X-Spam-Status: No, score=-2.0 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,RCVD_IN_MSPIKE_H4,RCVD_IN_MSPIKE_WL, SPF_HELO_NONE,URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.2 Received: from mail.nic.cz (lists.nic.cz [217.31.204.67]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by phobos.denx.de (Postfix) with ESMTPS id 09A06820AA for ; Wed, 24 Jun 2020 18:03:26 +0200 (CEST) Authentication-Results: phobos.denx.de; dmarc=pass (p=none dis=none) header.from=nic.cz Authentication-Results: phobos.denx.de; spf=none smtp.mailfrom=marek.behun@nic.cz Received: from dellmb.labs.office.nic.cz (unknown [IPv6:2001:1488:fffe:6:cac7:3539:7f1f:463]) by mail.nic.cz (Postfix) with ESMTP id A2CC01409ED; Wed, 24 Jun 2020 18:03:25 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=nic.cz; s=default; t=1593014605; bh=crTekNtyUAaWtSyW6vKiZanzXRDVzwloE6FSU/DeQWI=; h=From:To:Date; b=NgbHyJVFtnjP3yCpncAdxvzWvBa4he6hkKzFI8CHoNAQqmCz9PE2MloDgtiVv3nHZ JcvXieB/EQjzTBcSNJ4tpaoa6+MGJzCof8S33fKB0ulEVMO1vyGx2LWrZX7kHWkfP7 fLEbiUim9EYoe77gU8S1sZvnD3wkKiUgPkm4WMq0= From: =?utf-8?q?Marek_Beh=C3=BAn?= To: u-boot@lists.denx.de Cc: =?utf-8?q?Alberto_S=C3=A1nchez_Molero?= , Marek Vasut , Pierre Bourdon , Simon Glass , Tom Rini , Yevgeny Popovych , linux-btrfs@vger.kernel.org, Qu Wenruo , =?utf-8?q?Marek_Beh=C3=BAn?= Subject: [PATCH U-BOOT v3 25/30] fs: btrfs: Implement btrfs_file_read() Date: Wed, 24 Jun 2020 18:03:11 +0200 Message-Id: <20200624160316.5001-26-marek.behun@nic.cz> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20200624160316.5001-1-marek.behun@nic.cz> References: <20200624160316.5001-1-marek.behun@nic.cz> MIME-Version: 1.0 X-Spamd-Bar: / X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.30rc1 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" X-Virus-Scanned: clamav-milter 0.102.2 at phobos.denx.de X-Virus-Status: Clean From: Qu Wenruo This version of btrfs_file_read() has the following new features: - Tries all mirrors - More handling on unaligned size - Better compressed extent handling The old implementation doesn't handle compressed extent with offset properly: we need to read out the whole compressed extent, then decompress the whole extent, and only then copy the requested part. Signed-off-by: Qu Wenruo Reviewed-by: Marek BehĂșn --- fs/btrfs/btrfs.c | 48 +++++++++------- fs/btrfs/btrfs.h | 2 + fs/btrfs/inode.c | 146 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 176 insertions(+), 20 deletions(-) diff --git a/fs/btrfs/btrfs.c b/fs/btrfs/btrfs.c index 7eb01c4ff9..ffd96427cc 100644 --- a/fs/btrfs/btrfs.c +++ b/fs/btrfs/btrfs.c @@ -253,37 +253,45 @@ out: int btrfs_read(const char *file, void *buf, loff_t offset, loff_t len, loff_t *actread) { - struct __btrfs_root root = btrfs_info.fs_root; - struct btrfs_inode_item inode; - u64 inr, rd; + struct btrfs_fs_info *fs_info = current_fs_info; + struct btrfs_root *root; + loff_t real_size; + u64 ino; u8 type; + int ret; - inr = __btrfs_lookup_path(&root, root.root_dirid, file, &type, &inode, - 40); - - if (inr == -1ULL) { - printf("Cannot lookup file %s\n", file); - return -1; + ASSERT(fs_info); + ret = btrfs_lookup_path(fs_info->fs_root, BTRFS_FIRST_FREE_OBJECTID, + file, &root, &ino, &type, 40); + if (ret < 0) { + error("Cannot lookup file %s", file); + return ret; } if (type != BTRFS_FT_REG_FILE) { - printf("Not a regular file: %s\n", file); - return -1; + error("Not a regular file: %s", file); + return -EINVAL; } - if (!len) - len = inode.size; + if (!len) { + ret = btrfs_size(file, &real_size); + if (ret < 0) { + error("Failed to get inode size: %s", file); + return ret; + } + len = real_size; + } - if (len > inode.size - offset) - len = inode.size - offset; + if (len > real_size - offset) + len = real_size - offset; - rd = __btrfs_file_read(&root, inr, offset, len, buf); - if (rd == -1ULL) { - printf("An error occured while reading file %s\n", file); - return -1; + ret = btrfs_file_read(root, ino, offset, len, buf); + if (ret < 0) { + error("An error occured while reading file %s", file); + return ret; } - *actread = rd; + *actread = len; return 0; } diff --git a/fs/btrfs/btrfs.h b/fs/btrfs/btrfs.h index 32ea2fc53a..268ca077d9 100644 --- a/fs/btrfs/btrfs.h +++ b/fs/btrfs/btrfs.h @@ -59,6 +59,8 @@ int btrfs_readlink(struct btrfs_root *root, u64 ino, char *target); u64 __btrfs_lookup_path(struct __btrfs_root *, u64, const char *, u8 *, struct btrfs_inode_item *, int); u64 __btrfs_file_read(const struct __btrfs_root *, u64, u64, u64, char *); +int btrfs_file_read(struct btrfs_root *root, u64 ino, u64 file_offset, u64 len, + char *dest); /* subvolume.c */ u64 btrfs_get_default_subvol_objectid(void); diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 11eb30c27a..0c2b2b5705 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -926,3 +926,149 @@ check_next: *next_offset = key.offset; return 1; } + +static int read_and_truncate_page(struct btrfs_path *path, + struct btrfs_file_extent_item *fi, + int start, int len, char *dest) +{ + struct extent_buffer *leaf = path->nodes[0]; + struct btrfs_fs_info *fs_info = leaf->fs_info; + u64 aligned_start = round_down(start, fs_info->sectorsize); + u8 extent_type; + char *buf; + int page_off = start - aligned_start; + int page_len = fs_info->sectorsize - page_off; + int ret; + + ASSERT(start + len <= aligned_start + fs_info->sectorsize); + buf = malloc_cache_aligned(fs_info->sectorsize); + if (!buf) + return -ENOMEM; + + extent_type = btrfs_file_extent_type(leaf, fi); + if (extent_type == BTRFS_FILE_EXTENT_INLINE) { + ret = btrfs_read_extent_inline(path, fi, buf); + memcpy(dest, buf + page_off, min(page_len, ret)); + free(buf); + return len; + } + + ret = btrfs_read_extent_reg(path, fi, + round_down(start, fs_info->sectorsize), + fs_info->sectorsize, buf); + if (ret < 0) { + free(buf); + return ret; + } + memcpy(dest, buf + page_off, page_len); + free(buf); + return len; +} + +int btrfs_file_read(struct btrfs_root *root, u64 ino, u64 file_offset, u64 len, + char *dest) +{ + struct btrfs_fs_info *fs_info = root->fs_info; + struct btrfs_file_extent_item *fi; + struct btrfs_path path; + struct btrfs_key key; + u64 aligned_start = round_down(file_offset, fs_info->sectorsize); + u64 aligned_end = round_down(file_offset + len, fs_info->sectorsize); + u64 next_offset; + u64 cur = aligned_start; + int ret = 0; + + btrfs_init_path(&path); + + /* Set the whole dest all zero, so we won't need to bother holes */ + memset(dest, 0, len); + + /* Read out the leading unaligned part */ + if (aligned_start != file_offset) { + ret = lookup_data_extent(root, &path, ino, aligned_start, + &next_offset); + if (ret < 0) + goto out; + if (ret == 0) { + /* Read the unaligned part out*/ + fi = btrfs_item_ptr(path.nodes[0], path.slots[0], + struct btrfs_file_extent_item); + ret = read_and_truncate_page(&path, fi, file_offset, + round_up(file_offset, fs_info->sectorsize) - + file_offset, dest); + if (ret < 0) + goto out; + cur += fs_info->sectorsize; + } else { + /* The whole file is a hole */ + if (!next_offset) { + memset(dest, 0, len); + return len; + } + cur = next_offset; + } + } + + /* Read the aligned part */ + while (cur < aligned_end) { + u64 extent_num_bytes; + u8 type; + + btrfs_release_path(&path); + ret = lookup_data_extent(root, &path, ino, cur, &next_offset); + if (ret < 0) + goto out; + if (ret > 0) { + /* No next, direct exit */ + if (!next_offset) { + ret = 0; + goto out; + } + } + fi = btrfs_item_ptr(path.nodes[0], path.slots[0], + struct btrfs_file_extent_item); + btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]); + type = btrfs_file_extent_type(path.nodes[0], fi); + if (type == BTRFS_FILE_EXTENT_INLINE) { + ret = btrfs_read_extent_inline(&path, fi, dest); + goto out; + } + /* Skip holes, as we have zeroed the dest */ + if (type == BTRFS_FILE_EXTENT_PREALLOC || + btrfs_file_extent_disk_bytenr(path.nodes[0], fi) == 0) { + cur = key.offset + btrfs_file_extent_num_bytes( + path.nodes[0], fi); + continue; + } + + /* Read the remaining part of the extent */ + extent_num_bytes = btrfs_file_extent_num_bytes(path.nodes[0], + fi); + ret = btrfs_read_extent_reg(&path, fi, cur, + min(extent_num_bytes, aligned_end - cur), + dest + cur - file_offset); + if (ret < 0) + goto out; + cur += min(extent_num_bytes, aligned_end - cur); + } + + /* Read the tailing unaligned part*/ + if (file_offset + len != aligned_end) { + btrfs_release_path(&path); + ret = lookup_data_extent(root, &path, ino, aligned_end, + &next_offset); + /* <0 is error, >0 means no extent */ + if (ret) + goto out; + fi = btrfs_item_ptr(path.nodes[0], path.slots[0], + struct btrfs_file_extent_item); + ret = read_and_truncate_page(&path, fi, aligned_end, + file_offset + len - aligned_end, + dest + aligned_end - file_offset); + } +out: + btrfs_release_path(&path); + if (ret < 0) + return ret; + return len; +}