This adds the latest Samsung exfat driver to fs/exfat. This is an
implementation of the Microsoft exFAT specification. Previous versions
of this shipped with millions of Android phones, an a random previous
snaphot has been merged in drivers/staging/.
Compared to the sdfat driver shipped on the phones the following changes
have been made:
- the support for vfat has been removed as that is already supported
by fs/fat
- driver has been renamed to exfat
- the code has been refactored and clean up to fully integrate into
the upstream Linux version and follow the Linux coding style
- metadata operations like create, lookup and readdir have been further
optimized
- various major and minor bugs have been fixed
We plan to treat this version as the future upstream for the code base
once merged, and all new features and bug fixes will go upstream first.
v2:
- Check the bitmap count up to the total clusters.
- Rename proper goto labels in seveal place.
- Change time mode type with enumeration.
- Directly return error instead of goto at first error check.
- Combine seq_printfs calls into a single one.
Namjae Jeon (13):
exfat: add in-memory and on-disk structures and headers
exfat: add super block operations
exfat: add inode operations
exfat: add directory operations
exfat: add file operations
exfat: add exfat entry operations
exfat: add bitmap operations
exfat: add exfat cache
exfat: add misc operations
exfat: add nls operations
exfat: add Kconfig and Makefile
exfat: add exfat in fs/Kconfig and fs/Makefile
MAINTAINERS: add exfat filesystem
MAINTAINERS | 7 +
fs/Kconfig | 3 +-
fs/Makefile | 1 +
fs/exfat/Kconfig | 21 +
fs/exfat/Makefile | 8 +
fs/exfat/balloc.c | 271 ++++++++
fs/exfat/cache.c | 325 +++++++++
fs/exfat/dir.c | 1338 +++++++++++++++++++++++++++++++++++++
fs/exfat/exfat_fs.h | 534 +++++++++++++++
fs/exfat/exfat_raw.h | 190 ++++++
fs/exfat/fatent.c | 475 ++++++++++++++
fs/exfat/file.c | 346 ++++++++++
fs/exfat/inode.c | 691 +++++++++++++++++++
fs/exfat/misc.c | 247 +++++++
fs/exfat/namei.c | 1492 ++++++++++++++++++++++++++++++++++++++++++
fs/exfat/nls.c | 817 +++++++++++++++++++++++
fs/exfat/super.c | 752 +++++++++++++++++++++
17 files changed, 7517 insertions(+), 1 deletion(-)
create mode 100644 fs/exfat/Kconfig
create mode 100644 fs/exfat/Makefile
create mode 100644 fs/exfat/balloc.c
create mode 100644 fs/exfat/cache.c
create mode 100644 fs/exfat/dir.c
create mode 100644 fs/exfat/exfat_fs.h
create mode 100644 fs/exfat/exfat_raw.h
create mode 100644 fs/exfat/fatent.c
create mode 100644 fs/exfat/file.c
create mode 100644 fs/exfat/inode.c
create mode 100644 fs/exfat/misc.c
create mode 100644 fs/exfat/namei.c
create mode 100644 fs/exfat/nls.c
create mode 100644 fs/exfat/super.c
--
2.17.1
This adds the implementation of exfat cache.
Signed-off-by: Namjae Jeon <[email protected]>
Signed-off-by: Sungjong Seo <[email protected]>
---
fs/exfat/cache.c | 325 +++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 325 insertions(+)
create mode 100644 fs/exfat/cache.c
diff --git a/fs/exfat/cache.c b/fs/exfat/cache.c
new file mode 100644
index 000000000000..b87438360103
--- /dev/null
+++ b/fs/exfat/cache.c
@@ -0,0 +1,325 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * linux/fs/fat/cache.c
+ *
+ * Written 1992,1993 by Werner Almesberger
+ *
+ * Mar 1999. AV. Changed cache, so that it uses the starting cluster instead
+ * of inode number.
+ * May 1999. AV. Fixed the bogosity with FAT32 (read "FAT28"). Fscking lusers.
+ * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd.
+ */
+
+#include <linux/slab.h>
+#include <asm/unaligned.h>
+#include <linux/buffer_head.h>
+
+#include "exfat_raw.h"
+#include "exfat_fs.h"
+
+#define EXFAT_CACHE_VALID 0
+#define EXFAT_MAX_CACHE 16
+
+struct exfat_cache {
+ struct list_head cache_list;
+ unsigned int nr_contig; /* number of contiguous clusters */
+ unsigned int fcluster; /* cluster number in the file. */
+ unsigned int dcluster; /* cluster number on disk. */
+};
+
+struct exfat_cache_id {
+ unsigned int id;
+ unsigned int nr_contig;
+ unsigned int fcluster;
+ unsigned int dcluster;
+};
+
+static struct kmem_cache *exfat_cachep;
+
+static void exfat_cache_init_once(void *c)
+{
+ struct exfat_cache *cache = (struct exfat_cache *)c;
+
+ INIT_LIST_HEAD(&cache->cache_list);
+}
+
+int exfat_cache_init(void)
+{
+ exfat_cachep = kmem_cache_create("exfat_cache",
+ sizeof(struct exfat_cache),
+ 0, SLAB_RECLAIM_ACCOUNT|SLAB_MEM_SPREAD,
+ exfat_cache_init_once);
+ if (!exfat_cachep)
+ return -ENOMEM;
+ return 0;
+}
+
+void exfat_cache_shutdown(void)
+{
+ if (!exfat_cachep)
+ return;
+ kmem_cache_destroy(exfat_cachep);
+}
+
+void exfat_cache_init_inode(struct inode *inode)
+{
+ struct exfat_inode_info *ei = EXFAT_I(inode);
+
+ spin_lock_init(&ei->cache_lru_lock);
+ ei->nr_caches = 0;
+ ei->cache_valid_id = EXFAT_CACHE_VALID + 1;
+ INIT_LIST_HEAD(&ei->cache_lru);
+}
+
+static inline struct exfat_cache *exfat_cache_alloc(void)
+{
+ return kmem_cache_alloc(exfat_cachep, GFP_NOFS);
+}
+
+static inline void exfat_cache_free(struct exfat_cache *cache)
+{
+ WARN_ON(!list_empty(&cache->cache_list));
+ kmem_cache_free(exfat_cachep, cache);
+}
+
+static inline void exfat_cache_update_lru(struct inode *inode,
+ struct exfat_cache *cache)
+{
+ struct exfat_inode_info *ei = EXFAT_I(inode);
+
+ if (ei->cache_lru.next != &cache->cache_list)
+ list_move(&cache->cache_list, &ei->cache_lru);
+}
+
+static unsigned int exfat_cache_lookup(struct inode *inode,
+ unsigned int fclus, struct exfat_cache_id *cid,
+ unsigned int *cached_fclus, unsigned int *cached_dclus)
+{
+ struct exfat_inode_info *ei = EXFAT_I(inode);
+ static struct exfat_cache nohit = { .fcluster = 0, };
+ struct exfat_cache *hit = &nohit, *p;
+ unsigned int offset = EOF_CLUSTER;
+
+ spin_lock(&ei->cache_lru_lock);
+ list_for_each_entry(p, &ei->cache_lru, cache_list) {
+ /* Find the cache of "fclus" or nearest cache. */
+ if (p->fcluster <= fclus && hit->fcluster < p->fcluster) {
+ hit = p;
+ if (hit->fcluster + hit->nr_contig < fclus) {
+ offset = hit->nr_contig;
+ } else {
+ offset = fclus - hit->fcluster;
+ break;
+ }
+ }
+ }
+ if (hit != &nohit) {
+ exfat_cache_update_lru(inode, hit);
+
+ cid->id = ei->cache_valid_id;
+ cid->nr_contig = hit->nr_contig;
+ cid->fcluster = hit->fcluster;
+ cid->dcluster = hit->dcluster;
+ *cached_fclus = cid->fcluster + offset;
+ *cached_dclus = cid->dcluster + offset;
+ }
+ spin_unlock(&ei->cache_lru_lock);
+
+ return offset;
+}
+
+static struct exfat_cache *exfat_cache_merge(struct inode *inode,
+ struct exfat_cache_id *new)
+{
+ struct exfat_inode_info *ei = EXFAT_I(inode);
+ struct exfat_cache *p;
+
+ list_for_each_entry(p, &ei->cache_lru, cache_list) {
+ /* Find the same part as "new" in cluster-chain. */
+ if (p->fcluster == new->fcluster) {
+ if (new->nr_contig > p->nr_contig)
+ p->nr_contig = new->nr_contig;
+ return p;
+ }
+ }
+ return NULL;
+}
+
+static void exfat_cache_add(struct inode *inode,
+ struct exfat_cache_id *new)
+{
+ struct exfat_inode_info *ei = EXFAT_I(inode);
+ struct exfat_cache *cache, *tmp;
+
+ if (new->fcluster == EOF_CLUSTER) /* dummy cache */
+ return;
+
+ spin_lock(&ei->cache_lru_lock);
+ if (new->id != EXFAT_CACHE_VALID &&
+ new->id != ei->cache_valid_id)
+ goto out; /* this cache was invalidated */
+
+ cache = exfat_cache_merge(inode, new);
+ if (cache == NULL) {
+ if (ei->nr_caches < EXFAT_MAX_CACHE) {
+ ei->nr_caches++;
+ spin_unlock(&ei->cache_lru_lock);
+
+ tmp = exfat_cache_alloc();
+ if (!tmp) {
+ spin_lock(&ei->cache_lru_lock);
+ ei->nr_caches--;
+ spin_unlock(&ei->cache_lru_lock);
+ return;
+ }
+
+ spin_lock(&ei->cache_lru_lock);
+ cache = exfat_cache_merge(inode, new);
+ if (cache != NULL) {
+ ei->nr_caches--;
+ exfat_cache_free(tmp);
+ goto out_update_lru;
+ }
+ cache = tmp;
+ } else {
+ struct list_head *p = ei->cache_lru.prev;
+
+ cache = list_entry(p,
+ struct exfat_cache, cache_list);
+ }
+ cache->fcluster = new->fcluster;
+ cache->dcluster = new->dcluster;
+ cache->nr_contig = new->nr_contig;
+ }
+out_update_lru:
+ exfat_cache_update_lru(inode, cache);
+out:
+ spin_unlock(&ei->cache_lru_lock);
+}
+
+/*
+ * Cache invalidation occurs rarely, thus the LRU chain is not updated. It
+ * fixes itself after a while.
+ */
+static void __exfat_cache_inval_inode(struct inode *inode)
+{
+ struct exfat_inode_info *ei = EXFAT_I(inode);
+ struct exfat_cache *cache;
+
+ while (!list_empty(&ei->cache_lru)) {
+ cache = list_entry(ei->cache_lru.next,
+ struct exfat_cache, cache_list);
+ list_del_init(&cache->cache_list);
+ ei->nr_caches--;
+ exfat_cache_free(cache);
+ }
+ /* Update. The copy of caches before this id is discarded. */
+ ei->cache_valid_id++;
+ if (ei->cache_valid_id == EXFAT_CACHE_VALID)
+ ei->cache_valid_id++;
+}
+
+void exfat_cache_inval_inode(struct inode *inode)
+{
+ struct exfat_inode_info *ei = EXFAT_I(inode);
+
+ spin_lock(&ei->cache_lru_lock);
+ __exfat_cache_inval_inode(inode);
+ spin_unlock(&ei->cache_lru_lock);
+}
+
+static inline int cache_contiguous(struct exfat_cache_id *cid,
+ unsigned int dclus)
+{
+ cid->nr_contig++;
+ return cid->dcluster + cid->nr_contig == dclus;
+}
+
+static inline void cache_init(struct exfat_cache_id *cid,
+ unsigned int fclus, unsigned int dclus)
+{
+ cid->id = EXFAT_CACHE_VALID;
+ cid->fcluster = fclus;
+ cid->dcluster = dclus;
+ cid->nr_contig = 0;
+}
+
+int exfat_get_cluster(struct inode *inode, unsigned int cluster,
+ unsigned int *fclus, unsigned int *dclus,
+ unsigned int *last_dclus, int allow_eof)
+{
+ struct super_block *sb = inode->i_sb;
+ struct exfat_sb_info *sbi = EXFAT_SB(sb);
+ unsigned int limit = sbi->num_clusters;
+ struct exfat_inode_info *ei = EXFAT_I(inode);
+ struct exfat_cache_id cid;
+ unsigned int content;
+
+ if (ei->start_clu == FREE_CLUSTER) {
+ exfat_fs_error(sb,
+ "invalid access to exfat cache (entry 0x%08x)",
+ ei->start_clu);
+ return -EIO;
+ }
+
+ *fclus = 0;
+ *dclus = ei->start_clu;
+ *last_dclus = *dclus;
+
+ /*
+ * Don`t use exfat_cache if zero offset or non-cluster allocation
+ */
+ if (cluster == 0 || *dclus == EOF_CLUSTER)
+ return 0;
+
+ cache_init(&cid, EOF_CLUSTER, EOF_CLUSTER);
+
+ if (exfat_cache_lookup(inode, cluster, &cid, fclus, dclus) ==
+ EOF_CLUSTER) {
+ /*
+ * dummy, always not contiguous
+ * This is reinitialized by cache_init(), later.
+ */
+ WARN_ON(cid.id != EXFAT_CACHE_VALID ||
+ cid.fcluster != EOF_CLUSTER ||
+ cid.dcluster != EOF_CLUSTER ||
+ cid.nr_contig != 0);
+ }
+
+ if (*fclus == cluster)
+ return 0;
+
+ while (*fclus < cluster) {
+ /* prevent the infinite loop of cluster chain */
+ if (*fclus > limit) {
+ exfat_fs_error(sb,
+ "detected the cluster chain loop (i_pos %u)",
+ (*fclus));
+ return -EIO;
+ }
+
+ if (exfat_ent_get(sb, *dclus, &content))
+ return -EIO;
+
+ *last_dclus = *dclus;
+ *dclus = content;
+ (*fclus)++;
+
+ if (content == EOF_CLUSTER) {
+ if (!allow_eof) {
+ exfat_fs_error(sb,
+ "invalid cluster chain (i_pos %u, last_clus 0x%08x is EOF)",
+ *fclus, (*last_dclus));
+ return -EIO;
+ }
+
+ break;
+ }
+
+ if (!cache_contiguous(&cid, *dclus))
+ cache_init(&cid, *fclus, *dclus);
+ }
+
+ exfat_cache_add(inode, &cid);
+ return 0;
+}
--
2.17.1
This adds the implementation of exfat entry operations for exfat.
Signed-off-by: Namjae Jeon <[email protected]>
Signed-off-by: Sungjong Seo <[email protected]>
---
fs/exfat/fatent.c | 475 ++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 475 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..006c513ae5c0
--- /dev/null
+++ b/fs/exfat/fatent.c
@@ -0,0 +1,475 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd.
+ */
+
+#include <linux/slab.h>
+#include <asm/unaligned.h>
+#include <linux/buffer_head.h>
+
+#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;
+ }
+
+ WRITE_ONCE(sbi->s_dirt, true);
+ clu = p_chain->dir;
+
+ if (p_chain->flags == 0x03) {
+ 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 out;
+
+ num_clusters++;
+ } while (clu != EOF_CLUSTER);
+ }
+
+out:
+ 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 == 0x03) {
+ *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 error;
+ }
+ 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 error;
+ }
+
+ for (i = 0; i < n; i++)
+ brelse(bhs[i]);
+ n = 0;
+ }
+ }
+
+ if (IS_DIRSYNC(dir)) {
+ err = exfat_sync_bhs(bhs, n);
+ if (err)
+ goto error;
+ }
+
+ for (i = 0; i < n; i++)
+ brelse(bhs[i]);
+
+ return 0;
+
+error:
+ 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 == 0x03) {
+ if (exfat_chain_cont_cluster(sb, p_chain->dir,
+ num_clusters))
+ return -EIO;
+ p_chain->flags = 0x01;
+ }
+ }
+
+ WRITE_ONCE(sbi->s_dirt, true);
+
+ 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 == 0x03) {
+ if (exfat_chain_cont_cluster(sb, p_chain->dir,
+ num_clusters)) {
+ ret = -EIO;
+ goto error;
+ }
+ p_chain->flags = 0x01;
+ }
+
+ /* update allocation bitmap */
+ if (exfat_set_bitmap(inode, new_clu - BASE_CLUSTER)) {
+ ret = -EIO;
+ goto error;
+ }
+
+ num_clusters++;
+
+ /* update FAT table */
+ if (p_chain->flags == 0x01) {
+ if (exfat_ent_set(sb, new_clu, EOF_CLUSTER)) {
+ ret = -EIO;
+ goto error;
+ }
+ }
+
+ if (p_chain->dir == EOF_CLUSTER) {
+ p_chain->dir = new_clu;
+ } else if (p_chain->flags == 0x01) {
+ if (exfat_ent_set(sb, last_clu, new_clu)) {
+ ret = -EIO;
+ goto error;
+ }
+ }
+ 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 == 0x03) {
+ if (exfat_chain_cont_cluster(sb, p_chain->dir,
+ num_clusters)) {
+ ret = -EIO;
+ goto error;
+ }
+ p_chain->flags = 0x01;
+ }
+ }
+ }
+error:
+ 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 == 0x03) {
+ *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) {
+ err = -ENOMEM;
+ goto out;
+ }
+ 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);
+ }
+out:
+ return err;
+}
--
2.17.1
This adds the implementation of superblock operations for exfat.
Signed-off-by: Namjae Jeon <[email protected]>
Signed-off-by: Sungjong Seo <[email protected]>
---
fs/exfat/super.c | 752 +++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 752 insertions(+)
create mode 100644 fs/exfat/super.c
diff --git a/fs/exfat/super.c b/fs/exfat/super.c
new file mode 100644
index 000000000000..f7f9bf2cb40d
--- /dev/null
+++ b/fs/exfat/super.c
@@ -0,0 +1,752 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd.
+ */
+
+#include <linux/fs_context.h>
+#include <linux/fs_parser.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/time.h>
+#include <linux/mount.h>
+#include <linux/cred.h>
+#include <linux/statfs.h>
+#include <linux/seq_file.h>
+#include <linux/blkdev.h>
+#include <linux/fs_struct.h>
+#include <linux/iversion.h>
+#include <linux/nls.h>
+#include <linux/buffer_head.h>
+
+#include "exfat_raw.h"
+#include "exfat_fs.h"
+
+static char exfat_default_iocharset[] = CONFIG_EXFAT_FS_DEFAULT_IOCHARSET;
+static const char exfat_iocharset_with_utf8[] = "iso8859-1";
+static struct kmem_cache *exfat_inode_cachep;
+
+static void exfat_free_iocharset(struct exfat_sb_info *sbi)
+{
+ if (sbi->options.iocharset != exfat_default_iocharset)
+ kfree(sbi->options.iocharset);
+}
+
+static void exfat_put_super(struct super_block *sb)
+{
+ struct exfat_sb_info *sbi = EXFAT_SB(sb);
+
+ mutex_lock(&sbi->s_lock);
+ if (READ_ONCE(sbi->s_dirt)) {
+ WRITE_ONCE(sbi->s_dirt, true);
+ sync_blockdev(sb->s_bdev);
+ }
+ exfat_set_vol_flags(sb, VOL_CLEAN);
+ exfat_free_upcase_table(sb);
+ exfat_free_bitmap(sb);
+ mutex_unlock(&sbi->s_lock);
+
+ if (sbi->nls_io) {
+ unload_nls(sbi->nls_io);
+ sbi->nls_io = NULL;
+ }
+ exfat_free_iocharset(sbi);
+ sb->s_fs_info = NULL;
+ kfree(sbi);
+}
+
+static int exfat_sync_fs(struct super_block *sb, int wait)
+{
+ struct exfat_sb_info *sbi = EXFAT_SB(sb);
+ int err = 0;
+
+ /* If there are some dirty buffers in the bdev inode */
+ mutex_lock(&sbi->s_lock);
+ if (READ_ONCE(sbi->s_dirt)) {
+ WRITE_ONCE(sbi->s_dirt, true);
+ sync_blockdev(sb->s_bdev);
+ if (exfat_set_vol_flags(sb, VOL_CLEAN))
+ err = -EIO;
+ }
+ mutex_unlock(&sbi->s_lock);
+
+ return err;
+}
+
+static int exfat_statfs(struct dentry *dentry, struct kstatfs *buf)
+{
+ struct super_block *sb = dentry->d_sb;
+ struct exfat_sb_info *sbi = EXFAT_SB(sb);
+ unsigned long long id = huge_encode_dev(sb->s_bdev->bd_dev);
+
+ if (sbi->used_clusters == ~0u) {
+ mutex_lock(&sbi->s_lock);
+ if (exfat_count_used_clusters(sb, &sbi->used_clusters)) {
+ mutex_unlock(&sbi->s_lock);
+ return -EIO;
+ }
+ mutex_unlock(&sbi->s_lock);
+ }
+
+ buf->f_type = sb->s_magic;
+ buf->f_bsize = sbi->cluster_size;
+ buf->f_blocks = sbi->num_clusters - 2; /* clu 0 & 1 */
+ buf->f_bfree = buf->f_blocks - sbi->used_clusters;
+ buf->f_bavail = buf->f_bfree;
+ buf->f_fsid.val[0] = (unsigned int)id;
+ buf->f_fsid.val[1] = (unsigned int)(id >> 32);
+ buf->f_namelen = 260;
+
+ return 0;
+}
+
+static int __exfat_set_vol_flags(struct super_block *sb,
+ unsigned short new_flag, int always_sync)
+{
+ struct exfat_sb_info *sbi = EXFAT_SB(sb);
+ struct pbr64 *bpb;
+ int sync = 0;
+
+ /* flags are not changed */
+ if (sbi->vol_flag == new_flag)
+ return 0;
+
+ sbi->vol_flag = new_flag;
+
+ /* skip updating volume dirty flag,
+ * if this volume has been mounted with read-only
+ */
+ if (sb_rdonly(sb))
+ return 0;
+
+ if (!sbi->pbr_bh) {
+ sbi->pbr_bh = sb_bread(sb, 0);
+ if (!sbi->pbr_bh) {
+ exfat_msg(sb, KERN_ERR, "failed to read boot sector");
+ return -ENOMEM;
+ }
+ }
+
+ bpb = (struct pbr64 *)sbi->pbr_bh->b_data;
+ bpb->bsx.vol_flags = cpu_to_le16(new_flag);
+
+ if (always_sync)
+ sync = 1;
+ else if ((new_flag == VOL_DIRTY) && (!buffer_dirty(sbi->pbr_bh)))
+ sync = 1;
+ else
+ sync = 0;
+
+ set_buffer_uptodate(sbi->pbr_bh);
+ mark_buffer_dirty(sbi->pbr_bh);
+
+ if (sync)
+ sync_dirty_buffer(sbi->pbr_bh);
+ return 0;
+}
+
+int exfat_set_vol_flags(struct super_block *sb, unsigned short new_flag)
+{
+ return __exfat_set_vol_flags(sb, new_flag, 0);
+}
+
+static int exfat_show_options(struct seq_file *m, struct dentry *root)
+{
+ struct super_block *sb = root->d_sb;
+ struct exfat_sb_info *sbi = EXFAT_SB(sb);
+ struct exfat_mount_options *opts = &sbi->options;
+
+ /* Show partition info */
+ if (!uid_eq(opts->fs_uid, GLOBAL_ROOT_UID))
+ seq_printf(m, ",uid=%u",
+ from_kuid_munged(&init_user_ns, opts->fs_uid));
+ if (!gid_eq(opts->fs_gid, GLOBAL_ROOT_GID))
+ seq_printf(m, ",gid=%u",
+ from_kgid_munged(&init_user_ns, opts->fs_gid));
+ seq_printf(m, ",fmask=%04o,dmask=%04o", opts->fs_fmask, opts->fs_dmask);
+ if (opts->allow_utime)
+ seq_printf(m, ",allow_utime=%04o", opts->allow_utime);
+ if (sbi->nls_io)
+ seq_printf(m, ",iocharset=%s", sbi->nls_io->charset);
+ if (opts->utf8)
+ seq_puts(m, ",utf8");
+ seq_printf(m, ",case_sensitive=%u", opts->case_sensitive);
+ if (opts->tz_utc)
+ seq_puts(m, ",tz=UTC");
+ seq_printf(m, ",bps=%ld", sb->s_blocksize);
+ if (opts->errors == EXFAT_ERRORS_CONT)
+ seq_puts(m, ",errors=continue");
+ else if (opts->errors == EXFAT_ERRORS_PANIC)
+ seq_puts(m, ",errors=panic");
+ else
+ seq_puts(m, ",errors=remount-ro");
+ if (opts->discard)
+ seq_puts(m, ",discard");
+
+ return 0;
+}
+
+static struct inode *exfat_alloc_inode(struct super_block *sb)
+{
+ struct exfat_inode_info *ei;
+
+ ei = kmem_cache_alloc(exfat_inode_cachep, GFP_NOFS);
+ if (!ei)
+ return NULL;
+
+ init_rwsem(&ei->truncate_lock);
+ return &ei->vfs_inode;
+}
+
+static void exfat_destroy_inode(struct inode *inode)
+{
+ kmem_cache_free(exfat_inode_cachep, EXFAT_I(inode));
+}
+
+static const struct super_operations exfat_sops = {
+ .alloc_inode = exfat_alloc_inode,
+ .destroy_inode = exfat_destroy_inode,
+ .write_inode = exfat_write_inode,
+ .evict_inode = exfat_evict_inode,
+ .put_super = exfat_put_super,
+ .sync_fs = exfat_sync_fs,
+ .statfs = exfat_statfs,
+ .show_options = exfat_show_options,
+};
+
+enum {
+ Opt_uid,
+ Opt_gid,
+ Opt_umask,
+ Opt_dmask,
+ Opt_fmask,
+ Opt_allow_utime,
+ Opt_charset,
+ Opt_utf8,
+ Opt_case_sensitive,
+ Opt_tz,
+ Opt_errors,
+ Opt_discard,
+};
+
+static const struct fs_parameter_spec exfat_param_specs[] = {
+ fsparam_u32("uid", Opt_uid),
+ fsparam_u32("gid", Opt_gid),
+ fsparam_u32oct("umask", Opt_umask),
+ fsparam_u32oct("dmask", Opt_dmask),
+ fsparam_u32oct("fmask", Opt_fmask),
+ fsparam_u32oct("allow_utime", Opt_allow_utime),
+ fsparam_string("iocharset", Opt_charset),
+ fsparam_flag("utf8", Opt_utf8),
+ fsparam_flag("case_sensitive", Opt_case_sensitive),
+ fsparam_string("tz", Opt_tz),
+ fsparam_enum("errors", Opt_errors),
+ fsparam_flag("discard", Opt_discard),
+ {}
+};
+
+static const struct fs_parameter_enum exfat_param_enums[] = {
+ { Opt_errors, "continue", EXFAT_ERRORS_CONT },
+ { Opt_errors, "panic", EXFAT_ERRORS_PANIC },
+ { Opt_errors, "remount-ro", EXFAT_ERRORS_RO },
+ {}
+};
+
+static const struct fs_parameter_description exfat_parameters = {
+ .name = "exfat",
+ .specs = exfat_param_specs,
+ .enums = exfat_param_enums,
+};
+
+static int exfat_parse_param(struct fs_context *fc, struct fs_parameter *param)
+{
+ struct exfat_sb_info *sbi = fc->s_fs_info;
+ struct exfat_mount_options *opts = &sbi->options;
+ struct fs_parse_result result;
+ int opt;
+
+ opt = fs_parse(fc, &exfat_parameters, param, &result);
+ if (opt < 0)
+ return opt;
+
+ switch (opt) {
+ case Opt_uid:
+ opts->fs_uid = make_kuid(current_user_ns(), result.uint_32);
+ break;
+ case Opt_gid:
+ opts->fs_gid = make_kgid(current_user_ns(), result.uint_32);
+ break;
+ case Opt_umask:
+ opts->fs_fmask = result.uint_32;
+ opts->fs_dmask = result.uint_32;
+ break;
+ case Opt_dmask:
+ opts->fs_dmask = result.uint_32;
+ break;
+ case Opt_fmask:
+ opts->fs_fmask = result.uint_32;
+ break;
+ case Opt_allow_utime:
+ opts->allow_utime = result.uint_32 & 0022;
+ break;
+ case Opt_charset:
+ exfat_free_iocharset(sbi);
+ opts->iocharset = kstrdup(param->string, GFP_KERNEL);
+ if (!opts->iocharset)
+ return -ENOMEM;
+ break;
+ case Opt_case_sensitive:
+ opts->case_sensitive = 1;
+ break;
+ case Opt_utf8:
+ opts->utf8 = 1;
+ break;
+ case Opt_tz:
+ if (!strcmp(param->string, "UTC"))
+ opts->tz_utc = 1;
+ break;
+ case Opt_errors:
+ opts->errors = result.uint_32;
+ break;
+ case Opt_discard:
+ opts->discard = 1;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void exfat_hash_init(struct super_block *sb)
+{
+ struct exfat_sb_info *sbi = EXFAT_SB(sb);
+ int i;
+
+ spin_lock_init(&sbi->inode_hash_lock);
+ for (i = 0; i < EXFAT_HASH_SIZE; i++)
+ INIT_HLIST_HEAD(&sbi->inode_hashtable[i]);
+}
+
+static int exfat_read_root(struct inode *inode)
+{
+ struct super_block *sb = inode->i_sb;
+ struct exfat_sb_info *sbi = EXFAT_SB(sb);
+ struct exfat_inode_info *ei = EXFAT_I(inode);
+ struct exfat_chain cdir;
+ int num_subdirs, num_clu = 0;
+
+ exfat_chain_set(&ei->dir, sbi->root_dir, 0, 0x01);
+ ei->entry = -1;
+ ei->start_clu = sbi->root_dir;
+ ei->flags = 0x01;
+ ei->type = TYPE_DIR;
+ ei->version = 0;
+ ei->rwoffset = 0;
+ ei->hint_bmap.off = EOF_CLUSTER;
+ ei->hint_stat.eidx = 0;
+ ei->hint_stat.clu = sbi->root_dir;
+ ei->hint_femp.eidx = EXFAT_HINT_NONE;
+
+ exfat_chain_set(&cdir, sbi->root_dir, 0, 0x01);
+ if (exfat_count_num_clusters(sb, &cdir, &num_clu))
+ return -EIO;
+ i_size_write(inode, num_clu << sbi->cluster_size_bits);
+
+ num_subdirs = exfat_count_dir_entries(sb, &cdir);
+ if (num_subdirs < 0)
+ return -EIO;
+ set_nlink(inode, num_subdirs + EXFAT_MIN_SUBDIR);
+
+ inode->i_uid = sbi->options.fs_uid;
+ inode->i_gid = sbi->options.fs_gid;
+ inode_inc_iversion(inode);
+ inode->i_generation = 0;
+ inode->i_mode = exfat_make_mode(sbi, ATTR_SUBDIR, 0777);
+ inode->i_op = &exfat_dir_inode_operations;
+ inode->i_fop = &exfat_dir_operations;
+
+ inode->i_blocks = ((i_size_read(inode) + (sbi->cluster_size - 1))
+ & ~(sbi->cluster_size - 1)) >> inode->i_blkbits;
+ EXFAT_I(inode)->i_pos = ((loff_t)sbi->root_dir << 32) | 0xffffffff;
+ EXFAT_I(inode)->i_size_aligned = i_size_read(inode);
+ EXFAT_I(inode)->i_size_ondisk = i_size_read(inode);
+
+ exfat_save_attr(inode, ATTR_SUBDIR);
+ inode->i_mtime = inode->i_atime = inode->i_ctime = current_time(inode);
+ exfat_cache_init_inode(inode);
+ return 0;
+}
+
+static bool is_exfat(struct pbr *pbr)
+{
+ int i = 53;
+
+ do {
+ if (pbr->bpb.f64.res_zero[i - 1])
+ break;
+ } while (--i);
+ return i ? false : true;
+}
+
+static struct pbr *exfat_read_pbr_with_logical_sector(struct super_block *sb,
+ struct buffer_head **prev_bh)
+{
+ struct pbr *p_pbr = (struct pbr *) (*prev_bh)->b_data;
+ unsigned short logical_sect = 0;
+
+ logical_sect = 1 << p_pbr->bsx.f64.sect_size_bits;
+
+ if (!is_power_of_2(logical_sect) ||
+ logical_sect < 512 || logical_sect > 4096) {
+ exfat_msg(sb, KERN_ERR, "bogus logical sector size %u",
+ logical_sect);
+ return NULL;
+ }
+
+ if (logical_sect < sb->s_blocksize) {
+ exfat_msg(sb, KERN_ERR,
+ "logical sector size too small for device (logical sector size = %u)",
+ logical_sect);
+ return NULL;
+ }
+
+ if (logical_sect > sb->s_blocksize) {
+ struct buffer_head *bh = NULL;
+
+ __brelse(*prev_bh);
+ *prev_bh = NULL;
+
+ if (!sb_set_blocksize(sb, logical_sect)) {
+ exfat_msg(sb, KERN_ERR,
+ "unable to set blocksize %u", logical_sect);
+ return NULL;
+ }
+ bh = sb_bread(sb, 0);
+ if (!bh) {
+ exfat_msg(sb, KERN_ERR,
+ "unable to read boot sector (logical sector size = %lu)",
+ sb->s_blocksize);
+ return NULL;
+ }
+
+ *prev_bh = bh;
+ p_pbr = (struct pbr *) bh->b_data;
+ }
+
+ return p_pbr;
+}
+
+/* mount the file system volume */
+static int __exfat_fill_super(struct super_block *sb)
+{
+ int ret;
+ struct pbr *p_pbr;
+ struct pbr64 *p_bpb;
+ struct buffer_head *bh;
+ struct exfat_sb_info *sbi = EXFAT_SB(sb);
+
+ /* set block size to read super block */
+ sb_min_blocksize(sb, 512);
+
+ /* read boot sector */
+ bh = sb_bread(sb, 0);
+ if (!bh) {
+ exfat_msg(sb, KERN_ERR, "unable to read boot sector");
+ return -EIO;
+ }
+
+ /* PRB is read */
+ p_pbr = (struct pbr *)bh->b_data;
+
+ /* check the validity of PBR */
+ if (le16_to_cpu((p_pbr->signature)) != PBR_SIGNATURE) {
+ exfat_msg(sb, KERN_ERR, "invalid boot record signature");
+ ret = -EINVAL;
+ goto free_bh;
+ }
+
+
+ /* check logical sector size */
+ p_pbr = exfat_read_pbr_with_logical_sector(sb, &bh);
+ if (!p_pbr) {
+ ret = -EIO;
+ goto free_bh;
+ }
+
+ if (!is_exfat(p_pbr)) {
+ ret = -EINVAL;
+ goto free_bh;
+ }
+
+ /* set maximum file size for exFAT */
+ sb->s_maxbytes = 0x7fffffffffffffffLL;
+
+ p_bpb = (struct pbr64 *)p_pbr;
+ if (!p_bpb->bsx.num_fats) {
+ exfat_msg(sb, KERN_ERR, "bogus number of FAT structure");
+ ret = -EINVAL;
+ goto free_bh;
+ }
+
+ sbi->sect_per_clus = 1 << p_bpb->bsx.sect_per_clus_bits;
+ sbi->sect_per_clus_bits = p_bpb->bsx.sect_per_clus_bits;
+ sbi->cluster_size_bits = sbi->sect_per_clus_bits + sb->s_blocksize_bits;
+ sbi->cluster_size = 1 << sbi->cluster_size_bits;
+ sbi->num_FAT_sectors = le32_to_cpu(p_bpb->bsx.fat_length);
+ sbi->FAT1_start_sector = le32_to_cpu(p_bpb->bsx.fat_offset);
+
+ if (p_bpb->bsx.num_fats == 1)
+ sbi->FAT2_start_sector = sbi->FAT1_start_sector;
+ else
+ sbi->FAT2_start_sector =
+ sbi->FAT1_start_sector + sbi->num_FAT_sectors;
+
+ sbi->root_start_sector = le32_to_cpu(p_bpb->bsx.clu_offset);
+ sbi->data_start_sector = sbi->root_start_sector;
+ sbi->num_sectors = le64_to_cpu(p_bpb->bsx.vol_length);
+ /* because the cluster index starts with 2 */
+ sbi->num_clusters = le32_to_cpu(p_bpb->bsx.clu_count) + 2;
+
+ sbi->vol_id = le32_to_cpu(p_bpb->bsx.vol_serial);
+ sbi->root_dir = le32_to_cpu(p_bpb->bsx.root_cluster);
+ sbi->dentries_in_root = 0;
+ sbi->dentries_per_clu = 1 <<
+ (sbi->cluster_size_bits - DENTRY_SIZE_BITS);
+
+ sbi->vol_flag = le16_to_cpu(p_bpb->bsx.vol_flags);
+ sbi->clu_srch_ptr = BASE_CLUSTER;
+ sbi->used_clusters = ~0u;
+
+ if (le16_to_cpu(p_bpb->bsx.vol_flags) & VOL_DIRTY) {
+ sbi->vol_flag |= VOL_DIRTY;
+ exfat_msg(sb, KERN_WARNING,
+ "Volume was not properly unmounted. Some data may be corrupt. Please run fsck.");
+ }
+
+ ret = exfat_create_upcase_table(sb);
+ if (ret) {
+ exfat_msg(sb, KERN_ERR, "failed to load upcase table");
+ goto free_bh;
+ }
+
+ /* allocate-bitmap is only for exFAT */
+ ret = exfat_load_bitmap(sb);
+ if (ret) {
+ exfat_msg(sb, KERN_ERR, "failed to load alloc-bitmap");
+ goto free_upcase;
+ }
+
+ ret = exfat_count_used_clusters(sb, &sbi->used_clusters);
+ if (ret) {
+ exfat_msg(sb, KERN_ERR, "failed to scan clusters");
+ goto free_alloc_bitmap;
+ }
+
+ return 0;
+
+free_alloc_bitmap:
+ exfat_free_bitmap(sb);
+free_upcase:
+ exfat_free_upcase_table(sb);
+free_bh:
+ brelse(bh);
+
+ return ret;
+}
+
+static int exfat_fill_super(struct super_block *sb, struct fs_context *fc)
+{
+ struct exfat_sb_info *sbi = sb->s_fs_info;
+ struct exfat_mount_options *opts = &sbi->options;
+ struct inode *root_inode;
+ int err;
+
+ if (opts->allow_utime == -1)
+ opts->allow_utime = ~opts->fs_dmask & 0022;
+
+ if (opts->utf8 && strcmp(opts->iocharset, exfat_iocharset_with_utf8)) {
+ exfat_msg(sb, KERN_WARNING,
+ "utf8 enabled, \"iocharset=%s\" is recommended",
+ exfat_iocharset_with_utf8);
+ }
+
+ if (opts->discard) {
+ struct request_queue *q = bdev_get_queue(sb->s_bdev);
+
+ if (!blk_queue_discard(q))
+ exfat_msg(sb, KERN_WARNING,
+ "mounting with \"discard\" option, but the device does not support discard");
+ opts->discard = 0;
+ }
+
+ sb->s_flags |= SB_NODIRATIME;
+ sb->s_magic = EXFAT_SUPER_MAGIC;
+ sb->s_op = &exfat_sops;
+
+ if (EXFAT_SB(sb)->options.case_sensitive)
+ sb->s_d_op = &exfat_dentry_ops;
+ else
+ sb->s_d_op = &exfat_ci_dentry_ops;
+
+ err = __exfat_fill_super(sb);
+ if (err) {
+ exfat_msg(sb, KERN_ERR, "failed to recognize exfat type");
+ goto failed_mount;
+ }
+
+ /* set up enough so that it can read an inode */
+ exfat_hash_init(sb);
+
+ sbi->nls_io = load_nls(sbi->options.iocharset);
+ if (!sbi->nls_io) {
+ exfat_msg(sb, KERN_ERR, "IO charset %s not found",
+ sbi->options.iocharset);
+ err = -EINVAL;
+ goto failed_mount2;
+ }
+
+ root_inode = new_inode(sb);
+ if (!root_inode) {
+ exfat_msg(sb, KERN_ERR, "failed to allocate root inode.");
+ err = -ENOMEM;
+ goto failed_mount2;
+ }
+
+ root_inode->i_ino = EXFAT_ROOT_INO;
+ inode_set_iversion(root_inode, 1);
+ err = exfat_read_root(root_inode);
+ if (err) {
+ exfat_msg(sb, KERN_ERR, "failed to initialize root inode.");
+ goto failed_mount3;
+ }
+
+ exfat_hash_inode(root_inode, EXFAT_I(root_inode)->i_pos);
+ insert_inode_hash(root_inode);
+
+ sb->s_root = d_make_root(root_inode);
+ if (!sb->s_root) {
+ exfat_msg(sb, KERN_ERR, "failed to get the root dentry");
+ err = -ENOMEM;
+ goto failed_mount3;
+ }
+
+ return 0;
+
+failed_mount3:
+ iput(root_inode);
+ sb->s_root = NULL;
+
+failed_mount2:
+ exfat_free_upcase_table(sb);
+ exfat_free_bitmap(sb);
+
+failed_mount:
+ if (sbi->nls_io)
+ unload_nls(sbi->nls_io);
+ exfat_free_iocharset(sbi);
+ sb->s_fs_info = NULL;
+ kfree(sbi);
+ return err;
+}
+
+static int exfat_get_tree(struct fs_context *fc)
+{
+ return get_tree_bdev(fc, exfat_fill_super);
+}
+
+static void exfat_free(struct fs_context *fc)
+{
+ kfree(fc->s_fs_info);
+}
+
+static const struct fs_context_operations exfat_context_ops = {
+ .parse_param = exfat_parse_param,
+ .get_tree = exfat_get_tree,
+ .free = exfat_free,
+};
+
+static int exfat_init_fs_context(struct fs_context *fc)
+{
+ struct exfat_sb_info *sbi;
+
+ sbi = kzalloc(sizeof(struct exfat_sb_info), GFP_KERNEL);
+ if (!sbi)
+ return -ENOMEM;
+
+ mutex_init(&sbi->s_lock);
+ ratelimit_state_init(&sbi->ratelimit, DEFAULT_RATELIMIT_INTERVAL,
+ DEFAULT_RATELIMIT_BURST);
+
+ sbi->options.fs_uid = current_uid();
+ sbi->options.fs_gid = current_gid();
+ sbi->options.fs_fmask = current->fs->umask;
+ sbi->options.fs_dmask = current->fs->umask;
+ sbi->options.allow_utime = -1;
+ sbi->options.iocharset = exfat_default_iocharset;
+ sbi->options.errors = EXFAT_ERRORS_RO;
+
+ fc->s_fs_info = sbi;
+ fc->ops = &exfat_context_ops;
+ return 0;
+}
+
+static struct file_system_type exfat_fs_type = {
+ .owner = THIS_MODULE,
+ .name = "exfat",
+ .init_fs_context = exfat_init_fs_context,
+ .parameters = &exfat_parameters,
+ .kill_sb = kill_block_super,
+ .fs_flags = FS_REQUIRES_DEV,
+};
+
+static void exfat_inode_init_once(void *foo)
+{
+ struct exfat_inode_info *ei = (struct exfat_inode_info *)foo;
+
+ INIT_HLIST_NODE(&ei->i_hash_fat);
+ inode_init_once(&ei->vfs_inode);
+}
+
+static int __init init_exfat_fs(void)
+{
+ int err;
+
+ err = exfat_cache_init();
+ if (err)
+ return err;
+
+ err = -ENOMEM;
+ exfat_inode_cachep = kmem_cache_create("exfat_inode_cache",
+ sizeof(struct exfat_inode_info),
+ 0, SLAB_RECLAIM_ACCOUNT | SLAB_MEM_SPREAD,
+ exfat_inode_init_once);
+ if (!exfat_inode_cachep)
+ goto shutdown_cache;
+
+ err = register_filesystem(&exfat_fs_type);
+ if (err)
+ goto destroy_cache;
+
+ return 0;
+
+destroy_cache:
+ kmem_cache_destroy(exfat_inode_cachep);
+shutdown_cache:
+ exfat_cache_shutdown();
+
+ return err;
+}
+
+static void __exit exit_exfat_fs(void)
+{
+ kmem_cache_destroy(exfat_inode_cachep);
+ unregister_filesystem(&exfat_fs_type);
+ exfat_cache_shutdown();
+}
+
+module_init(init_exfat_fs);
+module_exit(exit_exfat_fs);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("exFAT filesystem support");
+MODULE_AUTHOR("Samsung Electronics Co., Ltd.");
--
2.17.1
This adds the implementation of bitmap operations for exfat.
Signed-off-by: Namjae Jeon <[email protected]>
Signed-off-by: Sungjong Seo <[email protected]>
---
fs/exfat/balloc.c | 271 ++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 271 insertions(+)
create mode 100644 fs/exfat/balloc.c
diff --git a/fs/exfat/balloc.c b/fs/exfat/balloc.c
new file mode 100644
index 000000000000..930e0ea3dbfd
--- /dev/null
+++ b/fs/exfat/balloc.c
@@ -0,0 +1,271 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd.
+ */
+
+#include <linux/blkdev.h>
+#include <linux/slab.h>
+#include <linux/buffer_head.h>
+
+#include "exfat_raw.h"
+#include "exfat_fs.h"
+
+static const unsigned char free_bit[] = {
+ 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2,/* 0 ~ 19*/
+ 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5, 0, 1, 0, 2, 0, 1, 0, 3,/* 20 ~ 39*/
+ 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2,/* 40 ~ 59*/
+ 0, 1, 0, 6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4,/* 60 ~ 79*/
+ 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5, 0, 1, 0, 2,/* 80 ~ 99*/
+ 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3,/*100 ~ 119*/
+ 0, 1, 0, 2, 0, 1, 0, 7, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2,/*120 ~ 139*/
+ 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5,/*140 ~ 159*/
+ 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2,/*160 ~ 179*/
+ 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 6, 0, 1, 0, 2, 0, 1, 0, 3,/*180 ~ 199*/
+ 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2,/*200 ~ 219*/
+ 0, 1, 0, 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4,/*220 ~ 239*/
+ 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0 /*240 ~ 254*/
+};
+
+static const unsigned char used_bit[] = {
+ 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, 1, 2, 2, 3,/* 0 ~ 19*/
+ 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 1, 2, 2, 3, 2, 3, 3, 4,/* 20 ~ 39*/
+ 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5,/* 40 ~ 59*/
+ 4, 5, 5, 6, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,/* 60 ~ 79*/
+ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4,/* 80 ~ 99*/
+ 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6,/*100 ~ 119*/
+ 4, 5, 5, 6, 5, 6, 6, 7, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4,/*120 ~ 139*/
+ 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,/*140 ~ 159*/
+ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5,/*160 ~ 179*/
+ 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 2, 3, 3, 4, 3, 4, 4, 5,/*180 ~ 199*/
+ 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6,/*200 ~ 219*/
+ 5, 6, 6, 7, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,/*220 ~ 239*/
+ 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8 /*240 ~ 255*/
+};
+
+/*
+ * Allocation Bitmap Management Functions
+ */
+static int exfat_allocate_bitmap(struct super_block *sb,
+ struct exfat_dentry *ep)
+{
+ struct exfat_sb_info *sbi = EXFAT_SB(sb);
+ long long map_size;
+ unsigned int i, need_map_size;
+ sector_t sector;
+
+ sbi->map_clu = le32_to_cpu(ep->bitmap_start_clu);
+ map_size = le64_to_cpu(ep->bitmap_size);
+ need_map_size = (((sbi->num_clusters - BASE_CLUSTER) - 1) >> 3) + 1;
+ if (need_map_size != map_size) {
+ exfat_msg(sb, KERN_ERR,
+ "bogus allocation bitmap size(need : %u, cur : %lld)",
+ need_map_size, map_size);
+ /*
+ * Only allowed when bogus allocation
+ * bitmap size is large
+ */
+ if (need_map_size > map_size)
+ return -EIO;
+ }
+ sbi->map_sectors = ((need_map_size - 1) >>
+ (sb->s_blocksize_bits)) + 1;
+ sbi->vol_amap = kmalloc_array(sbi->map_sectors,
+ sizeof(struct buffer_head *), GFP_KERNEL);
+ if (!sbi->vol_amap)
+ return -ENOMEM;
+
+ sector = exfat_cluster_to_sector(sbi, sbi->map_clu);
+ for (i = 0; i < sbi->map_sectors; i++) {
+ sbi->vol_amap[i] = sb_bread(sb, sector + i);
+ if (!sbi->vol_amap[i]) {
+ /* release all buffers and free vol_amap */
+ int j = 0;
+
+ while (j < i)
+ brelse(sbi->vol_amap[j++]);
+
+ kfree(sbi->vol_amap);
+ sbi->vol_amap = NULL;
+ return -EIO;
+ }
+ }
+
+ sbi->pbr_bh = NULL;
+ return 0;
+}
+
+int exfat_load_bitmap(struct super_block *sb)
+{
+ unsigned int i, type;
+ struct exfat_chain clu;
+ struct exfat_dentry *ep = NULL;
+ struct exfat_sb_info *sbi = EXFAT_SB(sb);
+ struct buffer_head *bh;
+
+ exfat_chain_set(&clu, sbi->root_dir, 0, 0x01);
+
+ while (clu.dir != EOF_CLUSTER) {
+ for (i = 0; i < sbi->dentries_per_clu; i++) {
+ ep = exfat_get_dentry(sb, &clu, i, &bh, NULL);
+ if (!ep)
+ return -EIO;
+
+ type = exfat_get_entry_type(ep);
+ if (type == TYPE_UNUSED)
+ break;
+ if (type != TYPE_BITMAP)
+ continue;
+ if (ep->bitmap_flags == 0x0) {
+ int err;
+
+ err = exfat_allocate_bitmap(sb, ep);
+ brelse(bh);
+ return err;
+ }
+ }
+
+ if (exfat_get_next_cluster(sb, &clu.dir))
+ return -EIO;
+ }
+
+ return -EINVAL;
+}
+
+void exfat_free_bitmap(struct super_block *sb)
+{
+ int i;
+ struct exfat_sb_info *sbi = EXFAT_SB(sb);
+
+ brelse(sbi->pbr_bh);
+
+ for (i = 0; i < sbi->map_sectors; i++)
+ __brelse(sbi->vol_amap[i]);
+
+ kfree(sbi->vol_amap);
+ sbi->vol_amap = NULL;
+}
+
+/*
+ * If the value of "clu" is 0, it means cluster 2 which is the first cluster of
+ * the cluster heap.
+ */
+int exfat_set_bitmap(struct inode *inode, unsigned int clu)
+{
+ int i, b;
+ struct super_block *sb = inode->i_sb;
+ struct exfat_sb_info *sbi = EXFAT_SB(sb);
+
+ i = clu >> (sb->s_blocksize_bits + 3);
+ b = clu & ((sb->s_blocksize << 3) - 1);
+
+ set_bit_le(b, sbi->vol_amap[i]->b_data);
+ exfat_update_bh(sb, sbi->vol_amap[i], IS_DIRSYNC(inode));
+
+ return 0;
+}
+
+/*
+ * If the value of "clu" is 0, it means cluster 2 which is the first cluster of
+ * the cluster heap.
+ */
+void exfat_clear_bitmap(struct inode *inode, unsigned int clu)
+{
+ int i, b;
+ struct super_block *sb = inode->i_sb;
+ struct exfat_sb_info *sbi = EXFAT_SB(sb);
+ struct exfat_mount_options *opts = &sbi->options;
+
+ i = clu >> (sb->s_blocksize_bits + 3);
+ b = clu & ((sb->s_blocksize << 3) - 1);
+
+ clear_bit_le(b, sbi->vol_amap[i]->b_data);
+ exfat_update_bh(sb, sbi->vol_amap[i], IS_DIRSYNC(inode));
+
+ if (opts->discard) {
+ int ret_discard;
+
+ ret_discard = sb_issue_discard(sb,
+ exfat_cluster_to_sector(sbi, clu + 2),
+ (1 << sbi->sect_per_clus_bits), GFP_NOFS, 0);
+
+ if (ret_discard == -EOPNOTSUPP) {
+ exfat_msg(sb, KERN_ERR,
+ "discard not supported by device, disabling");
+ opts->discard = 0;
+ }
+ }
+}
+
+/*
+ * If the value of "clu" is 0, it means cluster 2 which is the first cluster of
+ * the cluster heap.
+ */
+unsigned int exfat_test_bitmap(struct super_block *sb, unsigned int clu)
+{
+ unsigned int i, map_i, map_b;
+ unsigned int clu_base, clu_free;
+ unsigned char k, clu_mask;
+ struct exfat_sb_info *sbi = EXFAT_SB(sb);
+
+ clu_base = (clu & ~(0x7)) + 2;
+ clu_mask = (1 << (clu - clu_base + 2)) - 1;
+
+ map_i = clu >> (sb->s_blocksize_bits + 3);
+ map_b = (clu >> 3) & (unsigned int)(sb->s_blocksize - 1);
+
+ for (i = 2; i < sbi->num_clusters; i += 8) {
+ k = *(sbi->vol_amap[map_i]->b_data + map_b);
+ if (clu_mask > 0) {
+ k |= clu_mask;
+ clu_mask = 0;
+ }
+ if (k < 0xFF) {
+ clu_free = clu_base + free_bit[k];
+ if (clu_free < sbi->num_clusters)
+ return clu_free;
+ }
+ clu_base += 8;
+
+ if (++map_b >= sb->s_blocksize ||
+ clu_base >= sbi->num_clusters) {
+ if (++map_i >= sbi->map_sectors) {
+ clu_base = 2;
+ map_i = 0;
+ }
+ map_b = 0;
+ }
+ }
+
+ return EOF_CLUSTER;
+}
+
+int exfat_count_used_clusters(struct super_block *sb, unsigned int *ret_count)
+{
+ struct exfat_sb_info *sbi = EXFAT_SB(sb);
+ unsigned int count = 0;
+ unsigned int i, map_i = 0, map_b = 0;
+ unsigned int total_clus = sbi->num_clusters - 2;
+ unsigned int last_mask = total_clus & 7;
+ unsigned char clu_bits;
+ const unsigned char last_bit_mask[] = {0, 0b00000001, 0b00000011,
+ 0b00000111, 0b00001111, 0b00011111, 0b00111111, 0b01111111};
+
+ total_clus &= ~last_mask;
+ for (i = 0; i < total_clus; i += 8) {
+ clu_bits = *(sbi->vol_amap[map_i]->b_data + map_b);
+ count += used_bit[clu_bits];
+ if (++map_b >= (unsigned int)sb->s_blocksize) {
+ map_i++;
+ map_b = 0;
+ }
+ }
+
+ if (last_mask) {
+ clu_bits = *(sbi->vol_amap[map_i]->b_data + map_b);
+ clu_bits &= last_bit_mask[last_mask];
+ count += used_bit[clu_bits];
+ }
+
+ *ret_count = count;
+ return 0;
+}
--
2.17.1
This adds in-memory and on-disk structures and headers.
Signed-off-by: Namjae Jeon <[email protected]>
Signed-off-by: Sungjong Seo <[email protected]>
---
fs/exfat/exfat_fs.h | 534 +++++++++++++++++++++++++++++++++++++++++++
fs/exfat/exfat_raw.h | 190 +++++++++++++++
2 files changed, 724 insertions(+)
create mode 100644 fs/exfat/exfat_fs.h
create mode 100644 fs/exfat/exfat_raw.h
diff --git a/fs/exfat/exfat_fs.h b/fs/exfat/exfat_fs.h
new file mode 100644
index 000000000000..6e1c738e520a
--- /dev/null
+++ b/fs/exfat/exfat_fs.h
@@ -0,0 +1,534 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd.
+ */
+
+#ifndef _EXFAT_H
+#define _EXFAT_H
+
+#include <linux/fs.h>
+#include <linux/ratelimit.h>
+
+#define EXFAT_SUPER_MAGIC (0x2011BAB0UL)
+#define EXFAT_ROOT_INO 1
+
+enum exfat_time_mode {
+ TM_CREATE,
+ TM_MODIFY,
+ TM_ACCESS,
+};
+
+/*
+ * exfat error flags
+ */
+enum exfat_error_mode {
+ EXFAT_ERRORS_CONT, /* ignore error and continue */
+ EXFAT_ERRORS_PANIC, /* panic on error */
+ EXFAT_ERRORS_RO, /* remount r/o on error */
+};
+
+/*
+ * exfat nls lossy flag
+ */
+#define NLS_NAME_NO_LOSSY (0x00) /* no lossy */
+#define NLS_NAME_LOSSY (0x01) /* just detected incorrect filename(s) */
+#define NLS_NAME_OVERLEN (0x02) /* the length is over than its limit */
+
+/*
+ * exfat common MACRO
+ */
+#define CLUSTER_32(x) ((unsigned int)((x) & 0xFFFFFFFFU))
+#define EOF_CLUSTER CLUSTER_32(~0)
+#define BAD_CLUSTER (0xFFFFFFF7U)
+#define FREE_CLUSTER (0)
+#define BASE_CLUSTER (2)
+
+#define EXFAT_HASH_BITS 8
+#define EXFAT_HASH_SIZE (1UL << EXFAT_HASH_BITS)
+
+/*
+ * Type Definitions
+ */
+#define ES_2_ENTRIES 2
+#define ES_ALL_ENTRIES 0
+
+#define DIR_DELETED 0xFFFF0321
+
+/* type values */
+#define TYPE_UNUSED 0x0000
+#define TYPE_DELETED 0x0001
+#define TYPE_INVALID 0x0002
+#define TYPE_CRITICAL_PRI 0x0100
+#define TYPE_BITMAP 0x0101
+#define TYPE_UPCASE 0x0102
+#define TYPE_VOLUME 0x0103
+#define TYPE_DIR 0x0104
+#define TYPE_FILE 0x011F
+#define TYPE_CRITICAL_SEC 0x0200
+#define TYPE_STREAM 0x0201
+#define TYPE_EXTEND 0x0202
+#define TYPE_ACL 0x0203
+#define TYPE_BENIGN_PRI 0x0400
+#define TYPE_GUID 0x0401
+#define TYPE_PADDING 0x0402
+#define TYPE_ACLTAB 0x0403
+#define TYPE_BENIGN_SEC 0x0800
+#define TYPE_ALL 0x0FFF
+
+#define MAX_CHARSET_SIZE 6 /* max size of multi-byte character */
+#define MAX_NAME_LENGTH 255 /* max len of file name excluding NULL */
+#define MAX_VFSNAME_BUF_SIZE ((MAX_NAME_LENGTH + 1) * MAX_CHARSET_SIZE)
+
+#define FAT_CACHE_SIZE 128
+#define FAT_CACHE_HASH_SIZE 64
+#define BUF_CACHE_SIZE 256
+#define BUF_CACHE_HASH_SIZE 64
+
+#define EXFAT_HINT_NONE -1
+#define EXFAT_MIN_SUBDIR 2
+
+/*
+ * helpers for cluster size to byte conversion.
+ */
+#define EXFAT_CLU_TO_B(b, sbi) ((b) << (sbi)->cluster_size_bits)
+#define EXFAT_B_TO_CLU(b, sbi) ((b) >> (sbi)->cluster_size_bits)
+#define EXFAT_B_TO_CLU_ROUND_UP(b, sbi) \
+ (((b - 1) >> (sbi)->cluster_size_bits) + 1)
+#define EXFAT_CLU_OFFSET(off, sbi) ((off) & ((sbi)->cluster_size - 1))
+
+/*
+ * helpers for block size to byte conversion.
+ */
+#define EXFAT_BLK_TO_B(b, sb) ((b) << (sb)->s_blocksize_bits)
+#define EXFAT_B_TO_BLK(b, sb) ((b) >> (sb)->s_blocksize_bits)
+#define EXFAT_B_TO_BLK_ROUND_UP(b, sb) \
+ (((b - 1) >> (sb)->s_blocksize_bits) + 1)
+#define EXFAT_BLK_OFFSET(off, sb) ((off) & ((sb)->s_blocksize - 1))
+
+/*
+ * helpers for block size to dentry size conversion.
+ */
+#define EXFAT_B_TO_DEN_IDX(b, sbi) \
+ ((b) << ((sbi)->cluster_size_bits - DENTRY_SIZE_BITS))
+#define EXFAT_B_TO_DEN(b) ((b) >> DENTRY_SIZE_BITS)
+#define EXFAT_DEN_TO_B(b) ((b) << DENTRY_SIZE_BITS)
+
+struct exfat_timestamp {
+ unsigned short sec; /* 0 ~ 59 */
+ unsigned short min; /* 0 ~ 59 */
+ unsigned short hour; /* 0 ~ 23 */
+ unsigned short day; /* 1 ~ 31 */
+ unsigned short mon; /* 1 ~ 12 */
+ unsigned short year; /* 0 ~ 127 (since 1980) */
+};
+
+struct exfat_date_time {
+ unsigned short year;
+ unsigned short month;
+ unsigned short day;
+ unsigned short hour;
+ unsigned short minute;
+ unsigned short second;
+ unsigned short milli_second;
+};
+
+struct exfat_dentry_namebuf {
+ char *lfn;
+ int lfnbuf_len; /* usally MAX_UNINAME_BUF_SIZE */
+};
+
+/* unicode name structure */
+struct exfat_uni_name {
+ /* +3 for null and for converting */
+ unsigned short name[MAX_NAME_LENGTH + 3];
+ unsigned short name_hash;
+ unsigned char name_len;
+};
+
+/* directory structure */
+struct exfat_chain {
+ unsigned int dir;
+ unsigned int size;
+ unsigned char flags;
+};
+
+/* first empty entry hint information */
+struct exfat_hint_femp {
+ /* entry index of a directory */
+ int eidx;
+ /* count of continuous empty entry */
+ int count;
+ /* the cluster that first empty slot exists in */
+ struct exfat_chain cur;
+};
+
+/* hint structure */
+struct exfat_hint {
+ unsigned int clu;
+ union {
+ unsigned int off; /* cluster offset */
+ int eidx; /* entry index */
+ };
+};
+
+struct exfat_entry_set_cache {
+ /* sector number that contains file_entry */
+ sector_t sector;
+ /* byte offset in the sector */
+ unsigned int offset;
+ /* flag in stream entry. 01 for cluster chain, 03 for contig. */
+ int alloc_flag;
+ unsigned int num_entries;
+ struct exfat_dentry entries[];
+};
+
+struct exfat_dir_entry {
+ struct exfat_chain dir;
+ int entry;
+ unsigned int type;
+ unsigned int start_clu;
+ unsigned char flags;
+ unsigned short attr;
+ loff_t size;
+ unsigned int num_subdirs;
+ struct exfat_date_time create_timestamp;
+ struct exfat_date_time modify_timestamp;
+ struct exfat_date_time access_timestamp;
+ struct exfat_dentry_namebuf namebuf;
+};
+
+/*
+ * exfat mount in-memory data
+ */
+struct exfat_mount_options {
+ kuid_t fs_uid;
+ kgid_t fs_gid;
+ unsigned short fs_fmask;
+ unsigned short fs_dmask;
+ /* permission for setting the [am]time */
+ unsigned short allow_utime;
+ /* charset for filename input/display */
+ char *iocharset;
+ unsigned char utf8;
+ unsigned char case_sensitive;
+ unsigned char tz_utc;
+ /* on error: continue, panic, remount-ro */
+ enum exfat_error_mode errors;
+ /* flag on if -o dicard specified and device support discard() */
+ unsigned char discard;
+};
+
+/*
+ * EXFAT file system superblock in-memory data
+ */
+struct exfat_sb_info {
+ unsigned int vol_type; /* volume FAT type */
+ unsigned int vol_id; /* volume serial number */
+ unsigned long long num_sectors; /* num of sectors in volume */
+ unsigned int num_clusters; /* num of clusters in volume */
+ unsigned int cluster_size; /* cluster size in bytes */
+ unsigned int cluster_size_bits;
+ unsigned int sect_per_clus; /* cluster size in sectors */
+ unsigned int sect_per_clus_bits;
+ unsigned long long FAT1_start_sector; /* FAT1 start sector */
+ unsigned long long FAT2_start_sector; /* FAT2 start sector */
+ unsigned long long root_start_sector; /* root dir start sector */
+ unsigned long long data_start_sector; /* data area start sector */
+ unsigned int num_FAT_sectors; /* num of FAT sectors */
+ unsigned int root_dir; /* root dir cluster */
+ unsigned int dentries_in_root; /* num of dentries in root dir */
+ unsigned int dentries_per_clu; /* num of dentries per cluster */
+ unsigned int vol_flag; /* volume dirty flag */
+ struct buffer_head *pbr_bh; /* buffer_head of PBR sector */
+
+ unsigned int map_clu; /* allocation bitmap start cluster */
+ unsigned int map_sectors; /* num of allocation bitmap sectors */
+ struct buffer_head **vol_amap; /* allocation bitmap */
+
+ unsigned short *vol_utbl; /* upcase table */
+
+ unsigned int clu_srch_ptr; /* cluster search pointer */
+ unsigned int used_clusters; /* number of used clusters */
+
+ bool s_dirt;
+ struct mutex s_lock; /* superblock lock */
+ struct super_block *host_sb; /* sb pointer */
+ struct exfat_mount_options options;
+ struct nls_table *nls_io; /* Charset used for input and display */
+ struct ratelimit_state ratelimit;
+
+ spinlock_t inode_hash_lock;
+ struct hlist_head inode_hashtable[EXFAT_HASH_SIZE];
+};
+
+/*
+ * EXFAT file system inode in-memory data
+ */
+struct exfat_inode_info {
+ struct exfat_chain dir;
+ int entry;
+ unsigned int type;
+ unsigned short attr;
+ unsigned int start_clu;
+ unsigned char flags;
+ /*
+ * the copy of low 32bit of i_version to check
+ * the validation of hint_stat.
+ */
+ unsigned int version;
+ /* file offset or dentry index for readdir */
+ loff_t rwoffset;
+
+ /* hint for cluster last accessed */
+ struct exfat_hint hint_bmap;
+ /* hint for entry index we try to lookup next time */
+ struct exfat_hint hint_stat;
+ /* hint for first empty entry */
+ struct exfat_hint_femp hint_femp;
+
+ spinlock_t cache_lru_lock;
+ struct list_head cache_lru;
+ int nr_caches;
+ /* for avoiding the race between alloc and free */
+ unsigned int cache_valid_id;
+
+ /*
+ * NOTE: i_size_ondisk is 64bits, so must hold ->inode_lock to access.
+ * physically allocated size.
+ */
+ loff_t i_size_ondisk;
+ /* block-aligned i_size (used in cont_write_begin) */
+ loff_t i_size_aligned;
+ /* on-disk position of directory entry or 0 */
+ loff_t i_pos;
+ /* hash by i_location */
+ struct hlist_node i_hash_fat;
+ /* protect bmap against truncate */
+ struct rw_semaphore truncate_lock;
+ struct inode vfs_inode;
+};
+
+static inline struct exfat_sb_info *EXFAT_SB(struct super_block *sb)
+{
+ return sb->s_fs_info;
+}
+
+static inline struct exfat_inode_info *EXFAT_I(struct inode *inode)
+{
+ return container_of(inode, struct exfat_inode_info, vfs_inode);
+}
+
+/*
+ * If ->i_mode can't hold 0222 (i.e. ATTR_RO), we use ->i_attrs to
+ * save ATTR_RO instead of ->i_mode.
+ *
+ * If it's directory and !sbi->options.rodir, ATTR_RO isn't read-only
+ * bit, it's just used as flag for app.
+ */
+static inline int exfat_mode_can_hold_ro(struct inode *inode)
+{
+ struct exfat_sb_info *sbi = EXFAT_SB(inode->i_sb);
+
+ if (S_ISDIR(inode->i_mode))
+ return 0;
+
+ if ((~sbi->options.fs_fmask) & 0222)
+ return 1;
+ return 0;
+}
+
+/* Convert attribute bits and a mask to the UNIX mode. */
+static inline mode_t exfat_make_mode(struct exfat_sb_info *sbi,
+ unsigned short attr, mode_t mode)
+{
+ if ((attr & ATTR_READONLY) && !(attr & ATTR_SUBDIR))
+ mode &= ~0222;
+
+ if (attr & ATTR_SUBDIR)
+ return (mode & ~sbi->options.fs_dmask) | S_IFDIR;
+
+ return (mode & ~sbi->options.fs_fmask) | S_IFREG;
+}
+
+/* Return the FAT attribute byte for this inode */
+static inline unsigned short exfat_make_attr(struct inode *inode)
+{
+ unsigned short attr = EXFAT_I(inode)->attr;
+
+ if (S_ISDIR(inode->i_mode))
+ attr |= ATTR_SUBDIR;
+ if (exfat_mode_can_hold_ro(inode) && !(inode->i_mode & 0222))
+ attr |= ATTR_READONLY;
+ return attr;
+}
+
+static inline void exfat_save_attr(struct inode *inode, unsigned short attr)
+{
+ if (exfat_mode_can_hold_ro(inode))
+ EXFAT_I(inode)->attr = attr & (ATTR_RWMASK | ATTR_READONLY);
+ else
+ EXFAT_I(inode)->attr = attr & ATTR_RWMASK;
+}
+
+static inline bool exfat_is_last_sector_in_cluster(struct exfat_sb_info *sbi,
+ sector_t sec)
+{
+ return ((sec - sbi->data_start_sector + 1) &
+ ((1 << sbi->sect_per_clus_bits) - 1)) == 0;
+}
+
+static inline sector_t exfat_cluster_to_sector(struct exfat_sb_info *sbi,
+ unsigned int clus)
+{
+ return ((clus - BASE_CLUSTER) << sbi->sect_per_clus_bits)
+ + sbi->data_start_sector;
+}
+
+static inline int exfat_sector_to_cluster(struct exfat_sb_info *sbi,
+ sector_t sec)
+{
+ return ((sec - sbi->data_start_sector) >> sbi->sect_per_clus_bits) +
+ BASE_CLUSTER;
+}
+
+/* super.c */
+int exfat_set_vol_flags(struct super_block *sb, unsigned short new_flag);
+
+/* fatent.c */
+#define exfat_get_next_cluster(sb, pclu) exfat_ent_get(sb, *(pclu), pclu)
+
+int exfat_alloc_cluster(struct inode *inode, unsigned int num_alloc,
+ struct exfat_chain *p_chain);
+int exfat_free_cluster(struct inode *inode, struct exfat_chain *p_chain);
+int exfat_ent_get(struct super_block *sb, unsigned int loc,
+ unsigned int *content);
+int exfat_ent_set(struct super_block *sb, unsigned int loc,
+ unsigned int content);
+int exfat_count_ext_entries(struct super_block *sb, struct exfat_chain *p_dir,
+ int entry, struct exfat_dentry *p_entry);
+int exfat_chain_cont_cluster(struct super_block *sb, unsigned int chain,
+ unsigned int len);
+struct exfat_dentry *exfat_get_dentry(struct super_block *sb,
+ struct exfat_chain *p_dir, int entry, struct buffer_head **bh,
+ sector_t *sector);
+struct exfat_entry_set_cache *exfat_get_dentry_set(struct super_block *sb,
+ struct exfat_chain *p_dir, int entry, unsigned int type,
+ struct exfat_dentry **file_ep);
+int exfat_zeroed_cluster(struct inode *dir, unsigned int clu);
+int exfat_find_location(struct super_block *sb, struct exfat_chain *p_dir,
+ int entry, sector_t *sector, int *offset);
+int exfat_find_last_cluster(struct super_block *sb, struct exfat_chain *p_chain,
+ unsigned int *ret_clu);
+int exfat_mirror_bh(struct super_block *sb, sector_t sec,
+ struct buffer_head *bh);
+int exfat_count_num_clusters(struct super_block *sb,
+ struct exfat_chain *p_chain, unsigned int *ret_count);
+int exfat_count_dir_entries(struct super_block *sb, struct exfat_chain *p_dir);
+
+/* balloc.c */
+int exfat_load_bitmap(struct super_block *sb);
+void exfat_free_bitmap(struct super_block *sb);
+int exfat_set_bitmap(struct inode *inode, unsigned int clu);
+void exfat_clear_bitmap(struct inode *inode, unsigned int clu);
+unsigned int exfat_test_bitmap(struct super_block *sb, unsigned int clu);
+int exfat_count_used_clusters(struct super_block *sb, unsigned int *ret_count);
+
+/* file.c */
+extern const struct file_operations exfat_file_operations;
+int __exfat_truncate(struct inode *inode, loff_t new_size);
+void exfat_truncate(struct inode *inode, loff_t size);
+int exfat_setattr(struct dentry *dentry, struct iattr *attr);
+int exfat_getattr(const struct path *path, struct kstat *stat,
+ unsigned int request_mask, unsigned int query_flags);
+
+/* namei.c */
+extern const struct dentry_operations exfat_dentry_ops;
+extern const struct dentry_operations exfat_ci_dentry_ops;
+int exfat_find_empty_entry(struct inode *inode, struct exfat_chain *p_dir,
+ int num_entries);
+
+/* cache.c */
+int exfat_cache_init(void);
+void exfat_cache_shutdown(void);
+void exfat_cache_init_inode(struct inode *inode);
+void exfat_cache_inval_inode(struct inode *inode);
+int exfat_get_cluster(struct inode *inode, unsigned int cluster,
+ unsigned int *fclus, unsigned int *dclus,
+ unsigned int *last_dclus, int allow_eof);
+
+/* dir.c */
+extern const struct inode_operations exfat_dir_inode_operations;
+extern const struct file_operations exfat_dir_operations;
+void exfat_get_uniname_from_ext_entry(struct super_block *sb,
+ struct exfat_chain *p_dir, int entry, unsigned short *uniname);
+unsigned int exfat_get_entry_type(struct exfat_dentry *p_entry);
+void exfat_get_entry_time(struct exfat_dentry *p_entry,
+ struct exfat_timestamp *tp, unsigned char mode);
+void exfat_set_entry_time(struct exfat_dentry *p_entry,
+ struct exfat_timestamp *tp, unsigned char mode);
+int exfat_init_dir_entry(struct inode *inode, struct exfat_chain *p_dir,
+ int entry, unsigned int type, unsigned int start_clu,
+ unsigned long long size);
+int exfat_init_ext_entry(struct inode *inode, struct exfat_chain *p_dir,
+ int entry, int num_entries, struct exfat_uni_name *p_uniname);
+int exfat_remove_entries(struct inode *inode, struct exfat_chain *p_dir,
+ int entry, int order, int num_entries);
+int update_dir_chksum(struct inode *inode, struct exfat_chain *p_dir,
+ int entry);
+int exfat_update_dir_chksum_with_entry_set(struct super_block *sb,
+ struct exfat_entry_set_cache *es, int sync);
+int exfat_get_num_entries(struct exfat_uni_name *p_uniname);
+int exfat_find_dir_entry(struct super_block *sb, struct exfat_inode_info *ei,
+ struct exfat_chain *p_dir, struct exfat_uni_name *p_uniname,
+ int num_entries, unsigned int type);
+int exfat_alloc_new_dir(struct inode *inode, struct exfat_chain *clu);
+
+/* inode.c */
+extern const struct inode_operations exfat_file_inode_operations;
+void exfat_sync_inode(struct inode *inode);
+struct inode *exfat_build_inode(struct super_block *sb,
+ struct exfat_dir_entry *info, loff_t i_pos);
+void exfat_hash_inode(struct inode *inode, loff_t i_pos);
+void exfat_unhash_inode(struct inode *inode);
+void exfat_truncate(struct inode *inode, loff_t size);
+struct inode *exfat_iget(struct super_block *sb, loff_t i_pos);
+int exfat_write_inode(struct inode *inode, struct writeback_control *wbc);
+void exfat_evict_inode(struct inode *inode);
+int exfat_read_inode(struct inode *inode, struct exfat_dir_entry *info);
+
+/* exfat/nls.c */
+int exfat_nls_cmp_uniname(struct super_block *sb, unsigned short *a,
+ unsigned short *b);
+int exfat_nls_uni16s_to_vfsname(struct super_block *sb,
+ struct exfat_uni_name *uniname, unsigned char *p_cstring,
+ int len);
+int exfat_nls_vfsname_to_uni16s(struct super_block *sb,
+ const unsigned char *p_cstring, const int len,
+ struct exfat_uni_name *uniname, int *p_lossy);
+int exfat_create_upcase_table(struct super_block *sb);
+void exfat_free_upcase_table(struct super_block *sb);
+
+/* exfat/misc.c */
+void __exfat_fs_error(struct super_block *sb, int report, const char *fmt, ...)
+ __printf(3, 4) __cold;
+#define exfat_fs_error(sb, fmt, args...) \
+ __exfat_fs_error(sb, 1, fmt, ## args)
+#define exfat_fs_error_ratelimit(sb, fmt, args...) \
+ __exfat_fs_error(sb, __ratelimit(&EXFAT_SB(sb)->ratelimit), \
+ fmt, ## args)
+void exfat_msg(struct super_block *sb, const char *lv, const char *fmt, ...)
+ __printf(3, 4) __cold;
+void exfat_time_fat2unix(struct exfat_sb_info *sbi, struct timespec64 *ts,
+ struct exfat_date_time *tp);
+void exfat_time_unix2fat(struct exfat_sb_info *sbi, struct timespec64 *ts,
+ struct exfat_date_time *tp);
+struct exfat_timestamp *exfat_tm_now(struct exfat_sb_info *sbi,
+ struct exfat_timestamp *tm);
+unsigned short exfat_calc_chksum_2byte(void *data, int len,
+ unsigned short chksum, int type);
+void exfat_update_bh(struct super_block *sb, struct buffer_head *bh, int sync);
+void exfat_chain_set(struct exfat_chain *ec, unsigned int dir,
+ unsigned int size, unsigned char flags);
+struct exfat_chain *exfat_chain_dup(struct exfat_chain *dir);
+
+#endif /* !_EXFAT_H */
diff --git a/fs/exfat/exfat_raw.h b/fs/exfat/exfat_raw.h
new file mode 100644
index 000000000000..f40603992e0b
--- /dev/null
+++ b/fs/exfat/exfat_raw.h
@@ -0,0 +1,190 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd.
+ */
+
+#ifndef _EXFAT_RAW_H
+#define _EXFAT_RAW_H
+
+#include <linux/types.h>
+
+#define PBR_SIGNATURE 0xAA55
+
+#define VOL_CLEAN 0x0000
+#define VOL_DIRTY 0x0002
+
+#define DENTRY_SIZE 32 /* directory entry size */
+#define DENTRY_SIZE_BITS 5
+/* exFAT allows 8388608(256MB) directory entries */
+#define MAX_EXFAT_DENTRIES 8388608
+
+/* dentry types */
+#define MSDOS_DELETED 0xE5 /* deleted mark */
+#define MSDOS_UNUSED 0x00 /* end of directory */
+
+#define EXFAT_UNUSED 0x00 /* end of directory */
+#define EXFAT_DELETE ~(0x80)
+#define IS_EXFAT_DELETED(x) ((x) < 0x80) /* deleted file (0x01~0x7F) */
+#define EXFAT_INVAL 0x80 /* invalid value */
+#define EXFAT_BITMAP 0x81 /* allocation bitmap */
+#define EXFAT_UPCASE 0x82 /* upcase table */
+#define EXFAT_VOLUME 0x83 /* volume label */
+#define EXFAT_FILE 0x85 /* file or dir */
+#define EXFAT_GUID 0xA0
+#define EXFAT_PADDING 0xA1
+#define EXFAT_ACLTAB 0xA2
+#define EXFAT_STREAM 0xC0 /* stream entry */
+#define EXFAT_NAME 0xC1 /* file name entry */
+#define EXFAT_ACL 0xC2 /* stream entry */
+
+/* checksum types */
+#define CS_DIR_ENTRY 0
+#define CS_PBR_SECTOR 1
+#define CS_DEFAULT 2
+
+/* file attributes */
+#define ATTR_READONLY 0x0001
+#define ATTR_HIDDEN 0x0002
+#define ATTR_SYSTEM 0x0004
+#define ATTR_VOLUME 0x0008
+#define ATTR_SUBDIR 0x0010
+#define ATTR_ARCHIVE 0x0020
+#define ATTR_EXTEND (ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM | \
+ ATTR_VOLUME) /* 0x000F */
+
+#define ATTR_EXTEND_MASK (ATTR_EXTEND | ATTR_SUBDIR | ATTR_ARCHIVE)
+#define ATTR_RWMASK (ATTR_HIDDEN | ATTR_SYSTEM | ATTR_VOLUME | \
+ ATTR_SUBDIR | ATTR_ARCHIVE)
+
+#define ATTR_READONLY_LE cpu_to_le16(0x0001)
+#define ATTR_HIDDEN_LE cpu_to_le16(0x0002)
+#define ATTR_SYSTEM_LE cpu_to_le16(0x0004)
+#define ATTR_VOLUME_LE cpu_to_le16(0x0008)
+#define ATTR_SUBDIR_LE cpu_to_le16(0x0010)
+#define ATTR_ARCHIVE_LE cpu_to_le16(0x0020)
+
+/* EXFAT BIOS parameter block (64 bytes) */
+struct bpb64 {
+ __u8 jmp_boot[3];
+ __u8 oem_name[8];
+ __u8 res_zero[53];
+};
+
+/* EXFAT EXTEND BIOS parameter block (56 bytes) */
+struct bsx64 {
+ __le64 vol_offset;
+ __le64 vol_length;
+ __le32 fat_offset;
+ __le32 fat_length;
+ __le32 clu_offset;
+ __le32 clu_count;
+ __le32 root_cluster;
+ __le32 vol_serial;
+ __u8 fs_version[2];
+ __le16 vol_flags;
+ __u8 sect_size_bits;
+ __u8 sect_per_clus_bits;
+ __u8 num_fats;
+ __u8 phy_drv_no;
+ __u8 perc_in_use;
+ __u8 reserved2[7];
+};
+
+/* EXFAT PBR[BPB+BSX] (120 bytes) */
+struct pbr64 {
+ struct bpb64 bpb;
+ struct bsx64 bsx;
+};
+
+/* Common PBR[Partition Boot Record] (512 bytes) */
+struct pbr {
+ union {
+ __u8 raw[64];
+ struct bpb64 f64;
+ } bpb;
+ union {
+ __u8 raw[56];
+ struct bsx64 f64;
+ } bsx;
+ __u8 boot_code[390];
+ __le16 signature;
+};
+
+struct exfat_dentry {
+ __u8 type;
+ union {
+ struct {
+ __u8 num_ext;
+ __le16 checksum;
+ __le16 attr;
+ __le16 reserved1;
+ __le16 create_time;
+ __le16 create_date;
+ __le16 modify_time;
+ __le16 modify_date;
+ __le16 access_time;
+ __le16 access_date;
+ __u8 create_time_ms;
+ __u8 modify_time_ms;
+ __u8 access_time_ms;
+ __u8 reserved2[9];
+ } __packed file; /* file directory entry */
+ struct {
+ __u8 flags;
+ __u8 reserved1;
+ __u8 name_len;
+ __le16 name_hash;
+ __le16 reserved2;
+ __le64 valid_size;
+ __le32 reserved3;
+ __le32 start_clu;
+ __le64 size;
+ } __packed stream; /* stream extension directory entry */
+ struct {
+ __u8 flags;
+ __le16 unicode_0_14[15];
+ } __packed name; /* file name directory entry */
+ struct {
+ __u8 flags;
+ __u8 reserved[18];
+ __le32 start_clu;
+ __le64 size;
+ } __packed bitmap; /* allocation bitmap directory entry */
+ struct {
+ __u8 reserved1[3];
+ __le32 checksum;
+ __u8 reserved2[12];
+ __le32 start_clu;
+ __le64 size;
+ } __packed upcase; /* up-case table directory entry */
+ } __packed dentry;
+} __packed;
+
+#define file_num_ext dentry.file.num_ext
+#define file_checksum dentry.file.checksum
+#define file_attr dentry.file.attr
+#define file_create_time dentry.file.create_time
+#define file_create_date dentry.file.create_date
+#define file_modify_time dentry.file.modify_time
+#define file_modify_date dentry.file.modify_date
+#define file_access_time dentry.file.access_time
+#define file_access_date dentry.file.access_date
+#define file_create_time_ms dentry.file.create_time_ms
+#define file_modify_time_ms dentry.file.modify_time_ms
+#define file_access_time_ms dentry.file.access_time_ms
+#define stream_flags dentry.stream.flags
+#define stream_name_len dentry.stream.name_len
+#define stream_name_hash dentry.stream.name_hash
+#define stream_start_clu dentry.stream.start_clu
+#define stream_valid_size dentry.stream.valid_size
+#define stream_size dentry.stream.size
+#define name_flags dentry.name.flags
+#define name_unicode dentry.name.unicode_0_14
+#define bitmap_flags dentry.bitmap.flags
+#define bitmap_start_clu dentry.bitmap.start_clu
+#define bitmap_size dentry.bitmap.size
+#define upcase_start_clu dentry.upcase.start_clu
+#define upcase_size dentry.upcase.size
+#define upcase_checksum dentry.upcase.checksum
+
+#endif /* !_EXFAT_RAW_H */
--
2.17.1
Hi,
On Tue, Nov 19, 2019 at 02:10:56AM -0500, Namjae Jeon wrote:
> +static void exfat_put_super(struct super_block *sb)
> +{
> + struct exfat_sb_info *sbi = EXFAT_SB(sb);
> +
> + mutex_lock(&sbi->s_lock);
> + if (READ_ONCE(sbi->s_dirt)) {
> + WRITE_ONCE(sbi->s_dirt, true);
No idea what the code does. But I was just skimming over and find the
above pattern somehow strange. Shouldn't this be something like
if (!READ_ONCE(sbi->s_dirt)) {
WRITE_ONCE(sbi->s_dirt, true);
?
Thanks,
Daniel
> Hi,
Hi,
>
> On Tue, Nov 19, 2019 at 02:10:56AM -0500, Namjae Jeon wrote:
> > +static void exfat_put_super(struct super_block *sb)
> > +{
> > + struct exfat_sb_info *sbi = EXFAT_SB(sb);
> > +
> > + mutex_lock(&sbi->s_lock);
> > + if (READ_ONCE(sbi->s_dirt)) {
> > + WRITE_ONCE(sbi->s_dirt, true);
>
> No idea what the code does. But I was just skimming over and find the
> above pattern somehow strange. Shouldn't this be something like
Right.
>
> if (!READ_ONCE(sbi->s_dirt)) {
> WRITE_ONCE(sbi->s_dirt, true);
It should be :
if (READ_ONCE(sbi->s_dirt)) {
WRITE_ONCE(sbi->s_dirt, false);
I will fix it on v3.
Thanks for review!
>
> ?
>
> Thanks,
> Daniel
On Tue, Nov 19, 2019 at 06:22:28PM +0900, Namjae Jeon wrote:
> > No idea what the code does. But I was just skimming over and find the
> > above pattern somehow strange. Shouldn't this be something like
> Right.
>
> >
> > if (!READ_ONCE(sbi->s_dirt)) {
> > WRITE_ONCE(sbi->s_dirt, true);
>
> It should be :
> if (READ_ONCE(sbi->s_dirt)) {
> WRITE_ONCE(sbi->s_dirt, false);
> I will fix it on v3.
The other option would be to an unsigned long flags field and define
bits flags on it, then use test_and_set_bit, test_and_clear_bit etc.
Which might be closer to the pattern we use elsewhere in the kernel.
> On Tue, Nov 19, 2019 at 06:22:28PM +0900, Namjae Jeon wrote:
> > > No idea what the code does. But I was just skimming over and find the
> > > above pattern somehow strange. Shouldn't this be something like
> > Right.
> >
> > >
> > > if (!READ_ONCE(sbi->s_dirt)) {
> > > WRITE_ONCE(sbi->s_dirt, true);
> >
> > It should be :
> > if (READ_ONCE(sbi->s_dirt)) {
> > WRITE_ONCE(sbi->s_dirt, false);
> > I will fix it on v3.
>
> The other option would be to an unsigned long flags field and define
> bits flags on it, then use test_and_set_bit, test_and_clear_bit etc.
> Which might be closer to the pattern we use elsewhere in the kernel.
I will replace it with test_and_set/clear_bit().
Thanks!
On 19.11.19 г. 9:11 ч., Namjae Jeon wrote:
> This adds the implementation of exfat entry operations for exfat.
>
> Signed-off-by: Namjae Jeon <[email protected]>
> Signed-off-by: Sungjong Seo <[email protected]>
> ---
> fs/exfat/fatent.c | 475 ++++++++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 475 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..006c513ae5c0
> --- /dev/null
> +++ b/fs/exfat/fatent.c
> @@ -0,0 +1,475 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd.
> + */
> +
> +#include <linux/slab.h>
> +#include <asm/unaligned.h>
> +#include <linux/buffer_head.h>
> +
> +#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;
> + }
> +
> + WRITE_ONCE(sbi->s_dirt, true);
> + clu = p_chain->dir;
> +
> + if (p_chain->flags == 0x03) {
magic constant
> + 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 out;
> +
> + num_clusters++;
> + } while (clu != EOF_CLUSTER);
> + }
> +
> +out:
> + 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 == 0x03) {
ditto
> + *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 error;
> + }
> + 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 error;
> + }
> +
> + for (i = 0; i < n; i++)
> + brelse(bhs[i]);
> + n = 0;
> + }
> + }
> +
> + if (IS_DIRSYNC(dir)) {
> + err = exfat_sync_bhs(bhs, n);
> + if (err)
> + goto error;
> + }
> +
> + for (i = 0; i < n; i++)
> + brelse(bhs[i]);
> +
> + return 0;
> +
> +error:
> + 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 == 0x03) {
ditto
> + if (exfat_chain_cont_cluster(sb, p_chain->dir,
> + num_clusters))
> + return -EIO;
> + p_chain->flags = 0x01;
ditto
> + }
> + }
> +
> + WRITE_ONCE(sbi->s_dirt, true);
> +
> + 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 == 0x03) {
ditto
> + if (exfat_chain_cont_cluster(sb, p_chain->dir,
> + num_clusters)) {
> + ret = -EIO;
> + goto error;
> + }
> + p_chain->flags = 0x01;
ditto
> + }
> +
> + /* update allocation bitmap */
> + if (exfat_set_bitmap(inode, new_clu - BASE_CLUSTER)) {
> + ret = -EIO;
> + goto error;
> + }
> +
> + num_clusters++;
> +
> + /* update FAT table */
> + if (p_chain->flags == 0x01) {
ditto
> + if (exfat_ent_set(sb, new_clu, EOF_CLUSTER)) {
> + ret = -EIO;
> + goto error;
> + }
> + }
> +
> + if (p_chain->dir == EOF_CLUSTER) {
> + p_chain->dir = new_clu;
> + } else if (p_chain->flags == 0x01) {
ditto
> + if (exfat_ent_set(sb, last_clu, new_clu)) {
> + ret = -EIO;
> + goto error;
> + }
> + }
> + 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 == 0x03) {
ditto
> + if (exfat_chain_cont_cluster(sb, p_chain->dir,
> + num_clusters)) {
> + ret = -EIO;
> + goto error;
> + }
> + p_chain->flags = 0x01;
ditto
> + }
> + }
> + }
> +error:
> + 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 == 0x03) {
ditto
> + *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) {
> + err = -ENOMEM;
> + goto out;
> + }
> + 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);
> + }
> +out:
> + return err;
> +}
>
On 19.11.19 г. 9:11 ч., Namjae Jeon wrote:
> This adds the implementation of bitmap operations for exfat.
>
> Signed-off-by: Namjae Jeon <[email protected]>
> Signed-off-by: Sungjong Seo <[email protected]>
> ---
> fs/exfat/balloc.c | 271 ++++++++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 271 insertions(+)
> create mode 100644 fs/exfat/balloc.c
>
> diff --git a/fs/exfat/balloc.c b/fs/exfat/balloc.c
> new file mode 100644
> index 000000000000..930e0ea3dbfd
> --- /dev/null
> +++ b/fs/exfat/balloc.c
> @@ -0,0 +1,271 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd.
> + */
> +
> +#include <linux/blkdev.h>
> +#include <linux/slab.h>
> +#include <linux/buffer_head.h>
> +
> +#include "exfat_raw.h"
> +#include "exfat_fs.h"
> +
> +static const unsigned char free_bit[] = {
> + 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2,/* 0 ~ 19*/
> + 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5, 0, 1, 0, 2, 0, 1, 0, 3,/* 20 ~ 39*/
> + 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2,/* 40 ~ 59*/
> + 0, 1, 0, 6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4,/* 60 ~ 79*/
> + 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5, 0, 1, 0, 2,/* 80 ~ 99*/
> + 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3,/*100 ~ 119*/
> + 0, 1, 0, 2, 0, 1, 0, 7, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2,/*120 ~ 139*/
> + 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5,/*140 ~ 159*/
> + 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2,/*160 ~ 179*/
> + 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 6, 0, 1, 0, 2, 0, 1, 0, 3,/*180 ~ 199*/
> + 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2,/*200 ~ 219*/
> + 0, 1, 0, 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4,/*220 ~ 239*/
> + 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0 /*240 ~ 254*/
> +};
> +
> +static const unsigned char used_bit[] = {
> + 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, 1, 2, 2, 3,/* 0 ~ 19*/
> + 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 1, 2, 2, 3, 2, 3, 3, 4,/* 20 ~ 39*/
> + 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5,/* 40 ~ 59*/
> + 4, 5, 5, 6, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,/* 60 ~ 79*/
> + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4,/* 80 ~ 99*/
> + 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6,/*100 ~ 119*/
> + 4, 5, 5, 6, 5, 6, 6, 7, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4,/*120 ~ 139*/
> + 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,/*140 ~ 159*/
> + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5,/*160 ~ 179*/
> + 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 2, 3, 3, 4, 3, 4, 4, 5,/*180 ~ 199*/
> + 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6,/*200 ~ 219*/
> + 5, 6, 6, 7, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,/*220 ~ 239*/
> + 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8 /*240 ~ 255*/
> +};
> +
> +/*
> + * Allocation Bitmap Management Functions
> + */
> +static int exfat_allocate_bitmap(struct super_block *sb,
> + struct exfat_dentry *ep)
> +{
> + struct exfat_sb_info *sbi = EXFAT_SB(sb);
> + long long map_size;
> + unsigned int i, need_map_size;
> + sector_t sector;
> +
> + sbi->map_clu = le32_to_cpu(ep->bitmap_start_clu);
> + map_size = le64_to_cpu(ep->bitmap_size);
> + need_map_size = (((sbi->num_clusters - BASE_CLUSTER) - 1) >> 3) + 1;
> + if (need_map_size != map_size) {
> + exfat_msg(sb, KERN_ERR,
> + "bogus allocation bitmap size(need : %u, cur : %lld)",
> + need_map_size, map_size);
> + /*
> + * Only allowed when bogus allocation
> + * bitmap size is large
> + */
> + if (need_map_size > map_size)
> + return -EIO;
> + }
> + sbi->map_sectors = ((need_map_size - 1) >>
> + (sb->s_blocksize_bits)) + 1;
> + sbi->vol_amap = kmalloc_array(sbi->map_sectors,
> + sizeof(struct buffer_head *), GFP_KERNEL);
> + if (!sbi->vol_amap)
> + return -ENOMEM;
> +
> + sector = exfat_cluster_to_sector(sbi, sbi->map_clu);
> + for (i = 0; i < sbi->map_sectors; i++) {
> + sbi->vol_amap[i] = sb_bread(sb, sector + i);
> + if (!sbi->vol_amap[i]) {
> + /* release all buffers and free vol_amap */
> + int j = 0;
> +
> + while (j < i)
> + brelse(sbi->vol_amap[j++]);
> +
> + kfree(sbi->vol_amap);
> + sbi->vol_amap = NULL;
> + return -EIO;
> + }
> + }
> +
> + sbi->pbr_bh = NULL;
> + return 0;
> +}
> +
> +int exfat_load_bitmap(struct super_block *sb)
> +{
> + unsigned int i, type;
> + struct exfat_chain clu;
> + struct exfat_dentry *ep = NULL;
> + struct exfat_sb_info *sbi = EXFAT_SB(sb);
> + struct buffer_head *bh;
> +
> + exfat_chain_set(&clu, sbi->root_dir, 0, 0x01);
magic constant for the 4th parameter
> +
> + while (clu.dir != EOF_CLUSTER) {
> + for (i = 0; i < sbi->dentries_per_clu; i++) {
> + ep = exfat_get_dentry(sb, &clu, i, &bh, NULL);
> + if (!ep)
> + return -EIO;
> +
> + type = exfat_get_entry_type(ep);
> + if (type == TYPE_UNUSED)
> + break;
> + if (type != TYPE_BITMAP)
> + continue;
> + if (ep->bitmap_flags == 0x0) {
> + int err;
> +
> + err = exfat_allocate_bitmap(sb, ep);
> + brelse(bh);
> + return err;
> + }
> + }
> +
> + if (exfat_get_next_cluster(sb, &clu.dir))
> + return -EIO;
> + }
> +
> + return -EINVAL;
> +}
> +
> +void exfat_free_bitmap(struct super_block *sb)
> +{
> + int i;
> + struct exfat_sb_info *sbi = EXFAT_SB(sb);
> +
> + brelse(sbi->pbr_bh);
> +
> + for (i = 0; i < sbi->map_sectors; i++)
> + __brelse(sbi->vol_amap[i]);
> +
> + kfree(sbi->vol_amap);
> + sbi->vol_amap = NULL;
> +}
> +
> +/*
> + * If the value of "clu" is 0, it means cluster 2 which is the first cluster of
> + * the cluster heap.
> + */
> +int exfat_set_bitmap(struct inode *inode, unsigned int clu)
> +{
> + int i, b;
> + struct super_block *sb = inode->i_sb;
> + struct exfat_sb_info *sbi = EXFAT_SB(sb);
> +
> + i = clu >> (sb->s_blocksize_bits + 3);
> + b = clu & ((sb->s_blocksize << 3) - 1);
> +
> + set_bit_le(b, sbi->vol_amap[i]->b_data);
> + exfat_update_bh(sb, sbi->vol_amap[i], IS_DIRSYNC(inode));
> +
> + return 0;
> +}
> +
> +/*
> + * If the value of "clu" is 0, it means cluster 2 which is the first cluster of
> + * the cluster heap.
> + */
> +void exfat_clear_bitmap(struct inode *inode, unsigned int clu)
> +{
> + int i, b;
> + struct super_block *sb = inode->i_sb;
> + struct exfat_sb_info *sbi = EXFAT_SB(sb);
> + struct exfat_mount_options *opts = &sbi->options;
> +
> + i = clu >> (sb->s_blocksize_bits + 3);
> + b = clu & ((sb->s_blocksize << 3) - 1);
> +
> + clear_bit_le(b, sbi->vol_amap[i]->b_data);
> + exfat_update_bh(sb, sbi->vol_amap[i], IS_DIRSYNC(inode));
> +
> + if (opts->discard) {
> + int ret_discard;
> +
> + ret_discard = sb_issue_discard(sb,
> + exfat_cluster_to_sector(sbi, clu + 2),
> + (1 << sbi->sect_per_clus_bits), GFP_NOFS, 0);
> +
> + if (ret_discard == -EOPNOTSUPP) {
> + exfat_msg(sb, KERN_ERR,
> + "discard not supported by device, disabling");
> + opts->discard = 0;
> + }
> + }
> +}
> +
> +/*
> + * If the value of "clu" is 0, it means cluster 2 which is the first cluster of
> + * the cluster heap.
> + */
> +unsigned int exfat_test_bitmap(struct super_block *sb, unsigned int clu)
> +{
> + unsigned int i, map_i, map_b;
> + unsigned int clu_base, clu_free;
> + unsigned char k, clu_mask;
> + struct exfat_sb_info *sbi = EXFAT_SB(sb);
> +
> + clu_base = (clu & ~(0x7)) + 2;
> + clu_mask = (1 << (clu - clu_base + 2)) - 1;
> +
> + map_i = clu >> (sb->s_blocksize_bits + 3);
> + map_b = (clu >> 3) & (unsigned int)(sb->s_blocksize - 1);
> +
> + for (i = 2; i < sbi->num_clusters; i += 8) {
> + k = *(sbi->vol_amap[map_i]->b_data + map_b);
> + if (clu_mask > 0) {
> + k |= clu_mask;
> + clu_mask = 0;
> + }
> + if (k < 0xFF) {
> + clu_free = clu_base + free_bit[k];
> + if (clu_free < sbi->num_clusters)
> + return clu_free;
> + }
> + clu_base += 8;
> +
> + if (++map_b >= sb->s_blocksize ||
> + clu_base >= sbi->num_clusters) {
> + if (++map_i >= sbi->map_sectors) {
> + clu_base = 2;
> + map_i = 0;
> + }
> + map_b = 0;
> + }
> + }
> +
> + return EOF_CLUSTER;
> +}
> +
> +int exfat_count_used_clusters(struct super_block *sb, unsigned int *ret_count)
> +{
> + struct exfat_sb_info *sbi = EXFAT_SB(sb);
> + unsigned int count = 0;
> + unsigned int i, map_i = 0, map_b = 0;
> + unsigned int total_clus = sbi->num_clusters - 2;
> + unsigned int last_mask = total_clus & 7;
> + unsigned char clu_bits;
> + const unsigned char last_bit_mask[] = {0, 0b00000001, 0b00000011,
> + 0b00000111, 0b00001111, 0b00011111, 0b00111111, 0b01111111};
> +
> + total_clus &= ~last_mask;
> + for (i = 0; i < total_clus; i += 8) {
> + clu_bits = *(sbi->vol_amap[map_i]->b_data + map_b);
> + count += used_bit[clu_bits];
> + if (++map_b >= (unsigned int)sb->s_blocksize) {
> + map_i++;
> + map_b = 0;
> + }
> + }
> +
> + if (last_mask) {
> + clu_bits = *(sbi->vol_amap[map_i]->b_data + map_b);
> + clu_bits &= last_bit_mask[last_mask];
> + count += used_bit[clu_bits];
> + }
> +
> + *ret_count = count;
> + return 0;
> +}
>
On 19.11.19 г. 9:10 ч., Namjae Jeon wrote:
> This adds in-memory and on-disk structures and headers.
>
> Signed-off-by: Namjae Jeon <[email protected]>
> Signed-off-by: Sungjong Seo <[email protected]>
> ---
> fs/exfat/exfat_fs.h | 534 +++++++++++++++++++++++++++++++++++++++++++
> fs/exfat/exfat_raw.h | 190 +++++++++++++++
> 2 files changed, 724 insertions(+)
> create mode 100644 fs/exfat/exfat_fs.h
> create mode 100644 fs/exfat/exfat_raw.h
>
> diff --git a/fs/exfat/exfat_fs.h b/fs/exfat/exfat_fs.h
> new file mode 100644
> index 000000000000..6e1c738e520a
> --- /dev/null
> +++ b/fs/exfat/exfat_fs.h
> @@ -0,0 +1,534 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later */
> +/*
> + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd.
> + */
> +
> +#ifndef _EXFAT_H
> +#define _EXFAT_H
> +
> +#include <linux/fs.h>
> +#include <linux/ratelimit.h>
> +
> +#define EXFAT_SUPER_MAGIC (0x2011BAB0UL)
> +#define EXFAT_ROOT_INO 1
> +
> +enum exfat_time_mode {
> + TM_CREATE,
> + TM_MODIFY,
> + TM_ACCESS,
> +};
> +
> +/*
> + * exfat error flags
> + */
> +enum exfat_error_mode {
> + EXFAT_ERRORS_CONT, /* ignore error and continue */
> + EXFAT_ERRORS_PANIC, /* panic on error */
> + EXFAT_ERRORS_RO, /* remount r/o on error */
> +};
> +
> +/*
> + * exfat nls lossy flag
> + */
> +#define NLS_NAME_NO_LOSSY (0x00) /* no lossy */
> +#define NLS_NAME_LOSSY (0x01) /* just detected incorrect filename(s) */
> +#define NLS_NAME_OVERLEN (0x02) /* the length is over than its limit */
> +
> +/*
> + * exfat common MACRO
> + */
> +#define CLUSTER_32(x) ((unsigned int)((x) & 0xFFFFFFFFU))
> +#define EOF_CLUSTER CLUSTER_32(~0)
> +#define BAD_CLUSTER (0xFFFFFFF7U)
> +#define FREE_CLUSTER (0)
> +#define BASE_CLUSTER (2)
> +
> +#define EXFAT_HASH_BITS 8
> +#define EXFAT_HASH_SIZE (1UL << EXFAT_HASH_BITS)
> +
> +/*
> + * Type Definitions
> + */
> +#define ES_2_ENTRIES 2
> +#define ES_ALL_ENTRIES 0
> +
> +#define DIR_DELETED 0xFFFF0321
> +
> +/* type values */
> +#define TYPE_UNUSED 0x0000
> +#define TYPE_DELETED 0x0001
> +#define TYPE_INVALID 0x0002
> +#define TYPE_CRITICAL_PRI 0x0100
> +#define TYPE_BITMAP 0x0101
> +#define TYPE_UPCASE 0x0102
> +#define TYPE_VOLUME 0x0103
> +#define TYPE_DIR 0x0104
> +#define TYPE_FILE 0x011F
> +#define TYPE_CRITICAL_SEC 0x0200
> +#define TYPE_STREAM 0x0201
> +#define TYPE_EXTEND 0x0202
> +#define TYPE_ACL 0x0203
> +#define TYPE_BENIGN_PRI 0x0400
> +#define TYPE_GUID 0x0401
> +#define TYPE_PADDING 0x0402
> +#define TYPE_ACLTAB 0x0403
> +#define TYPE_BENIGN_SEC 0x0800
> +#define TYPE_ALL 0x0FFF
> +
> +#define MAX_CHARSET_SIZE 6 /* max size of multi-byte character */
> +#define MAX_NAME_LENGTH 255 /* max len of file name excluding NULL */
> +#define MAX_VFSNAME_BUF_SIZE ((MAX_NAME_LENGTH + 1) * MAX_CHARSET_SIZE)
> +
> +#define FAT_CACHE_SIZE 128
> +#define FAT_CACHE_HASH_SIZE 64
> +#define BUF_CACHE_SIZE 256
> +#define BUF_CACHE_HASH_SIZE 64
> +
> +#define EXFAT_HINT_NONE -1
> +#define EXFAT_MIN_SUBDIR 2
> +
> +/*
> + * helpers for cluster size to byte conversion.
> + */
> +#define EXFAT_CLU_TO_B(b, sbi) ((b) << (sbi)->cluster_size_bits)
> +#define EXFAT_B_TO_CLU(b, sbi) ((b) >> (sbi)->cluster_size_bits)
> +#define EXFAT_B_TO_CLU_ROUND_UP(b, sbi) \
> + (((b - 1) >> (sbi)->cluster_size_bits) + 1)
> +#define EXFAT_CLU_OFFSET(off, sbi) ((off) & ((sbi)->cluster_size - 1))
> +
> +/*
> + * helpers for block size to byte conversion.
> + */
> +#define EXFAT_BLK_TO_B(b, sb) ((b) << (sb)->s_blocksize_bits)
> +#define EXFAT_B_TO_BLK(b, sb) ((b) >> (sb)->s_blocksize_bits)
> +#define EXFAT_B_TO_BLK_ROUND_UP(b, sb) \
> + (((b - 1) >> (sb)->s_blocksize_bits) + 1)
> +#define EXFAT_BLK_OFFSET(off, sb) ((off) & ((sb)->s_blocksize - 1))
> +
> +/*
> + * helpers for block size to dentry size conversion.
> + */
> +#define EXFAT_B_TO_DEN_IDX(b, sbi) \
> + ((b) << ((sbi)->cluster_size_bits - DENTRY_SIZE_BITS))
> +#define EXFAT_B_TO_DEN(b) ((b) >> DENTRY_SIZE_BITS)
> +#define EXFAT_DEN_TO_B(b) ((b) << DENTRY_SIZE_BITS)
> +
> +struct exfat_timestamp {
> + unsigned short sec; /* 0 ~ 59 */
> + unsigned short min; /* 0 ~ 59 */
> + unsigned short hour; /* 0 ~ 23 */
> + unsigned short day; /* 1 ~ 31 */
> + unsigned short mon; /* 1 ~ 12 */
> + unsigned short year; /* 0 ~ 127 (since 1980) */
> +};
> +
> +struct exfat_date_time {
> + unsigned short year;
> + unsigned short month;
> + unsigned short day;
> + unsigned short hour;
> + unsigned short minute;
> + unsigned short second;
> + unsigned short milli_second;
> +};
> +
> +struct exfat_dentry_namebuf {
> + char *lfn;
> + int lfnbuf_len; /* usally MAX_UNINAME_BUF_SIZE */
> +};
> +
> +/* unicode name structure */
> +struct exfat_uni_name {
> + /* +3 for null and for converting */
> + unsigned short name[MAX_NAME_LENGTH + 3];
> + unsigned short name_hash;
> + unsigned char name_len;
> +};
> +
> +/* directory structure */
> +struct exfat_chain {
> + unsigned int dir;
> + unsigned int size;
> + unsigned char flags;
> +};
> +
> +/* first empty entry hint information */
> +struct exfat_hint_femp {
> + /* entry index of a directory */
> + int eidx;
> + /* count of continuous empty entry */
> + int count;
> + /* the cluster that first empty slot exists in */
> + struct exfat_chain cur;
> +};
> +
> +/* hint structure */
> +struct exfat_hint {
> + unsigned int clu;
> + union {
> + unsigned int off; /* cluster offset */
> + int eidx; /* entry index */
> + };
> +};
> +
> +struct exfat_entry_set_cache {
> + /* sector number that contains file_entry */
> + sector_t sector;
> + /* byte offset in the sector */
> + unsigned int offset;
> + /* flag in stream entry. 01 for cluster chain, 03 for contig. */
> + int alloc_flag;
> + unsigned int num_entries;
> + struct exfat_dentry entries[];
> +};
> +
> +struct exfat_dir_entry {
> + struct exfat_chain dir;
> + int entry;
> + unsigned int type;
> + unsigned int start_clu;
> + unsigned char flags;
> + unsigned short attr;
> + loff_t size;
> + unsigned int num_subdirs;
> + struct exfat_date_time create_timestamp;
> + struct exfat_date_time modify_timestamp;
> + struct exfat_date_time access_timestamp;
> + struct exfat_dentry_namebuf namebuf;
> +};
> +
> +/*
> + * exfat mount in-memory data
> + */
> +struct exfat_mount_options {
> + kuid_t fs_uid;
> + kgid_t fs_gid;
> + unsigned short fs_fmask;
> + unsigned short fs_dmask;
> + /* permission for setting the [am]time */
> + unsigned short allow_utime;
> + /* charset for filename input/display */
> + char *iocharset;
> + unsigned char utf8;
> + unsigned char case_sensitive;
> + unsigned char tz_utc;
> + /* on error: continue, panic, remount-ro */
> + enum exfat_error_mode errors;
> + /* flag on if -o dicard specified and device support discard() */
> + unsigned char discard;
> +};
> +
> +/*
> + * EXFAT file system superblock in-memory data
> + */
> +struct exfat_sb_info {
> + unsigned int vol_type; /* volume FAT type */
> + unsigned int vol_id; /* volume serial number */
> + unsigned long long num_sectors; /* num of sectors in volume */
> + unsigned int num_clusters; /* num of clusters in volume */
> + unsigned int cluster_size; /* cluster size in bytes */
> + unsigned int cluster_size_bits;
> + unsigned int sect_per_clus; /* cluster size in sectors */
> + unsigned int sect_per_clus_bits;
> + unsigned long long FAT1_start_sector; /* FAT1 start sector */
> + unsigned long long FAT2_start_sector; /* FAT2 start sector */
> + unsigned long long root_start_sector; /* root dir start sector */
> + unsigned long long data_start_sector; /* data area start sector */
> + unsigned int num_FAT_sectors; /* num of FAT sectors */
> + unsigned int root_dir; /* root dir cluster */
> + unsigned int dentries_in_root; /* num of dentries in root dir */
> + unsigned int dentries_per_clu; /* num of dentries per cluster */
> + unsigned int vol_flag; /* volume dirty flag */
> + struct buffer_head *pbr_bh; /* buffer_head of PBR sector */
> +
> + unsigned int map_clu; /* allocation bitmap start cluster */
> + unsigned int map_sectors; /* num of allocation bitmap sectors */
> + struct buffer_head **vol_amap; /* allocation bitmap */
> +
> + unsigned short *vol_utbl; /* upcase table */
> +
> + unsigned int clu_srch_ptr; /* cluster search pointer */
> + unsigned int used_clusters; /* number of used clusters */
> +
> + bool s_dirt;
> + struct mutex s_lock; /* superblock lock */
> + struct super_block *host_sb; /* sb pointer */
> + struct exfat_mount_options options;
> + struct nls_table *nls_io; /* Charset used for input and display */
> + struct ratelimit_state ratelimit;
> +
> + spinlock_t inode_hash_lock;
> + struct hlist_head inode_hashtable[EXFAT_HASH_SIZE];
> +};
> +
> +/*
> + * EXFAT file system inode in-memory data
> + */
> +struct exfat_inode_info {
> + struct exfat_chain dir;
> + int entry;
> + unsigned int type;
> + unsigned short attr;
> + unsigned int start_clu;
> + unsigned char flags;
> + /*
> + * the copy of low 32bit of i_version to check
> + * the validation of hint_stat.
> + */
> + unsigned int version;
> + /* file offset or dentry index for readdir */
> + loff_t rwoffset;
> +
> + /* hint for cluster last accessed */
> + struct exfat_hint hint_bmap;
> + /* hint for entry index we try to lookup next time */
> + struct exfat_hint hint_stat;
> + /* hint for first empty entry */
> + struct exfat_hint_femp hint_femp;
> +
> + spinlock_t cache_lru_lock;
> + struct list_head cache_lru;
> + int nr_caches;
> + /* for avoiding the race between alloc and free */
> + unsigned int cache_valid_id;
> +
> + /*
> + * NOTE: i_size_ondisk is 64bits, so must hold ->inode_lock to access.
> + * physically allocated size.
> + */
> + loff_t i_size_ondisk;
> + /* block-aligned i_size (used in cont_write_begin) */
> + loff_t i_size_aligned;
> + /* on-disk position of directory entry or 0 */
> + loff_t i_pos;
> + /* hash by i_location */
> + struct hlist_node i_hash_fat;
> + /* protect bmap against truncate */
> + struct rw_semaphore truncate_lock;
> + struct inode vfs_inode;
> +};
> +
> +static inline struct exfat_sb_info *EXFAT_SB(struct super_block *sb)
> +{
> + return sb->s_fs_info;
> +}
> +
> +static inline struct exfat_inode_info *EXFAT_I(struct inode *inode)
> +{
> + return container_of(inode, struct exfat_inode_info, vfs_inode);
> +}
> +
> +/*
> + * If ->i_mode can't hold 0222 (i.e. ATTR_RO), we use ->i_attrs to
> + * save ATTR_RO instead of ->i_mode.
> + *
> + * If it's directory and !sbi->options.rodir, ATTR_RO isn't read-only
> + * bit, it's just used as flag for app.
> + */
> +static inline int exfat_mode_can_hold_ro(struct inode *inode)
> +{
> + struct exfat_sb_info *sbi = EXFAT_SB(inode->i_sb);
> +
> + if (S_ISDIR(inode->i_mode))
> + return 0;
> +
> + if ((~sbi->options.fs_fmask) & 0222)
> + return 1;
> + return 0;
> +}
> +
> +/* Convert attribute bits and a mask to the UNIX mode. */
> +static inline mode_t exfat_make_mode(struct exfat_sb_info *sbi,
> + unsigned short attr, mode_t mode)
> +{
> + if ((attr & ATTR_READONLY) && !(attr & ATTR_SUBDIR))
> + mode &= ~0222;
> +
> + if (attr & ATTR_SUBDIR)
> + return (mode & ~sbi->options.fs_dmask) | S_IFDIR;
> +
> + return (mode & ~sbi->options.fs_fmask) | S_IFREG;
> +}
> +
> +/* Return the FAT attribute byte for this inode */
> +static inline unsigned short exfat_make_attr(struct inode *inode)
> +{
> + unsigned short attr = EXFAT_I(inode)->attr;
> +
> + if (S_ISDIR(inode->i_mode))
> + attr |= ATTR_SUBDIR;
> + if (exfat_mode_can_hold_ro(inode) && !(inode->i_mode & 0222))
> + attr |= ATTR_READONLY;
> + return attr;
> +}
> +
> +static inline void exfat_save_attr(struct inode *inode, unsigned short attr)
> +{
> + if (exfat_mode_can_hold_ro(inode))
> + EXFAT_I(inode)->attr = attr & (ATTR_RWMASK | ATTR_READONLY);
> + else
> + EXFAT_I(inode)->attr = attr & ATTR_RWMASK;
> +}
> +
> +static inline bool exfat_is_last_sector_in_cluster(struct exfat_sb_info *sbi,
> + sector_t sec)
> +{
> + return ((sec - sbi->data_start_sector + 1) &
> + ((1 << sbi->sect_per_clus_bits) - 1)) == 0;
> +}
> +
> +static inline sector_t exfat_cluster_to_sector(struct exfat_sb_info *sbi,
> + unsigned int clus)
> +{
> + return ((clus - BASE_CLUSTER) << sbi->sect_per_clus_bits)
> + + sbi->data_start_sector;
> +}
> +
> +static inline int exfat_sector_to_cluster(struct exfat_sb_info *sbi,
> + sector_t sec)
> +{
> + return ((sec - sbi->data_start_sector) >> sbi->sect_per_clus_bits) +
> + BASE_CLUSTER;
> +}
> +
> +/* super.c */
> +int exfat_set_vol_flags(struct super_block *sb, unsigned short new_flag);
> +
> +/* fatent.c */
> +#define exfat_get_next_cluster(sb, pclu) exfat_ent_get(sb, *(pclu), pclu)
> +
> +int exfat_alloc_cluster(struct inode *inode, unsigned int num_alloc,
> + struct exfat_chain *p_chain);
> +int exfat_free_cluster(struct inode *inode, struct exfat_chain *p_chain);
> +int exfat_ent_get(struct super_block *sb, unsigned int loc,
> + unsigned int *content);
> +int exfat_ent_set(struct super_block *sb, unsigned int loc,
> + unsigned int content);
> +int exfat_count_ext_entries(struct super_block *sb, struct exfat_chain *p_dir,
> + int entry, struct exfat_dentry *p_entry);
> +int exfat_chain_cont_cluster(struct super_block *sb, unsigned int chain,
> + unsigned int len);
> +struct exfat_dentry *exfat_get_dentry(struct super_block *sb,
> + struct exfat_chain *p_dir, int entry, struct buffer_head **bh,
> + sector_t *sector);
> +struct exfat_entry_set_cache *exfat_get_dentry_set(struct super_block *sb,
> + struct exfat_chain *p_dir, int entry, unsigned int type,
> + struct exfat_dentry **file_ep);
> +int exfat_zeroed_cluster(struct inode *dir, unsigned int clu);
> +int exfat_find_location(struct super_block *sb, struct exfat_chain *p_dir,
> + int entry, sector_t *sector, int *offset);
> +int exfat_find_last_cluster(struct super_block *sb, struct exfat_chain *p_chain,
> + unsigned int *ret_clu);
> +int exfat_mirror_bh(struct super_block *sb, sector_t sec,
> + struct buffer_head *bh);
> +int exfat_count_num_clusters(struct super_block *sb,
> + struct exfat_chain *p_chain, unsigned int *ret_count);
> +int exfat_count_dir_entries(struct super_block *sb, struct exfat_chain *p_dir);
> +
> +/* balloc.c */
> +int exfat_load_bitmap(struct super_block *sb);
> +void exfat_free_bitmap(struct super_block *sb);
> +int exfat_set_bitmap(struct inode *inode, unsigned int clu);
> +void exfat_clear_bitmap(struct inode *inode, unsigned int clu);
> +unsigned int exfat_test_bitmap(struct super_block *sb, unsigned int clu);
> +int exfat_count_used_clusters(struct super_block *sb, unsigned int *ret_count);
> +
> +/* file.c */
> +extern const struct file_operations exfat_file_operations;
> +int __exfat_truncate(struct inode *inode, loff_t new_size);
> +void exfat_truncate(struct inode *inode, loff_t size);
> +int exfat_setattr(struct dentry *dentry, struct iattr *attr);
> +int exfat_getattr(const struct path *path, struct kstat *stat,
> + unsigned int request_mask, unsigned int query_flags);
> +
> +/* namei.c */
> +extern const struct dentry_operations exfat_dentry_ops;
> +extern const struct dentry_operations exfat_ci_dentry_ops;
> +int exfat_find_empty_entry(struct inode *inode, struct exfat_chain *p_dir,
> + int num_entries);
> +
> +/* cache.c */
> +int exfat_cache_init(void);
> +void exfat_cache_shutdown(void);
> +void exfat_cache_init_inode(struct inode *inode);
> +void exfat_cache_inval_inode(struct inode *inode);
> +int exfat_get_cluster(struct inode *inode, unsigned int cluster,
> + unsigned int *fclus, unsigned int *dclus,
> + unsigned int *last_dclus, int allow_eof);
> +
> +/* dir.c */
> +extern const struct inode_operations exfat_dir_inode_operations;
> +extern const struct file_operations exfat_dir_operations;
> +void exfat_get_uniname_from_ext_entry(struct super_block *sb,
> + struct exfat_chain *p_dir, int entry, unsigned short *uniname);
> +unsigned int exfat_get_entry_type(struct exfat_dentry *p_entry);
> +void exfat_get_entry_time(struct exfat_dentry *p_entry,
> + struct exfat_timestamp *tp, unsigned char mode);
> +void exfat_set_entry_time(struct exfat_dentry *p_entry,
> + struct exfat_timestamp *tp, unsigned char mode);
> +int exfat_init_dir_entry(struct inode *inode, struct exfat_chain *p_dir,
> + int entry, unsigned int type, unsigned int start_clu,
> + unsigned long long size);
> +int exfat_init_ext_entry(struct inode *inode, struct exfat_chain *p_dir,
> + int entry, int num_entries, struct exfat_uni_name *p_uniname);
> +int exfat_remove_entries(struct inode *inode, struct exfat_chain *p_dir,
> + int entry, int order, int num_entries);
> +int update_dir_chksum(struct inode *inode, struct exfat_chain *p_dir,
> + int entry);
> +int exfat_update_dir_chksum_with_entry_set(struct super_block *sb,
> + struct exfat_entry_set_cache *es, int sync);
> +int exfat_get_num_entries(struct exfat_uni_name *p_uniname);
> +int exfat_find_dir_entry(struct super_block *sb, struct exfat_inode_info *ei,
> + struct exfat_chain *p_dir, struct exfat_uni_name *p_uniname,
> + int num_entries, unsigned int type);
> +int exfat_alloc_new_dir(struct inode *inode, struct exfat_chain *clu);
> +
> +/* inode.c */
> +extern const struct inode_operations exfat_file_inode_operations;
> +void exfat_sync_inode(struct inode *inode);
> +struct inode *exfat_build_inode(struct super_block *sb,
> + struct exfat_dir_entry *info, loff_t i_pos);
> +void exfat_hash_inode(struct inode *inode, loff_t i_pos);
> +void exfat_unhash_inode(struct inode *inode);
> +void exfat_truncate(struct inode *inode, loff_t size);
> +struct inode *exfat_iget(struct super_block *sb, loff_t i_pos);
> +int exfat_write_inode(struct inode *inode, struct writeback_control *wbc);
> +void exfat_evict_inode(struct inode *inode);
> +int exfat_read_inode(struct inode *inode, struct exfat_dir_entry *info);
> +
> +/* exfat/nls.c */
> +int exfat_nls_cmp_uniname(struct super_block *sb, unsigned short *a,
> + unsigned short *b);
> +int exfat_nls_uni16s_to_vfsname(struct super_block *sb,
> + struct exfat_uni_name *uniname, unsigned char *p_cstring,
> + int len);
> +int exfat_nls_vfsname_to_uni16s(struct super_block *sb,
> + const unsigned char *p_cstring, const int len,
> + struct exfat_uni_name *uniname, int *p_lossy);
> +int exfat_create_upcase_table(struct super_block *sb);
> +void exfat_free_upcase_table(struct super_block *sb);
> +
> +/* exfat/misc.c */
> +void __exfat_fs_error(struct super_block *sb, int report, const char *fmt, ...)
> + __printf(3, 4) __cold;
> +#define exfat_fs_error(sb, fmt, args...) \
> + __exfat_fs_error(sb, 1, fmt, ## args)
> +#define exfat_fs_error_ratelimit(sb, fmt, args...) \
> + __exfat_fs_error(sb, __ratelimit(&EXFAT_SB(sb)->ratelimit), \
> + fmt, ## args)
> +void exfat_msg(struct super_block *sb, const char *lv, const char *fmt, ...)
> + __printf(3, 4) __cold;
> +void exfat_time_fat2unix(struct exfat_sb_info *sbi, struct timespec64 *ts,
> + struct exfat_date_time *tp);
> +void exfat_time_unix2fat(struct exfat_sb_info *sbi, struct timespec64 *ts,
> + struct exfat_date_time *tp);
> +struct exfat_timestamp *exfat_tm_now(struct exfat_sb_info *sbi,
> + struct exfat_timestamp *tm);
> +unsigned short exfat_calc_chksum_2byte(void *data, int len,
> + unsigned short chksum, int type);
> +void exfat_update_bh(struct super_block *sb, struct buffer_head *bh, int sync);
> +void exfat_chain_set(struct exfat_chain *ec, unsigned int dir,
> + unsigned int size, unsigned char flags);
nit: You declare the function here and subsequent patches also use yet
it's not introduced until patch 8. This clearly will break the build
with only some patches applied of this series. This is likely not the
only offending function.
> +struct exfat_chain *exfat_chain_dup(struct exfat_chain *dir);
> +
> +#endif /* !_EXFAT_H */
> diff --git a/fs/exfat/exfat_raw.h b/fs/exfat/exfat_raw.h
> new file mode 100644
> index 000000000000..f40603992e0b
> --- /dev/null
> +++ b/fs/exfat/exfat_raw.h
> @@ -0,0 +1,190 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later */
> +/*
> + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd.
> + */
> +
> +#ifndef _EXFAT_RAW_H
> +#define _EXFAT_RAW_H
> +
> +#include <linux/types.h>
> +
> +#define PBR_SIGNATURE 0xAA55
> +
> +#define VOL_CLEAN 0x0000
> +#define VOL_DIRTY 0x0002
> +
> +#define DENTRY_SIZE 32 /* directory entry size */
> +#define DENTRY_SIZE_BITS 5
> +/* exFAT allows 8388608(256MB) directory entries */
> +#define MAX_EXFAT_DENTRIES 8388608
> +
> +/* dentry types */
> +#define MSDOS_DELETED 0xE5 /* deleted mark */
> +#define MSDOS_UNUSED 0x00 /* end of directory */
> +
> +#define EXFAT_UNUSED 0x00 /* end of directory */
> +#define EXFAT_DELETE ~(0x80)
> +#define IS_EXFAT_DELETED(x) ((x) < 0x80) /* deleted file (0x01~0x7F) */
> +#define EXFAT_INVAL 0x80 /* invalid value */
> +#define EXFAT_BITMAP 0x81 /* allocation bitmap */
> +#define EXFAT_UPCASE 0x82 /* upcase table */
> +#define EXFAT_VOLUME 0x83 /* volume label */
> +#define EXFAT_FILE 0x85 /* file or dir */
> +#define EXFAT_GUID 0xA0
> +#define EXFAT_PADDING 0xA1
> +#define EXFAT_ACLTAB 0xA2
> +#define EXFAT_STREAM 0xC0 /* stream entry */
> +#define EXFAT_NAME 0xC1 /* file name entry */
> +#define EXFAT_ACL 0xC2 /* stream entry */
> +
> +/* checksum types */
> +#define CS_DIR_ENTRY 0
> +#define CS_PBR_SECTOR 1
> +#define CS_DEFAULT 2
> +
> +/* file attributes */
> +#define ATTR_READONLY 0x0001
> +#define ATTR_HIDDEN 0x0002
> +#define ATTR_SYSTEM 0x0004
> +#define ATTR_VOLUME 0x0008
> +#define ATTR_SUBDIR 0x0010
> +#define ATTR_ARCHIVE 0x0020
> +#define ATTR_EXTEND (ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM | \
> + ATTR_VOLUME) /* 0x000F */
> +
> +#define ATTR_EXTEND_MASK (ATTR_EXTEND | ATTR_SUBDIR | ATTR_ARCHIVE)
> +#define ATTR_RWMASK (ATTR_HIDDEN | ATTR_SYSTEM | ATTR_VOLUME | \
> + ATTR_SUBDIR | ATTR_ARCHIVE)
> +
> +#define ATTR_READONLY_LE cpu_to_le16(0x0001)
> +#define ATTR_HIDDEN_LE cpu_to_le16(0x0002)
> +#define ATTR_SYSTEM_LE cpu_to_le16(0x0004)
> +#define ATTR_VOLUME_LE cpu_to_le16(0x0008)
> +#define ATTR_SUBDIR_LE cpu_to_le16(0x0010)
> +#define ATTR_ARCHIVE_LE cpu_to_le16(0x0020)
> +
> +/* EXFAT BIOS parameter block (64 bytes) */
> +struct bpb64 {
> + __u8 jmp_boot[3];
> + __u8 oem_name[8];
> + __u8 res_zero[53];
> +};
> +
> +/* EXFAT EXTEND BIOS parameter block (56 bytes) */
> +struct bsx64 {
> + __le64 vol_offset;
> + __le64 vol_length;
> + __le32 fat_offset;
> + __le32 fat_length;
> + __le32 clu_offset;
> + __le32 clu_count;
> + __le32 root_cluster;
> + __le32 vol_serial;
> + __u8 fs_version[2];
> + __le16 vol_flags;
> + __u8 sect_size_bits;
> + __u8 sect_per_clus_bits;
> + __u8 num_fats;
> + __u8 phy_drv_no;
> + __u8 perc_in_use;
> + __u8 reserved2[7];
> +};
> +
> +/* EXFAT PBR[BPB+BSX] (120 bytes) */
> +struct pbr64 {
> + struct bpb64 bpb;
> + struct bsx64 bsx;
> +};
> +
> +/* Common PBR[Partition Boot Record] (512 bytes) */
> +struct pbr {
> + union {
> + __u8 raw[64];
> + struct bpb64 f64;
> + } bpb;
> + union {
> + __u8 raw[56];
> + struct bsx64 f64;
> + } bsx;
> + __u8 boot_code[390];
> + __le16 signature;
> +};
> +
> +struct exfat_dentry {
> + __u8 type;
> + union {
> + struct {
> + __u8 num_ext;
> + __le16 checksum;
> + __le16 attr;
> + __le16 reserved1;
> + __le16 create_time;
> + __le16 create_date;
> + __le16 modify_time;
> + __le16 modify_date;
> + __le16 access_time;
> + __le16 access_date;
> + __u8 create_time_ms;
> + __u8 modify_time_ms;
> + __u8 access_time_ms;
> + __u8 reserved2[9];
> + } __packed file; /* file directory entry */
> + struct {
> + __u8 flags;
> + __u8 reserved1;
> + __u8 name_len;
> + __le16 name_hash;
> + __le16 reserved2;
> + __le64 valid_size;
> + __le32 reserved3;
> + __le32 start_clu;
> + __le64 size;
> + } __packed stream; /* stream extension directory entry */
> + struct {
> + __u8 flags;
> + __le16 unicode_0_14[15];
> + } __packed name; /* file name directory entry */
> + struct {
> + __u8 flags;
> + __u8 reserved[18];
> + __le32 start_clu;
> + __le64 size;
> + } __packed bitmap; /* allocation bitmap directory entry */
> + struct {
> + __u8 reserved1[3];
> + __le32 checksum;
> + __u8 reserved2[12];
> + __le32 start_clu;
> + __le64 size;
> + } __packed upcase; /* up-case table directory entry */
> + } __packed dentry;
> +} __packed;
> +
> +#define file_num_ext dentry.file.num_ext
> +#define file_checksum dentry.file.checksum
> +#define file_attr dentry.file.attr
> +#define file_create_time dentry.file.create_time
> +#define file_create_date dentry.file.create_date
> +#define file_modify_time dentry.file.modify_time
> +#define file_modify_date dentry.file.modify_date
> +#define file_access_time dentry.file.access_time
> +#define file_access_date dentry.file.access_date
> +#define file_create_time_ms dentry.file.create_time_ms
> +#define file_modify_time_ms dentry.file.modify_time_ms
> +#define file_access_time_ms dentry.file.access_time_ms
> +#define stream_flags dentry.stream.flags
> +#define stream_name_len dentry.stream.name_len
> +#define stream_name_hash dentry.stream.name_hash
> +#define stream_start_clu dentry.stream.start_clu
> +#define stream_valid_size dentry.stream.valid_size
> +#define stream_size dentry.stream.size
> +#define name_flags dentry.name.flags
> +#define name_unicode dentry.name.unicode_0_14
> +#define bitmap_flags dentry.bitmap.flags
> +#define bitmap_start_clu dentry.bitmap.start_clu
> +#define bitmap_size dentry.bitmap.size
> +#define upcase_start_clu dentry.upcase.start_clu
> +#define upcase_size dentry.upcase.size
> +#define upcase_checksum dentry.upcase.checksum
> +
> +#endif /* !_EXFAT_RAW_H */
>