Received: by 2002:a25:8b91:0:0:0:0:0 with SMTP id j17csp3760885ybl; Sun, 8 Dec 2019 22:57:04 -0800 (PST) X-Google-Smtp-Source: APXvYqxgQkSQhWUSQV0VfyNoUfSwn/odC3aRhhWOMi5NJbDibAFwrheqMB9x33Nm83Y7/AFnRXZ8 X-Received: by 2002:a05:6808:350:: with SMTP id j16mr23823384oie.1.1575874624357; Sun, 08 Dec 2019 22:57:04 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1575874624; cv=none; d=google.com; s=arc-20160816; b=epl8ukndVDT/lgzO+2IIxcy75wi5pNanARvkrR2ZZrw1cd0kIA1j99bGjrKUbpyKLc YkxZyaiarooKKqEJf5zVOneh0uz/HBJehpUKGI/nAwtN8X9nusswsSGPtppjtAB36F+8 dIW5nJp6gTFmkBRcwros0Slzt0o7BtP9Sqlw9yN+vdVtpsjIHSDl72bs85b8f2FuV/Sp nRFKClT37/JOFd8kSXmd1e7XhRup6t16JHk/Q38yyTrIeki1+ucwA0kaDjEwsR2mquBs pNqSeQmtRCi936Al67uKY9q1Bibcp+IU2N/PIS73N8DeBM0kV1m9/ceTazMFXXtKmNFo LTUQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:references:dlp-filter:cms-type :in-reply-to:message-id:date:subject:cc:to:from:dkim-signature :dkim-filter; bh=9bEzzOhUMxf7gBYWRripcTQol6c+Tk1hsbR7EopfI/c=; b=KQskmzvWf006uw4Cz6IUDemi/lvls9VCCvjmaK4/zTMNVJW99YBbb3uq7HLY2NFqxL 4plJqHUYcFfJ32/eGJTYhnBp2pRL2avrYZLgBTl9lO9FUZ8DJULJXhk/oaETEdHYgEsA +fmx+/I9leU/rw8gKBvHwe9PqPtD3cmBnTwQjHDPiNFyNW1Ka9vGhKaqx31C/Ls8jfbw 0wWRS4SP/hwKSg+0b+rYc9lhZQSMAbD6HXY2LTmhsYmEDJVlW4iBxG3ccPSGibAbQTGf b3xqcVF2ZOUl2NBpDUI5ik1JEIxUImDKRDr4RDR13CgRKJp7aLNM8OoiO5Bbhj1Kh9pu 1SIg== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@samsung.com header.s=mail20170921 header.b=pB3u5Ev5; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=samsung.com Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id u16si11565539otq.92.2019.12.08.22.56.53; Sun, 08 Dec 2019 22:57:04 -0800 (PST) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=pass header.i=@samsung.com header.s=mail20170921 header.b=pB3u5Ev5; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=samsung.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727174AbfLIGzH (ORCPT + 99 others); Mon, 9 Dec 2019 01:55:07 -0500 Received: from mailout4.samsung.com ([203.254.224.34]:19624 "EHLO mailout4.samsung.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726270AbfLIGzF (ORCPT ); Mon, 9 Dec 2019 01:55:05 -0500 Received: from epcas1p2.samsung.com (unknown [182.195.41.46]) by mailout4.samsung.com (KnoxPortal) with ESMTP id 20191209065502epoutp047a89b2986b6b92f3289e8a69817550f6~eoYK_YGgV1963619636epoutp04Q for ; Mon, 9 Dec 2019 06:55:02 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 mailout4.samsung.com 20191209065502epoutp047a89b2986b6b92f3289e8a69817550f6~eoYK_YGgV1963619636epoutp04Q DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=samsung.com; s=mail20170921; t=1575874502; bh=9bEzzOhUMxf7gBYWRripcTQol6c+Tk1hsbR7EopfI/c=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=pB3u5Ev5/WxmlOFfGjOwIBnSFV3GLJfAYVxA2pKf0Vv/9jy9vw/1Vt5sTore6CUba awCGvDVWhsxD1T4QjyHKbM0duo21UpzMkvA0O24JFYkCfcUoIWD+sb5XcM44vaXT4D catkOuY5DSKOWwGN7esfp1YhoZsQW4KhaskEsYHE= Received: from epsnrtp4.localdomain (unknown [182.195.42.165]) by epcas1p2.samsung.com (KnoxPortal) with ESMTP id 20191209065502epcas1p20624a1f72286aad1a5748318df1218d1~eoYKnFucu2292522925epcas1p2O; Mon, 9 Dec 2019 06:55:02 +0000 (GMT) Received: from epsmges1p3.samsung.com (unknown [182.195.40.160]) by epsnrtp4.localdomain (Postfix) with ESMTP id 47WYps0G8kzMqYkY; Mon, 9 Dec 2019 06:55:01 +0000 (GMT) Received: from epcas1p4.samsung.com ( [182.195.41.48]) by epsmges1p3.samsung.com (Symantec Messaging Gateway) with SMTP id 80.CA.52419.4CFEDED5; Mon, 9 Dec 2019 15:55:01 +0900 (KST) Received: from epsmtrp1.samsung.com (unknown [182.195.40.13]) by epcas1p2.samsung.com (KnoxPortal) with ESMTPA id 20191209065500epcas1p2f236643696f32677b809c6bb12ce0bef~eoYJTVXxo2292522925epcas1p2L; Mon, 9 Dec 2019 06:55:00 +0000 (GMT) Received: from epsmgms1p2new.samsung.com (unknown [182.195.42.42]) by epsmtrp1.samsung.com (KnoxPortal) with ESMTP id 20191209065500epsmtrp16d468fd8075bc8400c64f1e63ce37b45~eoYJSjZWg2418724187epsmtrp1I; Mon, 9 Dec 2019 06:55:00 +0000 (GMT) X-AuditID: b6c32a37-a10cb9c00001ccc3-e2-5dedefc4bc30 Received: from epsmtip1.samsung.com ( [182.195.34.30]) by epsmgms1p2new.samsung.com (Symantec Messaging Gateway) with SMTP id 99.E9.06569.4CFEDED5; Mon, 9 Dec 2019 15:55:00 +0900 (KST) Received: from localhost.localdomain (unknown [10.88.103.87]) by epsmtip1.samsung.com (KnoxPortal) with ESMTPA id 20191209065500epsmtip170340fdb163be4c3992fc7a4b180d652~eoYJFjDDV1822918229epsmtip1P; Mon, 9 Dec 2019 06:55:00 +0000 (GMT) From: Namjae Jeon To: linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org Cc: gregkh@linuxfoundation.org, valdis.kletnieks@vt.edu, hch@lst.de, sj1557.seo@samsung.com, Namjae Jeon Subject: [PATCH v6 06/13] exfat: add exfat entry operations Date: Mon, 9 Dec 2019 01:51:41 -0500 Message-Id: <20191209065149.2230-7-namjae.jeon@samsung.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20191209065149.2230-1-namjae.jeon@samsung.com> X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFnrOKsWRmVeSWpSXmKPExsWy7bCmge7R929jDbZ6WzQvXs9msXL1USaL PXtPslhc3jWHzeLH9HqLLf+OsFpcev+BxYHdY//cNeweu282sHn0bVnF6PF5k5zHoe1v2AJY o3JsMlITU1KLFFLzkvNTMvPSbZW8g+Od403NDAx1DS0tzJUU8hJzU22VXHwCdN0yc4COUFIo S8wpBQoFJBYXK+nb2RTll5akKmTkF5fYKqUWpOQUGBoU6BUn5haX5qXrJefnWhkaGBiZAlUm 5GS0vlUouBtd8XDjb8YGxjfuXYycHBICJhKbj65n6WLk4hAS2MEoMbV5A5TziVFize5vzBDO N0aJjz+2sMC0vNx+jR0isZdR4uauHexwLZO/NgA5HBxsAtoSf7aIgjSICNhLbJ59AGwss0AL o8SC0z+YQRLCAtYST/9dZgOxWQRUJTYcn8wEYvMCxWcuXcMKsU1eYvWGA2D1nAI2Em96DjKB DJIQmMMmcapjBSNEkYvE6WdLoRqEJV4d38IOYUtJvOxvAztIQqBa4uN+ZohwB6PEi++2ELax xM31G1hBSpgFNCXW79KHCCtK7Pw9F2w6swCfxLuvPawQU3glOtqEIEpUJfouHWaCsKUluto/ QC31kNjx8C0bJEj6GSUOTXvBOoFRbhbChgWMjKsYxVILinPTU4sNC4yRI2wTIziRaZnvYNxw zucQowAHoxIPr4LV21gh1sSy4srcQ4wSHMxKIrxLJr6KFeJNSaysSi3Kjy8qzUktPsRoCgzI icxSosn5wCSbVxJvaGpkbGxsYWJmbmZqrCTOy/HjYqyQQHpiSWp2ampBahFMHxMHp1QDo1bq 8ur3atlB5XLvnjwoFfnFr5SkVvzg/yyPW7/8Pq6v+vck8gMn12Srlu8VjZ/9rshXsKtNPbvn 8YOpm4vj2nOLZrkeqePy91gtK5tl79aVnxN77uifr0/vqM7T0mKb69p7YGVU+1+/jw/0LMLm OFjvqbu8NFc2inf1b9WSrPCNnaV1OvOslViKMxINtZiLihMBopKcBHoDAAA= X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFlrILMWRmVeSWpSXmKPExsWy7bCSnO6R929jDR5NVrVoXryezWLl6qNM Fnv2nmSxuLxrDpvFj+n1Flv+HWG1uPT+A4sDu8f+uWvYPXbfbGDz6NuyitHj8yY5j0Pb37AF sEZx2aSk5mSWpRbp2yVwZbS+VSi4G13xcONvxgbGN+5djJwcEgImEi+3X2PvYuTiEBLYzSix 8cdrZoiEtMSxE2eAbA4gW1ji8OFiiJoPjBJbO3azgcTZBLQl/mwRBSkXEXCU6N11mAWkhlmg i1HiUdM3sDnCAtYST/9dZgOxWQRUJTYcn8wEYvMCxWcuXcMKsUteYvWGA2D1nAI2Em96DoLV CAHVXH25lHECI98CRoZVjJKpBcW56bnFhgVGeanlesWJucWleel6yfm5mxjBAaeltYPxxIn4 Q4wCHIxKPLwKVm9jhVgTy4orcw8xSnAwK4nwLpn4KlaINyWxsiq1KD++qDQntfgQozQHi5I4 r3z+sUghgfTEktTs1NSC1CKYLBMHp1QDY4eN05z3LRyC+n+cYu73Pau6ZRFgUtBcMXfH2k/F 29NDdpk2/JJXm1i6L/JAFbdoOsern6p3VsVETnqnNqf93rfjbR8DZzq23naN4rhy/7iQ9fS4 QyGTvLeZFDuq7r5gdCsgsffzPPGGBenXtJZOv5/d21D+mtd13YHkq4ILVzZF6Wyeyb93uRJL cUaioRZzUXEiAIUet/00AgAA X-CMS-MailID: 20191209065500epcas1p2f236643696f32677b809c6bb12ce0bef X-Msg-Generator: CA Content-Type: text/plain; charset="utf-8" X-Sendblock-Type: SVC_REQ_APPROVE CMS-TYPE: 101P DLP-Filter: Pass X-CFilter-Loop: Reflected X-CMS-RootMailID: 20191209065500epcas1p2f236643696f32677b809c6bb12ce0bef References: <20191209065149.2230-1-namjae.jeon@samsung.com> Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org This adds the implementation of exfat entry operations for exfat. Signed-off-by: Namjae Jeon Signed-off-by: Sungjong Seo --- fs/exfat/fatent.c | 472 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 472 insertions(+) create mode 100644 fs/exfat/fatent.c diff --git a/fs/exfat/fatent.c b/fs/exfat/fatent.c new file mode 100644 index 000000000000..0598aa2d04c1 --- /dev/null +++ b/fs/exfat/fatent.c @@ -0,0 +1,472 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. + */ + +#include +#include +#include + +#include "exfat_raw.h" +#include "exfat_fs.h" + +static int __exfat_ent_get(struct super_block *sb, unsigned int loc, + unsigned int *content) +{ + unsigned int off, _content; + sector_t sec; + struct exfat_sb_info *sbi = EXFAT_SB(sb); + struct buffer_head *bh; + + sec = sbi->FAT1_start_sector + (loc >> (sb->s_blocksize_bits-2)); + off = (loc << 2) & (sb->s_blocksize - 1); + + bh = sb_bread(sb, sec); + if (!bh) + return -EIO; + + _content = le32_to_cpu(*(__le32 *)(&bh->b_data[off])); + + /* remap reserved clusters to simplify code */ + if (_content >= CLUSTER_32(0xFFFFFFF8)) + _content = EOF_CLUSTER; + + *content = CLUSTER_32(_content); + brelse(bh); + return 0; +} + +int exfat_ent_set(struct super_block *sb, unsigned int loc, + unsigned int content) +{ + unsigned int off; + sector_t sec; + __le32 *fat_entry; + struct exfat_sb_info *sbi = EXFAT_SB(sb); + struct buffer_head *bh; + + sec = sbi->FAT1_start_sector + (loc >> (sb->s_blocksize_bits-2)); + off = (loc << 2) & (sb->s_blocksize - 1); + + bh = sb_bread(sb, sec); + if (!bh) + return -EIO; + + fat_entry = (__le32 *)&(bh->b_data[off]); + *fat_entry = cpu_to_le32(content); + exfat_update_bh(sb, bh, sb->s_flags & SB_SYNCHRONOUS); + exfat_mirror_bh(sb, sec, bh); + brelse(bh); + return 0; +} + +static inline bool is_reserved_cluster(unsigned int clus) +{ + if (clus == FREE_CLUSTER || clus == EOF_CLUSTER || clus == BAD_CLUSTER) + return true; + return false; +} + +static inline bool is_valid_cluster(struct exfat_sb_info *sbi, + unsigned int clus) +{ + if (clus < BASE_CLUSTER || sbi->num_clusters <= clus) + return false; + return true; +} + +int exfat_ent_get(struct super_block *sb, unsigned int loc, + unsigned int *content) +{ + struct exfat_sb_info *sbi = EXFAT_SB(sb); + int err; + + if (!is_valid_cluster(sbi, loc)) { + exfat_fs_error(sb, "invalid access to FAT (entry 0x%08x)", + loc); + return -EIO; + } + + err = __exfat_ent_get(sb, loc, content); + if (err) { + exfat_fs_error(sb, + "failed to access to FAT (entry 0x%08x, err:%d)", + loc, err); + return err; + } + + if (!is_reserved_cluster(*content) && + !is_valid_cluster(sbi, *content)) { + exfat_fs_error(sb, + "invalid access to FAT (entry 0x%08x) bogus content (0x%08x)", + loc, *content); + return -EIO; + } + + if (*content == FREE_CLUSTER) { + exfat_fs_error(sb, + "invalid access to FAT free cluster (entry 0x%08x)", + loc); + return -EIO; + } + + if (*content == BAD_CLUSTER) { + exfat_fs_error(sb, + "invalid access to FAT bad cluster (entry 0x%08x)", + loc); + return -EIO; + } + return 0; +} + +int exfat_chain_cont_cluster(struct super_block *sb, unsigned int chain, + unsigned int len) +{ + if (!len) + return 0; + + while (len > 1) { + if (exfat_ent_set(sb, chain, chain + 1)) + return -EIO; + chain++; + len--; + } + + if (exfat_ent_set(sb, chain, EOF_CLUSTER)) + return -EIO; + return 0; +} + +int exfat_free_cluster(struct inode *inode, struct exfat_chain *p_chain) +{ + unsigned int num_clusters = 0; + unsigned int clu; + struct super_block *sb = inode->i_sb; + struct exfat_sb_info *sbi = EXFAT_SB(sb); + + /* invalid cluster number */ + if (p_chain->dir == FREE_CLUSTER || p_chain->dir == EOF_CLUSTER) + return 0; + + /* no cluster to truncate */ + if (p_chain->size == 0) + return 0; + + /* check cluster validation */ + if (p_chain->dir < 2 && p_chain->dir >= sbi->num_clusters) { + exfat_msg(sb, KERN_ERR, "invalid start cluster (%u)", + p_chain->dir); + return -EIO; + } + + set_bit(EXFAT_SB_DIRTY, &sbi->s_state); + clu = p_chain->dir; + + if (p_chain->flags == ALLOC_NO_FAT_CHAIN) { + do { + exfat_clear_bitmap(inode, clu-2); + clu++; + + num_clusters++; + } while (num_clusters < p_chain->size); + } else { + do { + exfat_clear_bitmap(inode, (clu - BASE_CLUSTER)); + + if (exfat_get_next_cluster(sb, &clu)) + goto dec_used_clus; + + num_clusters++; + } while (clu != EOF_CLUSTER); + } + +dec_used_clus: + sbi->used_clusters -= num_clusters; + return 0; +} + +int exfat_find_last_cluster(struct super_block *sb, struct exfat_chain *p_chain, + unsigned int *ret_clu) +{ + unsigned int clu, next; + unsigned int count = 0; + + next = p_chain->dir; + if (p_chain->flags == ALLOC_NO_FAT_CHAIN) { + *ret_clu = next + p_chain->size - 1; + return 0; + } + + do { + count++; + clu = next; + if (exfat_ent_get(sb, clu, &next)) + return -EIO; + } while (next != EOF_CLUSTER); + + if (p_chain->size != count) { + exfat_fs_error(sb, + "bogus directory size (clus : ondisk(%d) != counted(%d))", + p_chain->size, count); + return -EIO; + } + + *ret_clu = clu; + return 0; +} + +static inline int exfat_sync_bhs(struct buffer_head **bhs, int nr_bhs) +{ + int i, err = 0; + + for (i = 0; i < nr_bhs; i++) + write_dirty_buffer(bhs[i], 0); + + for (i = 0; i < nr_bhs; i++) { + wait_on_buffer(bhs[i]); + if (!err && !buffer_uptodate(bhs[i])) + err = -EIO; + } + return err; +} + +int exfat_zeroed_cluster(struct inode *dir, unsigned int clu) +{ + struct super_block *sb = dir->i_sb; + struct exfat_sb_info *sbi = EXFAT_SB(sb); + struct buffer_head *bhs[MAX_BUF_PER_PAGE]; + int nr_bhs = MAX_BUF_PER_PAGE; + sector_t blknr, last_blknr; + int err, i, n; + + blknr = exfat_cluster_to_sector(sbi, clu); + last_blknr = blknr + sbi->sect_per_clus; + + if (last_blknr > sbi->num_sectors && sbi->num_sectors > 0) { + exfat_fs_error_ratelimit(sb, + "%s: out of range(sect:%llu len:%u)", + __func__, (unsigned long long)blknr, + sbi->sect_per_clus); + return -EIO; + } + + /* Zeroing the unused blocks on this cluster */ + n = 0; + while (blknr < last_blknr) { + bhs[n] = sb_getblk(sb, blknr); + if (!bhs[n]) { + err = -ENOMEM; + goto release_bhs; + } + memset(bhs[n]->b_data, 0, sb->s_blocksize); + exfat_update_bh(sb, bhs[n], 0); + + n++; + blknr++; + + if (n == nr_bhs) { + if (IS_DIRSYNC(dir)) { + err = exfat_sync_bhs(bhs, n); + if (err) + goto release_bhs; + } + + for (i = 0; i < n; i++) + brelse(bhs[i]); + n = 0; + } + } + + if (IS_DIRSYNC(dir)) { + err = exfat_sync_bhs(bhs, n); + if (err) + goto release_bhs; + } + + for (i = 0; i < n; i++) + brelse(bhs[i]); + + return 0; + +release_bhs: + exfat_msg(sb, KERN_ERR, "failed zeroed sect %llu\n", + (unsigned long long)blknr); + for (i = 0; i < n; i++) + bforget(bhs[i]); + return err; +} + +int exfat_alloc_cluster(struct inode *inode, unsigned int num_alloc, + struct exfat_chain *p_chain) +{ + int ret = -ENOSPC; + unsigned int num_clusters = 0, total_cnt; + unsigned int hint_clu, new_clu, last_clu = EOF_CLUSTER; + struct super_block *sb = inode->i_sb; + struct exfat_sb_info *sbi = EXFAT_SB(sb); + + total_cnt = sbi->num_clusters - BASE_CLUSTER; + + if (unlikely(total_cnt < sbi->used_clusters)) { + exfat_fs_error_ratelimit(sb, + "%s: invalid used clusters(t:%u,u:%u)\n", + __func__, total_cnt, sbi->used_clusters); + return -EIO; + } + + if (num_alloc > total_cnt - sbi->used_clusters) + return -ENOSPC; + + hint_clu = p_chain->dir; + /* find new cluster */ + if (hint_clu == EOF_CLUSTER) { + if (sbi->clu_srch_ptr < BASE_CLUSTER) { + exfat_msg(sb, KERN_ERR, + "sbi->clu_srch_ptr is invalid (%u)\n", + sbi->clu_srch_ptr); + sbi->clu_srch_ptr = BASE_CLUSTER; + } + + hint_clu = exfat_test_bitmap(sb, + sbi->clu_srch_ptr - BASE_CLUSTER); + if (hint_clu == EOF_CLUSTER) + return -ENOSPC; + } + + /* check cluster validation */ + if (hint_clu < BASE_CLUSTER && hint_clu >= sbi->num_clusters) { + exfat_msg(sb, KERN_ERR, "hint_cluster is invalid (%u)\n", + hint_clu); + hint_clu = BASE_CLUSTER; + if (p_chain->flags == ALLOC_NO_FAT_CHAIN) { + if (exfat_chain_cont_cluster(sb, p_chain->dir, + num_clusters)) + return -EIO; + p_chain->flags = ALLOC_FAT_CHAIN; + } + } + + set_bit(EXFAT_SB_DIRTY, &sbi->s_state); + + p_chain->dir = EOF_CLUSTER; + + while ((new_clu = exfat_test_bitmap(sb, + hint_clu - BASE_CLUSTER)) != EOF_CLUSTER) { + if (new_clu != hint_clu && + p_chain->flags == ALLOC_NO_FAT_CHAIN) { + if (exfat_chain_cont_cluster(sb, p_chain->dir, + num_clusters)) { + ret = -EIO; + goto free_cluster; + } + p_chain->flags = ALLOC_FAT_CHAIN; + } + + /* update allocation bitmap */ + if (exfat_set_bitmap(inode, new_clu - BASE_CLUSTER)) { + ret = -EIO; + goto free_cluster; + } + + num_clusters++; + + /* update FAT table */ + if (p_chain->flags == ALLOC_FAT_CHAIN) { + if (exfat_ent_set(sb, new_clu, EOF_CLUSTER)) { + ret = -EIO; + goto free_cluster; + } + } + + if (p_chain->dir == EOF_CLUSTER) { + p_chain->dir = new_clu; + } else if (p_chain->flags == ALLOC_FAT_CHAIN) { + if (exfat_ent_set(sb, last_clu, new_clu)) { + ret = -EIO; + goto free_cluster; + } + } + last_clu = new_clu; + + if (--num_alloc == 0) { + sbi->clu_srch_ptr = hint_clu; + sbi->used_clusters += num_clusters; + + p_chain->size += num_clusters; + return 0; + } + + hint_clu = new_clu + 1; + if (hint_clu >= sbi->num_clusters) { + hint_clu = BASE_CLUSTER; + + if (p_chain->flags == ALLOC_NO_FAT_CHAIN) { + if (exfat_chain_cont_cluster(sb, p_chain->dir, + num_clusters)) { + ret = -EIO; + goto free_cluster; + } + p_chain->flags = ALLOC_FAT_CHAIN; + } + } + } +free_cluster: + if (num_clusters) + exfat_free_cluster(inode, p_chain); + return ret; +} + +int exfat_count_num_clusters(struct super_block *sb, + struct exfat_chain *p_chain, unsigned int *ret_count) +{ + unsigned int i, count; + unsigned int clu; + struct exfat_sb_info *sbi = EXFAT_SB(sb); + + if (!p_chain->dir || p_chain->dir == EOF_CLUSTER) { + *ret_count = 0; + return 0; + } + + if (p_chain->flags == ALLOC_NO_FAT_CHAIN) { + *ret_count = p_chain->size; + return 0; + } + + clu = p_chain->dir; + count = 0; + for (i = BASE_CLUSTER; i < sbi->num_clusters; i++) { + count++; + if (exfat_ent_get(sb, clu, &clu)) + return -EIO; + if (clu == EOF_CLUSTER) + break; + } + + *ret_count = count; + return 0; +} + +int exfat_mirror_bh(struct super_block *sb, sector_t sec, + struct buffer_head *bh) +{ + struct buffer_head *c_bh; + struct exfat_sb_info *sbi = EXFAT_SB(sb); + sector_t sec2; + int err = 0; + + if (sbi->FAT2_start_sector != sbi->FAT1_start_sector) { + sec2 = sec - sbi->FAT1_start_sector + sbi->FAT2_start_sector; + c_bh = sb_getblk(sb, sec2); + if (!c_bh) + return -ENOMEM; + memcpy(c_bh->b_data, bh->b_data, sb->s_blocksize); + set_buffer_uptodate(c_bh); + mark_buffer_dirty(c_bh); + if (sb->s_flags & SB_SYNCHRONOUS) + err = sync_dirty_buffer(c_bh); + brelse(c_bh); + } + + return err; +} -- 2.17.1