From patchwork Thu Nov 2 09:58:47 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: 13443560 Received: from lindbergh.monkeyblade.net (lindbergh.monkeyblade.net [23.128.96.19]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 069B5125B6 for ; Thu, 2 Nov 2023 09:59:13 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=sony.com header.i=@sony.com header.b="cwEOH2+f" Received: from mx08-001d1705.pphosted.com (mx08-001d1705.pphosted.com [185.183.30.70]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 37DA2111 for ; Thu, 2 Nov 2023 02:59:08 -0700 (PDT) Received: from pps.filterd (m0209320.ppops.net [127.0.0.1]) by mx08-001d1705.pphosted.com (8.17.1.19/8.17.1.19) with ESMTP id 3A1NL5U7022477; Thu, 2 Nov 2023 09:58:54 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=8Bur+poIx466hhppEkxWs+MTexxHg9eBPhvJ7QNitNY=; b=cwEOH2+ff3GKuIRrTl81cydZEwNFdZmJzSSZCPOs7OnUUkbfHLs9mSdLDMTvP7mzqaqC h5OcNY0lqWOJlnI5PcAFqf7v1nZJEVuV8K4Gr+3TppWO8EnrxuSXd86BpcuSiPNLSbCU qWIeAGMLEGEb0ue1AXn/LsoCvGU5L4EY4eoKnG4eMKAE4NgYrOo5gYjqEIESMnYRaz66 li8bOsT7Txz3XMvSUBJSjafXOOnjxeWKr/Wrw/YYmqyIAQZMqC/a6/6YDE6xlNu/Ane7 Wi+58+J5wgTbO1Muqx86lbVolzVaTP6A16X89kdV0fbhpeIv0FP5kUbacsTXLnGbm8aY hw== Received: from apc01-sg2-obe.outbound.protection.outlook.com (mail-sgaapc01lp2107.outbound.protection.outlook.com [104.47.26.107]) by mx08-001d1705.pphosted.com (PPS) with ESMTPS id 3u0r6mdht9-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK); Thu, 02 Nov 2023 09:58:54 +0000 ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=Mjwwjv+g8C1g2qDoRnBaCLYnd/kBhku4phtcZXRh2msQ6JdaTC55GL6sD+iGEmVqTa5uTNgWsEpz8GjqtO8YZZKBVutd7LbrZDa4VuCcPZdApGkpudIYXaPy1hLydO1pnETf4yjE0im3rZEFCbits3YPzlQr6Bu2CWtyaIf5gBoYOA1YhjetXpej3FI/PTy6G2ywgTJIxWTkGN8MEBcPtkDIfZ3N0C8HV8fEtsEy18QgQM8LOunGvg1KxxynfVVh0lGr4J30b2cz9wsPO2O6pcVo5YpyR5jtjHczRu/thMSjGHC2yRYb7M0D8W22Jx0D9xX5+KRJEYXla+rt/8JryA== 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=8Bur+poIx466hhppEkxWs+MTexxHg9eBPhvJ7QNitNY=; b=RsFeImKlvLJxkPBFGWbwc6bo7IqlfkRRvvSrGgURqw7eTmN9SC7MZxJ1q4b0iiox8Gjua152vvysmxLPJWBi6IJv5peoiqOoBPGgzq5jFG1xg/o7QNVTNRV3RR1oO+dmHtqpWrpZOHij0kob1ebIpjRy2CnxkXHxJPHggvDcTgb3re7gkIeuMNFpJPqdOK0VMjz5JwGMK0x3pukDlu7Y7Z/utwmSN0/6V9jEbsKYPpIJRsd2qw90c67cZlrOWzN3meZFIfAxaFtPc38uwz7fOa7qht7/W/ZZavRI7KL5eiwoqEeX9oYVz+6RpE5lecAe5Tifj3xYnzXItYEfIjzQng== 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 TYZPR04MB4432.apcprd04.prod.outlook.com (2603:1096:400:28::12) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.6954.20; Thu, 2 Nov 2023 09:58:47 +0000 Received: from PUZPR04MB6316.apcprd04.prod.outlook.com ([fe80::2fd0:f20f:14a9:a95a]) by PUZPR04MB6316.apcprd04.prod.outlook.com ([fe80::2fd0:f20f:14a9:a95a%4]) with mapi id 15.20.6907.025; Thu, 2 Nov 2023 09:58:47 +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 v4 1/2] exfat: change to get file size from DataLength Thread-Topic: [PATCH v4 1/2] exfat: change to get file size from DataLength Thread-Index: AdoNcqiciL5m7DeAQVy9qFZcQUwgXw== Date: Thu, 2 Nov 2023 09:58:47 +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_|TYZPR04MB4432:EE_ x-ms-office365-filtering-correlation-id: 3321c389-6722-44e4-1298-08dbdb8a4eb1 x-ms-exchange-senderadcheck: 1 x-ms-exchange-antispam-relay: 0 x-microsoft-antispam: BCL:0; x-microsoft-antispam-message-info: lmOQw1lvBnSRSm+QwjeS25CWvuJYgNvjFdGMBM1oyoE8P65VFLzoZCBw1DKBqkz8AEUgrAiDbFqmNzW/nZ4VNUj+oGJASkr9oRhah8X+LahumaPbFfbCUR6Yd4orRExxsAEkzKtcTs+3LFURyThlLI9gFn0IZGkX9km0lrHZVBCPTuO1PSfsMotkgSIntT4kUKPKWqp9WMaDe5CpJK+EUZ65cHxObHUno/QHudQNR07G8Zd/csEdmX1RaoC7B0xw81RU+5dZRd9ybGjMJ2hPtD6RbhNfqOYOXkvjOMHbWfHElhu1Fx90iZIT6mz09l8UXxnbKaFMKpT6riHjOn2fFMtgIFUFVe5LpdJBfNG8aKfbO2INOzS3xljRFpUtItTSJmLn2JQRwlqwYwYMU6YAo/1ZYHPfw02KdECsMHnLScnX3JEtw4Ofn4Ork/whwHb7/nA+4puxI2LUzM2x1fsNnd8RDXqmWSdqwnKo1uvy+Vp0Ue07GoUeD6NIJiVtHah7lmt83KybRETNMheIJU7wOCdrDECjTlV3zWpDRqqkTl9nfXPz01pXplm/t26RSFFfZDo7NWbFnb3TSSyFVkNWoWEdNuX3a9o0imkb1OTJ74LqMiwJ/xl0iNBVw4FdQR/p 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:(13230031)(39860400002)(376002)(136003)(366004)(346002)(396003)(230922051799003)(1800799009)(451199024)(64100799003)(186009)(107886003)(71200400001)(6506007)(7696005)(83380400001)(9686003)(8936002)(4326008)(5660300002)(52536014)(41300700001)(8676002)(2906002)(478600001)(26005)(30864003)(76116006)(316002)(110136005)(66476007)(66556008)(66446008)(54906003)(64756008)(66946007)(33656002)(122000001)(82960400001)(86362001)(38100700002)(38070700009)(55016003);DIR:OUT;SFP:1102; x-ms-exchange-antispam-messagedata-chunkcount: 1 x-ms-exchange-antispam-messagedata-0: =?utf-8?q?Kcs7IwAOhAV8KzapLemKvDm/bWpE?= =?utf-8?q?/ueMgofOO82wE+cVOt3scCWue47Uyj2mzXaUsOkfnXz4E73OjmD5Itowm6ofg95AR?= =?utf-8?q?g5O9DTnejJtN0sxgDpYZVgvbqPCGs3nsVeesG2hTSFmTTpR+KIS7jbAzUC9ZKCahN?= =?utf-8?q?TE+MESDBpC3NJIoLF+wzrxQlbxTsEUud5BL0Ze8V/LFsytqV3pkdtfESi8K2ke82p?= =?utf-8?q?BdnZx9VCF5osrQapfDF48yoO2WRZcHLzQmfR4DFQchf1/3VTpuTpwjnYg2j8dx1LA?= =?utf-8?q?Qd/5doZwOVvI7qg6AYfk9oeLNlcTtFRUB4C+WiFOqsNa+g5qrcCM0vxGoZw2HTQrM?= =?utf-8?q?RGemkAC8PIgwPpYzZrY96lorWIoQMtK7OAJxnbGGBJRhUZhcoxvjNOeF8iY5ALXux?= =?utf-8?q?k/RkFnljkKjEaLTjrKNEPSPUso6dAvA4IZ3ae+uBsQRyp5UCYMk+2R1CrSdLahEBv?= =?utf-8?q?0DvjlE3g6cf/9Dr+QVvL2xi024C/4SAvDTyQ8cvtdcV0FVpVzSM7WhlBhcL9LunBU?= =?utf-8?q?8o9ek47oKMBfIWm+nRx9BsVUpmHrmAQYsHdQHerIpJ19fkl4Gd6uu/kT6mB6DoYxp?= =?utf-8?q?IMZfcGLIq6EYBo+DlNN3Wu3lnBMkjcYrzBnyuK5YPq8B3k/7GatlPbATUuUgQ/mXi?= =?utf-8?q?wHzxhM6YEjBre5Yency5v8xBAEKYY96lSGSOBHCeI2o/Ow4Vtk4z8KcgaKi8Tqswg?= =?utf-8?q?eq5PW486zSEciU21mAxCBfFdrusnWK38fNC2AZp16s67X68M77B/1rxzLLKy6xxCd?= =?utf-8?q?mxpRiQvVjuf4MEogB3WCv2c/IIW3zhzcym5gPPhE4HhC0D6b8rHgrJ1DRh51TzhIm?= =?utf-8?q?ekbVy28Il0zwNOCc5y5XEusnAAX3tI2a+lXmM3z5DrKmrgqM4YiU0s4z9eYEZLKnc?= =?utf-8?q?6Szay2KKl2pK+JYWfeqF2E5biRC2+W0GvVCZrK5x4gFxWAFE2pJ/iglUpuoQN7O8B?= =?utf-8?q?9AXwCSInrywP785MTSLdyFn24rYrtU3C1/5PVv3uKPvmMaKmNs9nAuezcwS8oi1Wu?= =?utf-8?q?PQOn26bUgmzCPL1TqWeFNgd8IWhvUClERa6LJ1M7BWNUi4dooeaTX90Bk1fatHP/5?= =?utf-8?q?/JmHOZnUwKHVJS/RRpgHBCYWP9s9QokQYcEk+mBRgRmRrhYhHJaBKgvVNJTqvfESE?= =?utf-8?q?cwbpqEZZgIa5os2AA/oZ0boJVqo3wEGzSFPLddI7JvfdmbnL31Q7OJ6Tz2G2cHEJ1?= =?utf-8?q?hv8187wwlegQL5NclAbUNWRJ1wtMqvnDZ++0amyFy5GyWzohq6uB407+S+SKg4ZQq?= =?utf-8?q?EZtTqG3MaO0IKOlYTsNcgFZLjiLDC9dSuh26F4gTDEXv/pYJsc7cNNYxAT+MRJaol?= =?utf-8?q?noysDWZamOO5DFnoli5JyVYGiRRbreEvNoo17cFAgYWj3e/xeA9mCKh/XZl9yO+uc?= =?utf-8?q?b2L25+dN+VasrRH2Bg/aYH+lMKvcuIaETSQx6zGFIlJyn+DjQ8kzKAffOtE7kYMAq?= =?utf-8?q?oK1qqwUgBTpaHhmkkHx4AnrMNJ3jr8o52EHwwRV16dWw8/7MIbG8fN6lS1M5WfRfx?= =?utf-8?q?xF58bDaP/GBD?= Precedence: bulk X-Mailing-List: linux-fsdevel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-MS-Exchange-AntiSpam-ExternalHop-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-ExternalHop-MessageData-0: SakllNWW1Nl2stoUYMiJqQY7dt/qj0sOUA2/lwd8Jxg0bm8f4f4+VH3zP6wtLx41DazRH86kklESgLxaZ+RllKnvu1AoxqyUHs+a1UpDmvAnyaFeR+p/BRTDRloMZU7ES9d0mhRWql+D6v22iPh5C8kuQatU7sdkmnWjnAgAPX2r/Fw5vAR1GYU42NC125J3cwBqfFyKPU0x/SauVGPfRQKx+i0CYfifGW2GfdtOXdcN0jBShX6NE4PTPUyhChOlm7x25KHxYYstvGMEH66W2vOBRM/gCMBxPiQtylp9xrIBxoJtsL7xS3+oQJDG+pRrHQ2nWaSxs6Rel6GBz7K7iGT3AZoUowBMuXtInKHZPIw+2HKDKuauLS0Y/fg5t8JSav6SO2IPDHD3KH4A6TS75OUotepyEmRzefnNEkCBdENYyWvi14fRXVE/R49PavJhWISn0aY4xZU7iOOduNo+zNUzJ0pNHykafJnLSDNSPen6fm1taTM4uZE3qEMg7B9BrpG1E+5WBAOdJpQyzI+JwOd8qTiQcahH/jNmYJIYtYy4G4MYkuZo5eFvvIEtrHoM6fbKsWRXHGs6JgGi5oqVtQQylLYKrfJOuZvR9vZFG9cKtXGTh/nggOdtJAu7XfZN1L3v08Mcm+SDvvU3AUlKBNUgREa/0OfbyXEI+Iq5Iy6E34G2JKk3Cqg20kxYuiFpR6J6wJhU6Z6Fpo3mGs1hZjwFeQ/ml9/xf6uLHxhPS0Vw5IAfrNka+e0Fmd9O/tAO3ClofXbM4jivZVBK37pRL0a2Umi+aS+ZDy9L9Pq4fZgxAiXTye9wMDFmHmkzQDyVnVvnGpxyY6yG/3JLAr+oMw== 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: 3321c389-6722-44e4-1298-08dbdb8a4eb1 X-MS-Exchange-CrossTenant-originalarrivaltime: 02 Nov 2023 09:58:47.4962 (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: ZHPAZZeB2CWdsANz4UoYuD2Ym5HCVt7ndoyqSHhLWAue7vPNPF7zIwI2sicKVwqjebz75kt8+U2GfQrk/f+scQ== X-MS-Exchange-Transport-CrossTenantHeadersStamped: TYZPR04MB4432 X-Proofpoint-ORIG-GUID: cLlnDUg4dTi3q0pB2FyXqJnHeq6Ie7zG X-Proofpoint-GUID: cLlnDUg4dTi3q0pB2FyXqJnHeq6Ie7zG X-Sony-Outbound-GUID: cLlnDUg4dTi3q0pB2FyXqJnHeq6Ie7zG X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.272,Aquarius:18.0.987,Hydra:6.0.619,FMLib:17.11.176.26 definitions=2023-11-01_23,2023-11-01_02,2023-05-22_02 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 | 122 +++++++++++++++++++++++++++++++++++++++++++- fs/exfat/inode.c | 96 ++++++++++++++++++++++++++++------ fs/exfat/namei.c | 6 +++ 4 files changed, 207 insertions(+), 19 deletions(-) diff --git a/fs/exfat/exfat_fs.h b/fs/exfat/exfat_fs.h index a7a2c35d74fb..e3b1f8e022df 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 bfdfafe00993..7fec9a660734 100644 --- a/fs/exfat/file.c +++ b/fs/exfat/file.c @@ -11,6 +11,7 @@ #include #include #include +#include #include "exfat_raw.h" #include "exfat_fs.h" @@ -26,6 +27,7 @@ static int exfat_cont_expand(struct inode *inode, loff_t size) return err; inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode)); + EXFAT_I(inode)->valid_size = size; mark_inode_dirty(inode); if (!IS_SYNC(inode)) @@ -146,6 +148,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 |= EXFAT_ATTR_ARCHIVE; @@ -474,15 +479,128 @@ 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 = NULL; + + 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; + + inode_lock(inode); + + valid_size = ei->valid_size; + + ret = generic_write_checks(iocb, iter); + if (ret < 0) + goto unlock; + + if (pos > valid_size) { + ret = exfat_file_zeroed_range(file, valid_size, pos); + if (ret < 0 && ret != -ENOSPC) { + exfat_err(inode->i_sb, + "write: fail to zero from %llu to %llu(%ld)", + valid_size, pos, ret); + } + if (ret < 0) + goto unlock; + } + + ret = __generic_file_write_iter(iocb, iter); + if (ret < 0) + goto unlock; + + 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; + } + + if (ret) + ret = generic_write_sync(iocb, ret); + + return 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 = filemap_splice_read, .splice_write = iter_file_splice_write, diff --git a/fs/exfat/inode.c b/fs/exfat/inode.c index e7ff58b8e68c..4a06df3ed4b7 100644 --- a/fs/exfat/inode.c +++ b/fs/exfat/inode.c @@ -75,8 +75,7 @@ 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.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); @@ -85,6 +84,8 @@ int __exfat_write_inode(struct inode *inode, int sync) ep2->dentry.stream.start_clu = EXFAT_FREE_CLUSTER; } + ep2->dentry.stream.valid_size = cpu_to_le64(ei->valid_size); + exfat_update_dir_chksum_with_entry_set(&es); return exfat_put_dentry_set(&es, sync); } @@ -306,17 +307,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, @@ -324,11 +333,35 @@ static int exfat_get_block(struct inode *inode, sector_t iblock, pos, ei->i_size_aligned); goto unlock_ret; } + } else { + size_t b_size = EXFAT_BLK_TO_B(max_blocks, sb); + + pos -= sb->s_blocksize; + 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_folio) { + loff_t size = ei->valid_size - pos; + loff_t off = pos & (PAGE_SIZE - 1); + + folio_set_bh(bh_result, bh_result->b_folio, off); + err = bh_read(bh_result, 0); + if (err < 0) + goto unlock_ret; + + folio_zero_segment(bh_result->b_folio, off + size, + off + sb->s_blocksize); + } + max_blocks = 1; + } } - - if (buffer_delay(bh_result)) - clear_buffer_delay(bh_result); - map_bh(bh_result, sb, phys); done: bh_result->b_size = EXFAT_BLK_TO_B(max_blocks, sb); unlock_ret: @@ -343,6 +376,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); } @@ -370,9 +414,7 @@ static int exfat_write_begin(struct file *file, struct address_space *mapping, int ret; *pagep = NULL; - ret = cont_write_begin(file, mapping, pos, len, pagep, fsdata, - exfat_get_block, - &EXFAT_I(mapping->host)->i_size_ondisk); + ret = block_write_begin(mapping, pos, len, pagep, exfat_get_block); if (ret < 0) exfat_write_failed(mapping, pos+len); @@ -400,6 +442,11 @@ static int exfat_write_end(struct file *file, struct address_space *mapping, if (err < len) exfat_write_failed(mapping, pos+len); + if (!(err < 0) && pos + err > ei->valid_size) { + ei->valid_size = pos + err; + mark_inode_dirty(inode); + } + if (!(err < 0) && !(ei->attr & EXFAT_ATTR_ARCHIVE)) { inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode)); ei->attr |= EXFAT_ATTR_ARCHIVE; @@ -413,6 +460,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; @@ -436,8 +485,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; } @@ -537,6 +598,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 5d737e0b639a..9c549fd11fc8 100644 --- a/fs/exfat/namei.c +++ b/fs/exfat/namei.c @@ -406,6 +406,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; } @@ -558,6 +559,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)); @@ -660,6 +663,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->size == 0) { info->flags = ALLOC_NO_FAT_CHAIN; info->start_clu = EXFAT_EOF_CLUSTER; @@ -1288,6 +1293,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; }