From patchwork Thu Jun 15 03:29:11 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Yuezhang.Mo@sony.com" X-Patchwork-Id: 13280687 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 103F5EB64D9 for ; Thu, 15 Jun 2023 04:33:26 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S243036AbjFOEdY (ORCPT ); Thu, 15 Jun 2023 00:33:24 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:37104 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229944AbjFOEdW (ORCPT ); Thu, 15 Jun 2023 00:33:22 -0400 Received: from mx07-001d1705.pphosted.com (mx07-001d1705.pphosted.com [185.132.183.11]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id C32D71BDB for ; Wed, 14 Jun 2023 21:33:20 -0700 (PDT) Received: from pps.filterd (m0209326.ppops.net [127.0.0.1]) by mx08-001d1705.pphosted.com (8.17.1.19/8.17.1.19) with ESMTP id 35ENLK1U030487; Thu, 15 Jun 2023 03:29:18 GMT DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sony.com; h=from : to : cc : subject : date : message-id : mime-version : content-type : content-transfer-encoding; s=S1; bh=F4c8hw/QvO78bZtYo+SYTaR3BglNGnRcIp4w1WXNDXk=; b=m7BWKkeQXLNUqJYuCDS9XeRc3ZuanZOOhHp+qjfFNKPrhmvJru3zbdafyH12RPX86oEq KkSepq9fSgmhxbh/kv2Fe3YvCbn7vE81RkN+RZpfnFB2f0A6KW2w5TXRm8R8uEnto3r1 7sm5VW7HJ6b/49Hik8IYPzUS+2CpEMK2+59ls5dDM6Wol4b/uhamI076QJMQpdzf1sP4 VfjuIPqJ4jnfhrDNDb11ozc7d0xWiOpioLy0itKoQ1B/MjRsrPjmvmN3cV7PGDS/M8RV JM60jlDNyniuJ7KEDvejkQ4CuzTjhww2jQNPC3VjOS0BEre84RVvufO3uHYC0USjks0j Zg== Received: from apc01-sg2-obe.outbound.protection.outlook.com (mail-sgaapc01lp2106.outbound.protection.outlook.com [104.47.26.106]) by mx08-001d1705.pphosted.com (PPS) with ESMTPS id 3r6uunsna5-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK); Thu, 15 Jun 2023 03:29:17 +0000 ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=eWASmDJKWZEbtR2rppbrr01J4KHdWOSqefklLzO1YBTu8LRrCrv4QB9/53x1OXFyBV/bC5vAgCndEd7Jh+U06RUPCIoXrEgF8yUYGQ4VmOKG/uFtb9k6znt+TvLSJJXqGKDe+HDsABZ2RC2fsOWTZic1oDfq4IRquODqLOjMEZivQ8y4UKgMdiftuvYuinglfqWyCLQ264pzUxEB94vuvrLwsegF1xlA7UMZf0lufGr0lp3ENcQ7B8GM5maGIUb9SE2JbGqsoZG/rLjsp5TwHLaAPi5rFyVzqzlTBiOiAhKwqsSm++vxYJE20QtBdfwRZRt9Wca0qIykLoF5H52M3g== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=F4c8hw/QvO78bZtYo+SYTaR3BglNGnRcIp4w1WXNDXk=; b=ntFaaFyirapb23nOl6mztGY+igLgnzMWNyWCIOMnLLqUzvFbZyT4jXvcjG2qOusqRvzJGk46cptmazery6YLjBFtYWfn+bqJ2xIEpOom7U0ezUVJW2/G2uu5IKn26bvmwh3ilUKsbey3txUwVG/wRMl/eZnxoffblpUEjqbWcSaUetTgMLC7SUXlRWjAglSN7TyVQ4NYV8SW2TuxI//+MShFvw+06tkxOD6zoKS9H+5mLsgVre91LjNaiuOmvy/OlATocWuwdd+oUM969OTW5Jg5q1dZwzyJXirNKZ4TTrN7fh44XdJ/vsDCPJ85VtqVpgafDFCLQgFO/CnSLNE2sQ== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=sony.com; dmarc=pass action=none header.from=sony.com; dkim=pass header.d=sony.com; arc=none Received: from PUZPR04MB6316.apcprd04.prod.outlook.com (2603:1096:301:fc::7) by KL1PR0401MB4212.apcprd04.prod.outlook.com (2603:1096:820:27::14) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.6500.25; Thu, 15 Jun 2023 03:29:11 +0000 Received: from PUZPR04MB6316.apcprd04.prod.outlook.com ([fe80::ce2d:a9dc:4955:5275]) by PUZPR04MB6316.apcprd04.prod.outlook.com ([fe80::ce2d:a9dc:4955:5275%5]) with mapi id 15.20.6455.037; Thu, 15 Jun 2023 03:29:11 +0000 From: "Yuezhang.Mo@sony.com" To: "linkinjeon@kernel.org" , "sj1557.seo@samsung.com" CC: "linux-fsdevel@vger.kernel.org" , "Andy.Wu@sony.com" , "Wataru.Aoyama@sony.com" Subject: [PATCH v1 1/2] exfat: change to get file size from DataLength Thread-Topic: [PATCH v1 1/2] exfat: change to get file size from DataLength Thread-Index: AdmfOM+RRg1waKiWT3mEw2cVv4oUyg== Date: Thu, 15 Jun 2023 03:29:11 +0000 Message-ID: Accept-Language: zh-CN, en-US Content-Language: zh-CN X-MS-Has-Attach: X-MS-TNEF-Correlator: x-ms-publictraffictype: Email x-ms-traffictypediagnostic: PUZPR04MB6316:EE_|KL1PR0401MB4212:EE_ x-ms-office365-filtering-correlation-id: 433e14b8-3824-45b7-ba45-08db6d50afc2 x-ms-exchange-senderadcheck: 1 x-ms-exchange-antispam-relay: 0 x-microsoft-antispam: BCL:0; x-microsoft-antispam-message-info: J5D59+4hBFbvHP80d2B0czS94fwSwKVdItKC2KDGmuPeSKMGGHps6Na+8uXm1qOtPFPhVWhkl/PQ6b1q0PyUXzwc7HpEL+rKsJ646SM3h0QlMSss9bUxDt4awBI7WDjuA/JBjHAjb88No9uIBoqjHKznsmNE8h1zX3Us1W/kN/MnEjVWJWHnpSHgOKGtN3iJ0YNtTwIyQeP/MvM9YgGu4oijUsdbHVaxexQe85GhU7TWG4rO7to4PHOPojX14a5XY4vayTw2xAru5po6c9Wt9hlagBpO9i5ZEODRZMJM/V39Cj2hSYQHBc96XrPNQXNY2/bGRll1N99s6ViFqF6NF9mUZZQsoPCdz4ndqVvWQSywPq/JA9lXEMXVMLL38LSQ++C9Q1b475nMbqnb2IB/aKNqu9iNmimC22YN+F6F7sbg27OtyhV/+OUk95RT8UnBmGCt6qdAEjLW/z4U4ZLK16ShGtl8U9dP3v3VT/qhIm+YLf0kqpSwYorye8AcHTCEj1O9IIEMqDIPnImwjhLviK9oloqChRGX7lwyx2IZRkUOl0gPhEFLDg23uvZ7UJicAAjCUQyY6tdAoXW/qEF4in6uS031CF1Cu7TpieJ0rcAxNr2YtU8v9BakkAQ5SzgS x-forefront-antispam-report: CIP:255.255.255.255;CTRY:;LANG:en;SCL:1;SRV:;IPV:NLI;SFV:NSPM;H:PUZPR04MB6316.apcprd04.prod.outlook.com;PTR:;CAT:NONE;SFS:(13230028)(4636009)(396003)(366004)(136003)(346002)(39860400002)(376002)(451199021)(76116006)(4326008)(30864003)(2906002)(86362001)(38070700005)(316002)(64756008)(66446008)(66476007)(66556008)(66946007)(41300700001)(110136005)(5660300002)(52536014)(8936002)(8676002)(54906003)(38100700002)(478600001)(122000001)(82960400001)(7696005)(71200400001)(83380400001)(107886003)(55016003)(9686003)(26005)(6506007)(186003)(33656002);DIR:OUT;SFP:1102; x-ms-exchange-antispam-messagedata-chunkcount: 1 x-ms-exchange-antispam-messagedata-0: =?utf-8?q?1UrexdE5i67oKU1p4u8gZ8SlNLof?= =?utf-8?q?pGbTv8rN2b7PuW5HTxw2eSzca2xiPshJt8cs0tpcXwedOm67YECAJqczOymKgPX3Y?= =?utf-8?q?ClGBDNnewnNQWZXo0A46yeyko538g5UGfWMkzk/1UGBfboXbpyAKXK2Tw/yAORrRN?= =?utf-8?q?Y+8G8Rq6CbRg9s7mKogiBc4kSBCbQo/R1rKE37xOCXbAXCqKW/+7jxy1L7v2k8EKM?= =?utf-8?q?/Qfkw1+NwfOxxOMeyejJxroClBCHfKuNnq+ynVajKbzMwRw2QcleY/TmltOXJyIuv?= =?utf-8?q?N26CV0UiE27+NkJ8bFGNU1+qMR6fYb4kDJfZIq20k42hAuyx2+eTLnfcFVPFeT89g?= =?utf-8?q?kjWi7nP2EjOTtMRW3wBw2R/EYi7R5uQf4vH3LPJce8k6wVYWqa9GZqta6VfGlG4wU?= =?utf-8?q?zn9tscCGDRzP0n+deFTl+AuFSxsfOJ0BxjlBQDfNUjBTgPyilHKiiJnDSQy1116+x?= =?utf-8?q?Ol1dNUprkxb/1KOUD1XgWiPt1LeQF4SJ3PSqbwYwRKT587p2UwCLMola1l9kR78/d?= =?utf-8?q?mXYLCGj3neyLyqpjv/rhPALp4T2lSaWz/mgTlBZRm3bxCDwtt13r/l1Tadb+BTcvj?= =?utf-8?q?R7pf8VRBdqh0fmUCRIkUsE1w6hww8hW5n7m5tRcQhEpnAfjVDacmsiovsZf6OKeIe?= =?utf-8?q?CQfGovwjmeKQpT0yG5DPaFjYf2LcdzMZKugejvL0zLkU9U/9Vv9xYG9VsqKTUxEXx?= =?utf-8?q?I/TLxTyQ39CC3KUInQ7GrpWbLKUBUakYwHbmygz8S7RgwOwa83OvBYmJlMdRxQNvt?= =?utf-8?q?NQ6YUlKHrWJvH4lYyEHaqN0FtC+IdrxhFowQtXeLQjo751gks3nkXKEubEgVdHYGL?= =?utf-8?q?bUP328gv9QlKmNqSAAZR5AQkagTAxR5Ak2Dys0UBRqg7YS3UyVOTYOgMQNFfCTH/5?= =?utf-8?q?eJbpM9dF8V77oeBw+5r2C+l2fp0Dw8dJ/enXQH3vGJe1HdnnCn71jSLL8qiZFhC/X?= =?utf-8?q?S4i2Ko4NcezmOSkejjXbyErBDPo/QDvJZXSx6G0ZXFLc/GPgTahPrthVIqFl473wb?= =?utf-8?q?NIR9gwTKubscxKawqWLZsUZLd1H7m5uzctBJo4CTYzgsNtD1PVRFS2VqkXcaCO7yY?= =?utf-8?q?buK/JQjHkyMimBk5Dd2hLFXSVND0L3bAvwSNr47upNJNORTDbfD5+hgdcGAR5bA26?= =?utf-8?q?dFGXZWvjVFVigAx+iK5Por8iQCKX1PBnduZBSE2ggRMDAjTT7OXnZi93s4t2fMWRV?= =?utf-8?q?TG02gal8VfssxM9LJWRP2czfXduQAqChDDOioIgfCvxx26Uuf5nzqqKVIAN+jTsi/?= =?utf-8?q?/A7QHO91EL+jjWfiMAHTU/anm0pylnslJueCpF48x7PHqH0y3XLJ9jCf0s5BXtkJr?= =?utf-8?q?nASCSz2Q1jkt9YZ5FPhkeg1Ighp5dM7986R9sU40nHa3a1AOd/xOQatNJA0PuUx/v?= =?utf-8?q?lIl4V2yHv2TpXsgL35rfH7XB8bIm40/gGit870rmwKyjbHS6yV8MfpOZXmfyB8HaF?= =?utf-8?q?AoYYeFHps3kDgJQmsEhbySNcqI4Q0twhPey4+sVTsyeTpK17FVmO0VfoDT3+dAvOk?= =?utf-8?q?gS7l5S6Ei/XR?= MIME-Version: 1.0 X-MS-Exchange-AntiSpam-ExternalHop-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-ExternalHop-MessageData-0: 44Mfj9jZ2ATBozhU67O/KQmoYn/1S85MBR71rr+zW8pWDi6TSE65vaTbL/GvzrHANPddLUccIJHBaI6leAO2adOlecSIumF622y8tRAzC+JfPsOgi1vMXYHLGpZZcRsp5cRLKdSK0DAaOroHuF+0Y4YV1cEFf1KZ340T3sGEXajhB1bFiWfaylXzgzRudVIlY6YpI91I4eNYq2tFd1nhAMU/vXEFqTTexVqX4CehSnItDiDz5iFKW87r0yzF7R2T644sbTQX3dJUc9PQS8b1owRaK6SPDx7PrnZqLruDmCkxFAFCSr6eJTzjFhZ0EmrWs2w5puh4Le3SAFkjfXNAWTuZuhwYSAytZP37uKaWIimpmaDPXOP5OpFu5Dx2V8iTYbv+j6u16+9k7wcR0L9vEckx21a3xtsnkd1Q4XWWUqJaG1Z5i8ojmvPRnkIt0Yo5d9JBMkvPZfVg2B7tUHVteZuDQZjrkUHIwGuYCk+FcjE0D0VTOdYtZnOL/CDw96G252xzAxCX6Tt88b8iqdTO7zoKvI8FgU7R/166LHl+z8PD3vhKWDW+B0qefz/Zc3uOxStYWEi45Tc1POYiYrkkY++YCUyD5VGaAN5aegiaESPw6zkUtmptnLAURboYamzIVk+P2EJ/9dBilpPRhcP3pzmFnkjIdAfJ2dF7Mgj00zoyFWNxXB0+rg7TtSzpq1DpKfutWlUPQ/gFZB+OL6yJya3lVb9tQM2+8u5djhbAsK44HIYH3jDhN8USVI8VY74l/gv4dqfxefu89JEMyWkDgqEdKfqq2cvnZaUJcQKkG0veObX2IKTb2hiwelczBvypKXj8R59OnorVTWTNh/hSXg== X-OriginatorOrg: sony.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-AuthSource: PUZPR04MB6316.apcprd04.prod.outlook.com X-MS-Exchange-CrossTenant-Network-Message-Id: 433e14b8-3824-45b7-ba45-08db6d50afc2 X-MS-Exchange-CrossTenant-originalarrivaltime: 15 Jun 2023 03:29:11.6571 (UTC) X-MS-Exchange-CrossTenant-fromentityheader: Hosted X-MS-Exchange-CrossTenant-id: 66c65d8a-9158-4521-a2d8-664963db48e4 X-MS-Exchange-CrossTenant-mailboxtype: HOSTED X-MS-Exchange-CrossTenant-userprincipalname: XCV6neeJWGlsqVqJmPkMIwZpLp8P2ZSXml3BLEeilOc2NnJaovWfMqufEZCEN7khgHtQpdRGnizl3JS6zF1MxA== X-MS-Exchange-Transport-CrossTenantHeadersStamped: KL1PR0401MB4212 X-Proofpoint-ORIG-GUID: FgG6EyIlyd715H7_7traVuy1BqMupbms X-Proofpoint-GUID: FgG6EyIlyd715H7_7traVuy1BqMupbms X-Sony-Outbound-GUID: FgG6EyIlyd715H7_7traVuy1BqMupbms X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.254,Aquarius:18.0.957,Hydra:6.0.573,FMLib:17.11.176.26 definitions=2023-06-14_14,2023-06-14_02,2023-05-22_02 Precedence: bulk List-ID: X-Mailing-List: linux-fsdevel@vger.kernel.org In stream extension directory entry, the ValidDataLength field describes how far into the data stream user data has been written, and the DataLength field describes the file size. Signed-off-by: Yuezhang Mo Reviewed-by: Andy Wu Reviewed-by: Aoyama Wataru --- fs/exfat/exfat_fs.h | 2 + fs/exfat/file.c | 139 ++++++++++++++++++++++++++++++++++++++++++-- fs/exfat/inode.c | 108 +++++++++++++++++++++++++++++----- fs/exfat/namei.c | 7 ++- 4 files changed, 236 insertions(+), 20 deletions(-) diff --git a/fs/exfat/exfat_fs.h b/fs/exfat/exfat_fs.h index 729ada9e26e8..8d6248367433 100644 --- a/fs/exfat/exfat_fs.h +++ b/fs/exfat/exfat_fs.h @@ -208,6 +208,7 @@ struct exfat_dir_entry { unsigned char flags; unsigned short attr; loff_t size; + loff_t valid_size; unsigned int num_subdirs; struct timespec64 atime; struct timespec64 mtime; @@ -317,6 +318,7 @@ struct exfat_inode_info { loff_t i_size_aligned; /* on-disk position of directory entry or 0 */ loff_t i_pos; + loff_t valid_size; /* hash by i_location */ struct hlist_node i_hash_fat; /* protect bmap against truncate */ diff --git a/fs/exfat/file.c b/fs/exfat/file.c index e99183a74611..680597f3237d 100644 --- a/fs/exfat/file.c +++ b/fs/exfat/file.c @@ -8,11 +8,12 @@ #include #include #include +#include #include "exfat_raw.h" #include "exfat_fs.h" -static int exfat_cont_expand(struct inode *inode, loff_t size) +static int exfat_cont_expand(struct inode *inode, loff_t size, bool sync) { struct address_space *mapping = inode->i_mapping; loff_t start = i_size_read(inode), count = size - i_size_read(inode); @@ -23,9 +24,10 @@ static int exfat_cont_expand(struct inode *inode, loff_t size) return err; inode->i_ctime = inode->i_mtime = current_time(inode); + EXFAT_I(inode)->valid_size = size; mark_inode_dirty(inode); - if (!IS_SYNC(inode)) + if (!sync) return 0; err = filemap_fdatawrite_range(mapping, start, start + count - 1); @@ -143,6 +145,9 @@ int __exfat_truncate(struct inode *inode) ei->start_clu = EXFAT_EOF_CLUSTER; } + if (i_size_read(inode) < ei->valid_size) + ei->valid_size = i_size_read(inode); + if (ei->type == TYPE_FILE) ei->attr |= ATTR_ARCHIVE; @@ -251,7 +256,7 @@ int exfat_setattr(struct mnt_idmap *idmap, struct dentry *dentry, if ((attr->ia_valid & ATTR_SIZE) && attr->ia_size > i_size_read(inode)) { - error = exfat_cont_expand(inode, attr->ia_size); + error = exfat_cont_expand(inode, attr->ia_size, IS_SYNC(inode)); if (error || attr->ia_valid == ATTR_SIZE) return error; attr->ia_valid &= ~ATTR_SIZE; @@ -379,15 +384,139 @@ int exfat_file_fsync(struct file *filp, loff_t start, loff_t end, int datasync) return blkdev_issue_flush(inode->i_sb->s_bdev); } +static int exfat_file_zeroed_range(struct file *file, loff_t start, loff_t end) +{ + int err; + struct inode *inode = file_inode(file); + struct exfat_inode_info *ei = EXFAT_I(inode); + struct address_space *mapping = inode->i_mapping; + const struct address_space_operations *ops = mapping->a_ops; + + while (start < end) { + u32 zerofrom, len; + struct page *page; + + zerofrom = start & (PAGE_SIZE - 1); + len = PAGE_SIZE - zerofrom; + if (start + len > end) + len = end - start; + + err = ops->write_begin(file, mapping, start, len, &page, NULL); + if (err) + goto out; + + zero_user_segment(page, zerofrom, zerofrom + len); + + err = ops->write_end(file, mapping, start, len, len, page, NULL); + if (err < 0) + goto out; + start += len; + + balance_dirty_pages_ratelimited(mapping); + cond_resched(); + } + + ei->valid_size = end; + mark_inode_dirty(inode); + +out: + return err; +} + +static ssize_t exfat_file_write_iter(struct kiocb *iocb, struct iov_iter *iter) +{ + ssize_t ret; + struct file *file = iocb->ki_filp; + struct inode *inode = file_inode(file); + struct exfat_inode_info *ei = EXFAT_I(inode); + loff_t pos = iocb->ki_pos; + loff_t valid_size; + loff_t size; + size_t count = iov_iter_count(iter); + + inode_lock(inode); + + size = i_size_read(inode); + valid_size = ei->valid_size; + + ret = generic_write_checks(iocb, iter); + if (ret < 0) + goto unlock; + + if (pos + count > size) { + ret = exfat_cont_expand(inode, pos + count, false); + if (ret < 0) + goto unlock; + } + + if (pos > valid_size) { + ret = exfat_file_zeroed_range(file, valid_size, pos); + if (ret < 0) { + exfat_err(inode->i_sb, + "write: fail to zero from %llu to %llu(%ld)", + valid_size, pos, ret); + + goto unlock; + } + } + + ret = __generic_file_write_iter(iocb, iter); + if (ret <= 0) + goto unlock; + + if ((iocb->ki_flags & IOCB_DIRECT) && pos + ret > ei->valid_size) { + ei->valid_size = pos + ret; + mark_inode_dirty(inode); + } + + inode_unlock(inode); + + if (pos > valid_size && iocb_is_dsync(iocb)) { + ssize_t err = vfs_fsync_range(file, valid_size, pos - 1, + iocb->ki_flags & IOCB_SYNC); + if (err < 0) + return err; + } + + return generic_write_sync(iocb, ret); + +unlock: + inode_unlock(inode); + + return ret; +} + +static int exfat_file_mmap(struct file *file, struct vm_area_struct *vma) +{ + int ret; + struct inode *inode = file_inode(file); + struct exfat_inode_info *ei = EXFAT_I(inode); + loff_t start = ((loff_t)vma->vm_pgoff << PAGE_SHIFT); + loff_t end = min_t(loff_t, i_size_read(inode), + start + vma->vm_end - vma->vm_start); + + if ((vma->vm_flags & VM_WRITE) && ei->valid_size < end) { + ret = exfat_file_zeroed_range(file, ei->valid_size, end); + if (ret < 0) { + exfat_err(inode->i_sb, + "mmap: fail to zero from %llu to %llu(%d)", + start, end, ret); + return ret; + } + } + + return generic_file_mmap(file, vma); +} + const struct file_operations exfat_file_operations = { .llseek = generic_file_llseek, .read_iter = generic_file_read_iter, - .write_iter = generic_file_write_iter, + .write_iter = exfat_file_write_iter, .unlocked_ioctl = exfat_ioctl, #ifdef CONFIG_COMPAT .compat_ioctl = exfat_compat_ioctl, #endif - .mmap = generic_file_mmap, + .mmap = exfat_file_mmap, .fsync = exfat_file_fsync, .splice_read = generic_file_splice_read, .splice_write = iter_file_splice_write, diff --git a/fs/exfat/inode.c b/fs/exfat/inode.c index 481dd338f2b8..b85a8030b866 100644 --- a/fs/exfat/inode.c +++ b/fs/exfat/inode.c @@ -72,8 +72,8 @@ int __exfat_write_inode(struct inode *inode, int sync) if (ei->start_clu == EXFAT_EOF_CLUSTER) on_disk_size = 0; - ep2->dentry.stream.valid_size = cpu_to_le64(on_disk_size); - ep2->dentry.stream.size = ep2->dentry.stream.valid_size; + ep2->dentry.stream.valid_size = cpu_to_le64(ei->valid_size); + ep2->dentry.stream.size = cpu_to_le64(on_disk_size); if (on_disk_size) { ep2->dentry.stream.flags = ei->flags; ep2->dentry.stream.start_clu = cpu_to_le32(ei->start_clu); @@ -276,6 +276,7 @@ static int exfat_get_block(struct inode *inode, sector_t iblock, sector_t last_block; sector_t phys = 0; loff_t pos; + size_t b_size = bh_result->b_size; mutex_lock(&sbi->s_lock); last_block = EXFAT_B_TO_BLK_ROUND_UP(i_size_read(inode), sb); @@ -303,17 +304,25 @@ static int exfat_get_block(struct inode *inode, sector_t iblock, mapped_blocks = sbi->sect_per_clus - sec_offset; max_blocks = min(mapped_blocks, max_blocks); - /* Treat newly added block / cluster */ - if (iblock < last_block) - create = 0; - - if (create || buffer_delay(bh_result)) { - pos = EXFAT_BLK_TO_B((iblock + 1), sb); + pos = EXFAT_BLK_TO_B((iblock + 1), sb); + if ((create && iblock >= last_block) || buffer_delay(bh_result)) { if (ei->i_size_ondisk < pos) ei->i_size_ondisk = pos; } + map_bh(bh_result, sb, phys); + if (buffer_delay(bh_result)) + clear_buffer_delay(bh_result); + if (create) { + sector_t valid_blks; + + valid_blks = EXFAT_B_TO_BLK_ROUND_UP(ei->valid_size, sb); + if (iblock < valid_blks && iblock + max_blocks >= valid_blks) { + max_blocks = valid_blks - iblock; + goto done; + } + err = exfat_map_new_buffer(ei, bh_result, pos); if (err) { exfat_fs_error(sb, @@ -321,11 +330,40 @@ static int exfat_get_block(struct inode *inode, sector_t iblock, pos, ei->i_size_aligned); goto unlock_ret; } - } - if (buffer_delay(bh_result)) - clear_buffer_delay(bh_result); - map_bh(bh_result, sb, phys); + if (pos - sb->s_blocksize + b_size > ei->valid_size) { + ei->valid_size = pos - sb->s_blocksize + b_size; + mark_inode_dirty(inode); + } + } else { + size_t b_size = EXFAT_BLK_TO_B(max_blocks, sb); + + pos = EXFAT_BLK_TO_B(iblock, sb); + if (pos >= ei->valid_size) { + /* Read out of valid data */ + clear_buffer_mapped(bh_result); + } else if (pos + b_size <= ei->valid_size) { + /* Normal read */ + } else if (pos + sb->s_blocksize <= ei->valid_size) { + /* Normal short read */ + max_blocks = 1; + } else { + /* Read across valid size */ + if (bh_result->b_page) { + loff_t size = ei->valid_size - pos; + loff_t off = pos & (PAGE_SIZE - 1); + + set_bh_page(bh_result, bh_result->b_page, off); + err = bh_read(bh_result, 0); + if (err < 0) + goto unlock_ret; + + zero_user_segment(bh_result->b_page, off + size, + off + sb->s_blocksize); + } + max_blocks = 1; + } + } done: bh_result->b_size = EXFAT_BLK_TO_B(max_blocks, sb); unlock_ret: @@ -340,6 +378,17 @@ static int exfat_read_folio(struct file *file, struct folio *folio) static void exfat_readahead(struct readahead_control *rac) { + struct address_space *mapping = rac->mapping; + struct inode *inode = mapping->host; + struct exfat_inode_info *ei = EXFAT_I(inode); + loff_t pos = readahead_pos(rac); + + /* Range cross valid_size, read it page by page. */ + if (ei->valid_size < i_size_read(inode) && + pos <= ei->valid_size && + ei->valid_size < pos + readahead_length(rac)) + return; + mpage_readahead(rac, exfat_get_block); } @@ -403,6 +452,22 @@ static int exfat_write_end(struct file *file, struct address_space *mapping, mark_inode_dirty(inode); } + if (err >= 0) { + if (pos + err > ei->valid_size) { + ei->valid_size = pos + err; + mark_inode_dirty(inode); + } + + /* + * valid_size is extended with sector-aligned length in + * exfat_get_block(), set to the writren length. + */ + if (ei->valid_size > i_size_read(inode)) { + ei->valid_size = i_size_read(inode); + mark_inode_dirty(inode); + } + } + return err; } @@ -410,6 +475,8 @@ static ssize_t exfat_direct_IO(struct kiocb *iocb, struct iov_iter *iter) { struct address_space *mapping = iocb->ki_filp->f_mapping; struct inode *inode = mapping->host; + struct exfat_inode_info *ei = EXFAT_I(inode); + loff_t pos = iocb->ki_pos; loff_t size = iocb->ki_pos + iov_iter_count(iter); int rw = iov_iter_rw(iter); ssize_t ret; @@ -433,8 +500,20 @@ static ssize_t exfat_direct_IO(struct kiocb *iocb, struct iov_iter *iter) * condition of exfat_get_block() and ->truncate(). */ ret = blockdev_direct_IO(iocb, inode, iter, exfat_get_block); - if (ret < 0 && (rw & WRITE)) - exfat_write_failed(mapping, size); + if (ret < 0) { + if (rw & WRITE) + exfat_write_failed(mapping, size); + + if (ret != -EIOCBQUEUED) + return ret; + } else + size = pos + ret; + + if ((rw & READ) && pos < ei->valid_size && ei->valid_size < size) { + iov_iter_revert(iter, size - ei->valid_size); + iov_iter_zero(size - ei->valid_size, iter); + } + return ret; } @@ -534,6 +613,7 @@ static int exfat_fill_inode(struct inode *inode, struct exfat_dir_entry *info) ei->start_clu = info->start_clu; ei->flags = info->flags; ei->type = info->type; + ei->valid_size = info->valid_size; ei->version = 0; ei->hint_stat.eidx = 0; diff --git a/fs/exfat/namei.c b/fs/exfat/namei.c index e0ff9d156f6f..ceaa720804b9 100644 --- a/fs/exfat/namei.c +++ b/fs/exfat/namei.c @@ -395,6 +395,7 @@ static int exfat_find_empty_entry(struct inode *inode, i_size_write(inode, size); ei->i_size_ondisk += sbi->cluster_size; ei->i_size_aligned += sbi->cluster_size; + ei->valid_size += sbi->cluster_size; ei->flags = p_dir->flags; inode->i_blocks += sbi->cluster_size >> 9; } @@ -544,6 +545,8 @@ static int exfat_add_entry(struct inode *inode, const char *path, info->size = clu_size; info->num_subdirs = EXFAT_MIN_SUBDIR; } + info->valid_size = info->size; + memset(&info->crtime, 0, sizeof(info->crtime)); memset(&info->mtime, 0, sizeof(info->mtime)); memset(&info->atime, 0, sizeof(info->atime)); @@ -645,7 +648,8 @@ static int exfat_find(struct inode *dir, struct qstr *qname, info->type = exfat_get_entry_type(ep); info->attr = le16_to_cpu(ep->dentry.file.attr); - info->size = le64_to_cpu(ep2->dentry.stream.valid_size); + info->valid_size = le64_to_cpu(ep2->dentry.stream.valid_size); + info->size = le64_to_cpu(ep2->dentry.stream.size); if ((info->type == TYPE_FILE) && (info->size == 0)) { info->flags = ALLOC_NO_FAT_CHAIN; info->start_clu = EXFAT_EOF_CLUSTER; @@ -1271,6 +1275,7 @@ static int __exfat_rename(struct inode *old_parent_inode, } i_size_write(new_inode, 0); + new_ei->valid_size = 0; new_ei->start_clu = EXFAT_EOF_CLUSTER; new_ei->flags = ALLOC_NO_FAT_CHAIN; }