From patchwork Thu Nov 30 03:09:53 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: 13474035 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=sony.com header.i=@sony.com header.b="V5d+LKNh" Received: from mx07-001d1705.pphosted.com (mx07-001d1705.pphosted.com [185.132.183.11]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id AA1061A6 for ; Wed, 29 Nov 2023 19:10:10 -0800 (PST) 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 3AU0GcFE005456; Thu, 30 Nov 2023 03:09:59 GMT DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sony.com; h=from : to : cc : subject : date : message-id : references : in-reply-to : mime-version : content-type : content-transfer-encoding; s=S1; bh=SQ/rEYoYWlyYFK7TJAUb9Rk37h/frgHh+7BnOaieQ/4=; b=V5d+LKNhw8zSNTRQWrWGyzSNXBhWDynls9T2o49YRKbJFS7o5OjKF8d/e57xs/P3CC4z fKFxkgMWR3PzvJ/XEbDSEfNFVqTs+MAaWCUnd6+2UdbKXt1K9wognxx9FwZyUKPXPo4P Ghk2BCycPgf2/fm+LvlYyTmLUYNNt7H6BUnzhUyuDtPCvOg22ahnWYrI13VCBrR1hHXI vO/aDXaokhwy7o+GxumszhcQHaR7Y8Nb5v81I2wiXtMiFUv+F4FL2b1GU8qpPJZuBYh0 iL30VwVNilzHlkd32YB+O91F1T9oEznKyLEi9kOZwcXBTBBhaHleBQNdxts8Lwf50AVp JQ== Received: from apc01-sg2-obe.outbound.protection.outlook.com (mail-sgaapc01lp2110.outbound.protection.outlook.com [104.47.26.110]) by mx08-001d1705.pphosted.com (PPS) with ESMTPS id 3uk74k51c1-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK); Thu, 30 Nov 2023 03:09:58 +0000 ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=nG0YBuzzpgQ4+rA4ZmGfNPgtU8F9BuMW97eq1YAm2eTZsjUFr1nLxmG14BQgQ+byWFyPi/axjdljSSZjUqWR4F2hAtZ/vpuN6E3GahWmQTNNG7OyDaroAHfTap+q3AVrV5f2v4BW+g7tRC/l+kt9F+7YrfVE9NKLYel8jur6Bxtox7kBB5Q6fxPhQmnPdS7EV7eaal7lRJnV99xPMolAX5MhY8Zru4jVnWqHHGWmRqriP6jspRZWanIPAf7iD2e6SawvInIVR3hZmMz4rBEHnetpNc/1vnLkeciAM3juleFZkhNnFvq/Q0ILAPAPOCYhn2WuM23Z90VWHPSpchkReA== 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=SQ/rEYoYWlyYFK7TJAUb9Rk37h/frgHh+7BnOaieQ/4=; b=BYHVK9JjF+n9xnLU08OTsHYn4zJlO4VdTQrGqTED2cKrBm367QYhlVXypA/WxCMz8Dy8rqMCPEpYOtaAY+tfw45YmSaqewX99zwBpOyrJuIgfOlubSUeIdX368tn5aFQAIR1mLlXGDWlE/lsTLe9Ul2XhqruAhep3Cn3Zrs8V+iRL81JnB4NmdxUOU7sQ8FtbU51v7m39UtkPRVfQP8XmPom2lJ6kZNYHnz6hIOrxIBwdumJIDwkEgXrGyT8P86PhwAHXgST8j5ZrRGASvX7bCjOsL0djpyEp02wM0GBKhPfA6ZdmijUXLIXqggBFoghL36G3K4CfFK0S3ZDD35TvQ== 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 SI6PR04MB7955.apcprd04.prod.outlook.com (2603:1096:4:249::10) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.7046.24; Thu, 30 Nov 2023 03:09:53 +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.7025.022; Thu, 30 Nov 2023 03:09:53 +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" , "cpgs@samsung.com" Subject: [PATCH v5 1/2] exfat: change to get file size from DataLength Thread-Topic: [PATCH v5 1/2] exfat: change to get file size from DataLength Thread-Index: AQHaIzqwOA3CBbJcCUGm1LmnmZM7PA== Date: Thu, 30 Nov 2023 03:09:53 +0000 Message-ID: References: In-Reply-To: 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_|SI6PR04MB7955:EE_ x-ms-office365-filtering-correlation-id: edf66c76-a9bd-489f-adff-08dbf151d2ea x-ms-exchange-senderadcheck: 1 x-ms-exchange-antispam-relay: 0 x-microsoft-antispam: BCL:0; x-microsoft-antispam-message-info: O/t1eFBV2zCoOImL+gRqLvnv9mJh4aAgcPE0Vt0XE/hRC0Kg7g0ezo9Z5WJc6b0Dv/d60dUkOdrDtONvzLn8s+TuzAe6uU06hwNqrjgw8wD7WQvZMrtKDAcS1+EWXynJFR/k2xyAxgewv8Mz4qRxcs1gdBosMR8ycUB7ZNYJnRGK8/tyB0Ch8vvL8t2eRIzSAJX03YYOlMQVMvUZ71MD/ZdKXLZ0gpNfFjCtXKCVa2vB4mS9QTHV1gE6EPI5p5KskD7QweHvbMiOlC0JGYcSuM4yZlWWY2jaRz2ShR1N6Uid5peGc9lYJ7QRrE6S8M6ic2KQUgk4/1uLGz7lCino4tn3mAeBqWpFc46agfejiv82Sr1dnQQQLKa+RVnMJ16y+VrZyXncRH/vMMgaD2XYrXPCsUgjLB+KDGK3nTM2dcwk1P5lRbnqsMJP9vEQuJOIryGo/N64Bh25z8qqhSuavm/5FRVdpDXLNQjpCMUIx22qFHC5Mj5DH/c7J3N+zNF/X5Nrg+4Vark76cFhjRfFKVrKrCKCZHYkm1a9pPuyDw6qturmLD7zHW3u7fr3K+kLUjoczVncMjit/a2SR9O98t6uXBcG3wtSJe1kS/CiW5AUaGXPuIbTyVcdYf0IlIQ+r572Dt8S92ZNP7EQFeK8C8NnexIS1ZqUKIgAPWiZkaM= 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)(366004)(346002)(396003)(136003)(230922051799003)(451199024)(1800799012)(186009)(64100799003)(5660300002)(30864003)(478600001)(26005)(9686003)(33656002)(7696005)(6506007)(41300700001)(38070700009)(38100700002)(122000001)(83380400001)(86362001)(71200400001)(82960400001)(2906002)(202311291699003)(66946007)(52536014)(110136005)(76116006)(55016003)(66446008)(316002)(64756008)(66476007)(54906003)(66556008)(4326008)(8676002)(8936002);DIR:OUT;SFP:1102; x-ms-exchange-antispam-messagedata-chunkcount: 1 x-ms-exchange-antispam-messagedata-0: =?utf-8?q?VhYdfVK4zUWZ28ASw7raTrjIWasc?= =?utf-8?q?PG221NeDjN2njEcA1tEsq7uYuSHL/6B8I5SlzCN62hfAzEz9MRgSfp+g/kZdFKdE7?= =?utf-8?q?aCzxBOssZvzRy1jW3sq7opvZ8DU9/0grEeYYxGWQvoQDyl9wdeEvyPhHbZHkw4hAy?= =?utf-8?q?/XLRk6H3d/JNULSwifpPsh9SJmONnf87Xnr4hNYYEL6Z3hK/PH2LBcfnb1VYAlPwW?= =?utf-8?q?T2ZwMmFYNKnFzyBefHGgSiEISDWKsT0WIQA0UkPgoKAL/ykrGtTidYdnAcqH/PvZC?= =?utf-8?q?C7LIn7KHCYCb1XxlC8/StDB1KDz7U48FdeOfxzUgmif/oIsnMmKj3awGfvZuwSsev?= =?utf-8?q?bJDfo/+gok9uPbSln4noPsjW3oRaYj6CSLrfy7hHvy2qfJChuFwYlD6AXxAhFjYPf?= =?utf-8?q?uIJPz+nI4gJUiDaAXa/1oOBJSn+MlNbZTWQ28zzabvmB6lxpaWhVPIMzBoRkk21pm?= =?utf-8?q?PIJzVqaFm7M4wxKHsycmoZN5GbPnoFemccuvR3dxSq9xhpr8bNqjNW++xGDaRhCS6?= =?utf-8?q?tfot2NtTe5gVFq6jExnXm7/PsDUL43sPOIzRhe5uxs1bb9YdmlQQ63XGNFU36urtN?= =?utf-8?q?EJvyoWi5+OwezI8YLYWjAP89UG/yKf3SJOrx3fkFMf6QWIlDQU9J0LbWocHkrCSAG?= =?utf-8?q?CNAOXZzq2aqOpMXLy2CKZTBufuEFqvW7hD5oumWkC0mCg7dij8UbdiXvdiNbd5UEB?= =?utf-8?q?aYpOlGR2XX0aSYWmWso4gTtHsDQucLPx2lMrlgbc5+Ad0E/A10BzIop/9wY2BF/V6?= =?utf-8?q?qnOSIZeNFK6jHQCo3cEL73Gk8K1wUMXaPw2PocyDoLIrcur2IyLi05TsF0NUgwNjj?= =?utf-8?q?lT+kOZhHznzW5sjTke4pcEsCydmG6GRrtAcegDsWiDt6tJVhrHmD0MGpOU7vCA7f2?= =?utf-8?q?XYjex+7qiMYQUpzuQydhLQ1QyVdmuc99nU3hAxNGEaAsog00eBNc0vbnqOyKpmyKp?= =?utf-8?q?mAy9QxU9JctQhmoBXUlc36v2qa0H6pwslQocnidBPfF95EgsBZ1fSFom3xVIML+W0?= =?utf-8?q?bCNCAJpVi5+dHntaXLlJQjvXtfE7G1Bc3m775FJXW56aKEHVH1odQxYAwbyQB7/Zh?= =?utf-8?q?2Y/XYL423Qw3WVN7wlFwFdgYB4z4TM4rINOSi78U0uBLXXh1GkqQ1Vp3PgbxeS19H?= =?utf-8?q?GMcllO7MGosq35H7/U8cLBkGWmfA9bFYJo9Vs40G5wrDwG8/wZYNg6fsjJqmZpMQ0?= =?utf-8?q?YTw7OU3uruoaNgIwbrQN9bOiLl8u9/MxJDiWYqRAc0K6OZBh2tUGkOtlqwXlVxvSH?= =?utf-8?q?KHT0PWgB2jogEPeKgzvgJ08gaz+fhSwb887K3M2BmafzAhTLXQP0/iKEp0u3f5jLA?= =?utf-8?q?QnGGuG4+O7F3eIVWfaOGWWFDomMDcpGvZ4AYvGPU/sWKsro4pJ7RS+al7dH4j6OuS?= =?utf-8?q?GL7DB6Zs8i2q148UvotG8qdnkK1hLso8cIm+ki6LSol1PVCyK3gMlMYnjmTZCHxhD?= =?utf-8?q?3Uf0BSH4TA44MWjWKFHBKRgp8ZVMIIDWvGB7ytqkUKXElpOiAc30fi60yGpKI7MDn?= =?utf-8?q?psVKHhn8BBG9?= 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: r8dh1MH2bHV3I6xiHcIZWF4ittBXscT30XXLG3GziYP0tR2XV1vKjiZy/XZ+2rKDWe0GRR7kzl11MWpz/hTx7roCSWVHHMSm1RGBiJoHkgOyQr6nzEl/1/4eSYnFj+1QByutBGZpELcgHfLHPtQTZWDXbjJp8o0GyzKsnb7Vy0vDQrKGWwt72U/Pfmw5eM7mjVaJQNMXc2smCBoeSz/wETPy++DESm1XiTnWVE20h6LfqGNnq9yYJbWWBfsAo6OOtoc1EJlX6I8+trZGsyKeSgEnCy4unKbQ56/dhuakyT7vRaA+WnUB5Ed0369B0ps6oaS21sUDGQkC/wOOEo+DTUqcZpnWrLs4jJkuFKDX5SiD7mTsxyIWOecmHdlDN9idT8FOMRUajrxI191Ouz/pz4r+RgfYBgrWqyW4VIV0TONANelonmj2A4K0iYEB4shti0BB8/1C+4UhWbJiVvPLbPXfoKPZ/C+RCtAh5KMngkeg5TiRN7b6+qmm+CqWtv33z3JpYVmJpLDtmrlocB9TsXp10fvQnh0Fuew/5vHaYU0z4spIvHpC5oDt/anRDc3xduMy5XY20LmKa3dZ2Aaa2gY262pNC6mkLV+o6V9/EX+3vj1CmY0h1fi4RT5r45xpsSZad1MWqBWQ6cy1JRoDRJXPa6LvTn5+qDQHoUg+WLmixNZZyKGMdCX848w2pDD6NabWe0FAQkwDyo8YutjmR7dXeEytcxHoEfZzJLMAiK7q8jVw/xp3hGOhuJRdb0p+Fm20AD1+8rsK5fctOl4yJzI0TNvFkG81ZiI4IYUoKRLMsJ5n/mKoJxG5fUmwUhB2GRdwCy17qRtFG1KiTybsGQ== 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: edf66c76-a9bd-489f-adff-08dbf151d2ea X-MS-Exchange-CrossTenant-originalarrivaltime: 30 Nov 2023 03:09:53.4178 (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: XO29hGc9mJQO9luO0boYxk5S3iug1q673CtUVJfhDObAhFaKJMxfo7ZBU1bIT27UfKx2vBYL841lvqnipM/t6g== X-MS-Exchange-Transport-CrossTenantHeadersStamped: SI6PR04MB7955 X-Proofpoint-GUID: SWxRuEcT9U3fKsHSHDHOlIphY7rXD87b X-Proofpoint-ORIG-GUID: SWxRuEcT9U3fKsHSHDHOlIphY7rXD87b X-Sony-Outbound-GUID: SWxRuEcT9U3fKsHSHDHOlIphY7rXD87b X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.272,Aquarius:18.0.997,Hydra:6.0.619,FMLib:17.11.176.26 definitions=2023-11-29_21,2023-11-29_01,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 | 124 ++++++++++++++++++++++++++++++++++++++------ fs/exfat/namei.c | 6 +++ 4 files changed, 235 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..154f39a03a69 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) + pos = valid_size; + + if (iocb_is_dsync(iocb) && iocb->ki_pos > pos) { + ssize_t err = vfs_fsync_range(file, pos, iocb->ki_pos - 1, + iocb->ki_flags & IOCB_SYNC); + if (err < 0) + return err; + } + + 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..a7bb234c8bb1 100644 --- a/fs/exfat/inode.c +++ b/fs/exfat/inode.c @@ -75,8 +75,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); @@ -278,6 +278,7 @@ static int exfat_get_block(struct inode *inode, sector_t iblock, unsigned int cluster, sec_offset; sector_t last_block; sector_t phys = 0; + sector_t valid_blks; loff_t pos; mutex_lock(&sbi->s_lock); @@ -306,17 +307,32 @@ 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) { + valid_blks = EXFAT_B_TO_BLK_ROUND_UP(ei->valid_size, sb); + + if (iblock + max_blocks < valid_blks) { + /* The range has been written, map it */ + goto done; + } else if (iblock < valid_blks) { + /* + * The range has been partially written, + * map the written part. + */ + max_blocks = valid_blks - iblock; + goto done; + } + + /* The area has not been written, map and mark as new. */ err = exfat_map_new_buffer(ei, bh_result, pos); if (err) { exfat_fs_error(sb, @@ -324,11 +340,55 @@ static int exfat_get_block(struct inode *inode, sector_t iblock, pos, ei->i_size_aligned); goto unlock_ret; } + } else { + valid_blks = EXFAT_B_TO_BLK(ei->valid_size, sb); + + if (iblock + max_blocks < valid_blks) { + /* The range has been written, map it */ + goto done; + } else if (iblock < valid_blks) { + /* + * The area has been partially written, + * map the written part. + */ + max_blocks = valid_blks - iblock; + goto done; + } else if (iblock == valid_blks && + (ei->valid_size & (sb->s_blocksize - 1))) { + /* + * The block has been partially written, + * zero the unwritten part and map the block. + */ + loff_t size, off; + + max_blocks = 1; + + /* + * For direct read, the unwritten part will be zeroed in + * exfat_direct_IO() + */ + if (!bh_result->b_folio) + goto done; + + pos -= sb->s_blocksize; + size = ei->valid_size - pos; + 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); + } else { + /* + * The range has not been written, clear the mapped flag + * to only zero the cache and do not read from disk. + */ + clear_buffer_mapped(bh_result); + } } - - 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 +403,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 +441,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 +469,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 +487,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 +512,21 @@ 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; + + /* zero the unwritten part in the partially written block */ + 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 +626,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; }