162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  linux/fs/fat/inode.c
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci *  Written 1992,1993 by Werner Almesberger
662306a36Sopenharmony_ci *  VFAT extensions by Gordon Chaffee, merged with msdos fs by Henrik Storner
762306a36Sopenharmony_ci *  Rewritten for the constant inumbers support by Al Viro
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci *  Fixes:
1062306a36Sopenharmony_ci *
1162306a36Sopenharmony_ci *	Max Cohan: Fixed invalid FSINFO offset when info_sector is 0
1262306a36Sopenharmony_ci */
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#include <linux/module.h>
1562306a36Sopenharmony_ci#include <linux/pagemap.h>
1662306a36Sopenharmony_ci#include <linux/mpage.h>
1762306a36Sopenharmony_ci#include <linux/vfs.h>
1862306a36Sopenharmony_ci#include <linux/seq_file.h>
1962306a36Sopenharmony_ci#include <linux/parser.h>
2062306a36Sopenharmony_ci#include <linux/uio.h>
2162306a36Sopenharmony_ci#include <linux/blkdev.h>
2262306a36Sopenharmony_ci#include <linux/backing-dev.h>
2362306a36Sopenharmony_ci#include <asm/unaligned.h>
2462306a36Sopenharmony_ci#include <linux/random.h>
2562306a36Sopenharmony_ci#include <linux/iversion.h>
2662306a36Sopenharmony_ci#include "fat.h"
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci#ifndef CONFIG_FAT_DEFAULT_IOCHARSET
2962306a36Sopenharmony_ci/* if user don't select VFAT, this is undefined. */
3062306a36Sopenharmony_ci#define CONFIG_FAT_DEFAULT_IOCHARSET	""
3162306a36Sopenharmony_ci#endif
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci#define KB_IN_SECTORS 2
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci/* DOS dates from 1980/1/1 through 2107/12/31 */
3662306a36Sopenharmony_ci#define FAT_DATE_MIN (0<<9 | 1<<5 | 1)
3762306a36Sopenharmony_ci#define FAT_DATE_MAX (127<<9 | 12<<5 | 31)
3862306a36Sopenharmony_ci#define FAT_TIME_MAX (23<<11 | 59<<5 | 29)
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci/*
4162306a36Sopenharmony_ci * A deserialized copy of the on-disk structure laid out in struct
4262306a36Sopenharmony_ci * fat_boot_sector.
4362306a36Sopenharmony_ci */
4462306a36Sopenharmony_cistruct fat_bios_param_block {
4562306a36Sopenharmony_ci	u16	fat_sector_size;
4662306a36Sopenharmony_ci	u8	fat_sec_per_clus;
4762306a36Sopenharmony_ci	u16	fat_reserved;
4862306a36Sopenharmony_ci	u8	fat_fats;
4962306a36Sopenharmony_ci	u16	fat_dir_entries;
5062306a36Sopenharmony_ci	u16	fat_sectors;
5162306a36Sopenharmony_ci	u16	fat_fat_length;
5262306a36Sopenharmony_ci	u32	fat_total_sect;
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	u8	fat16_state;
5562306a36Sopenharmony_ci	u32	fat16_vol_id;
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci	u32	fat32_length;
5862306a36Sopenharmony_ci	u32	fat32_root_cluster;
5962306a36Sopenharmony_ci	u16	fat32_info_sector;
6062306a36Sopenharmony_ci	u8	fat32_state;
6162306a36Sopenharmony_ci	u32	fat32_vol_id;
6262306a36Sopenharmony_ci};
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_cistatic int fat_default_codepage = CONFIG_FAT_DEFAULT_CODEPAGE;
6562306a36Sopenharmony_cistatic char fat_default_iocharset[] = CONFIG_FAT_DEFAULT_IOCHARSET;
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_cistatic struct fat_floppy_defaults {
6862306a36Sopenharmony_ci	unsigned nr_sectors;
6962306a36Sopenharmony_ci	unsigned sec_per_clus;
7062306a36Sopenharmony_ci	unsigned dir_entries;
7162306a36Sopenharmony_ci	unsigned media;
7262306a36Sopenharmony_ci	unsigned fat_length;
7362306a36Sopenharmony_ci} floppy_defaults[] = {
7462306a36Sopenharmony_ci{
7562306a36Sopenharmony_ci	.nr_sectors = 160 * KB_IN_SECTORS,
7662306a36Sopenharmony_ci	.sec_per_clus = 1,
7762306a36Sopenharmony_ci	.dir_entries = 64,
7862306a36Sopenharmony_ci	.media = 0xFE,
7962306a36Sopenharmony_ci	.fat_length = 1,
8062306a36Sopenharmony_ci},
8162306a36Sopenharmony_ci{
8262306a36Sopenharmony_ci	.nr_sectors = 180 * KB_IN_SECTORS,
8362306a36Sopenharmony_ci	.sec_per_clus = 1,
8462306a36Sopenharmony_ci	.dir_entries = 64,
8562306a36Sopenharmony_ci	.media = 0xFC,
8662306a36Sopenharmony_ci	.fat_length = 2,
8762306a36Sopenharmony_ci},
8862306a36Sopenharmony_ci{
8962306a36Sopenharmony_ci	.nr_sectors = 320 * KB_IN_SECTORS,
9062306a36Sopenharmony_ci	.sec_per_clus = 2,
9162306a36Sopenharmony_ci	.dir_entries = 112,
9262306a36Sopenharmony_ci	.media = 0xFF,
9362306a36Sopenharmony_ci	.fat_length = 1,
9462306a36Sopenharmony_ci},
9562306a36Sopenharmony_ci{
9662306a36Sopenharmony_ci	.nr_sectors = 360 * KB_IN_SECTORS,
9762306a36Sopenharmony_ci	.sec_per_clus = 2,
9862306a36Sopenharmony_ci	.dir_entries = 112,
9962306a36Sopenharmony_ci	.media = 0xFD,
10062306a36Sopenharmony_ci	.fat_length = 2,
10162306a36Sopenharmony_ci},
10262306a36Sopenharmony_ci};
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ciint fat_add_cluster(struct inode *inode)
10562306a36Sopenharmony_ci{
10662306a36Sopenharmony_ci	int err, cluster;
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	err = fat_alloc_clusters(inode, &cluster, 1);
10962306a36Sopenharmony_ci	if (err)
11062306a36Sopenharmony_ci		return err;
11162306a36Sopenharmony_ci	/* FIXME: this cluster should be added after data of this
11262306a36Sopenharmony_ci	 * cluster is writed */
11362306a36Sopenharmony_ci	err = fat_chain_add(inode, cluster, 1);
11462306a36Sopenharmony_ci	if (err)
11562306a36Sopenharmony_ci		fat_free_clusters(inode, cluster);
11662306a36Sopenharmony_ci	return err;
11762306a36Sopenharmony_ci}
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_cistatic inline int __fat_get_block(struct inode *inode, sector_t iblock,
12062306a36Sopenharmony_ci				  unsigned long *max_blocks,
12162306a36Sopenharmony_ci				  struct buffer_head *bh_result, int create)
12262306a36Sopenharmony_ci{
12362306a36Sopenharmony_ci	struct super_block *sb = inode->i_sb;
12462306a36Sopenharmony_ci	struct msdos_sb_info *sbi = MSDOS_SB(sb);
12562306a36Sopenharmony_ci	unsigned long mapped_blocks;
12662306a36Sopenharmony_ci	sector_t phys, last_block;
12762306a36Sopenharmony_ci	int err, offset;
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	err = fat_bmap(inode, iblock, &phys, &mapped_blocks, create, false);
13062306a36Sopenharmony_ci	if (err)
13162306a36Sopenharmony_ci		return err;
13262306a36Sopenharmony_ci	if (phys) {
13362306a36Sopenharmony_ci		map_bh(bh_result, sb, phys);
13462306a36Sopenharmony_ci		*max_blocks = min(mapped_blocks, *max_blocks);
13562306a36Sopenharmony_ci		return 0;
13662306a36Sopenharmony_ci	}
13762306a36Sopenharmony_ci	if (!create)
13862306a36Sopenharmony_ci		return 0;
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	if (iblock != MSDOS_I(inode)->mmu_private >> sb->s_blocksize_bits) {
14162306a36Sopenharmony_ci		fat_fs_error(sb, "corrupted file size (i_pos %lld, %lld)",
14262306a36Sopenharmony_ci			MSDOS_I(inode)->i_pos, MSDOS_I(inode)->mmu_private);
14362306a36Sopenharmony_ci		return -EIO;
14462306a36Sopenharmony_ci	}
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	last_block = inode->i_blocks >> (sb->s_blocksize_bits - 9);
14762306a36Sopenharmony_ci	offset = (unsigned long)iblock & (sbi->sec_per_clus - 1);
14862306a36Sopenharmony_ci	/*
14962306a36Sopenharmony_ci	 * allocate a cluster according to the following.
15062306a36Sopenharmony_ci	 * 1) no more available blocks
15162306a36Sopenharmony_ci	 * 2) not part of fallocate region
15262306a36Sopenharmony_ci	 */
15362306a36Sopenharmony_ci	if (!offset && !(iblock < last_block)) {
15462306a36Sopenharmony_ci		/* TODO: multiple cluster allocation would be desirable. */
15562306a36Sopenharmony_ci		err = fat_add_cluster(inode);
15662306a36Sopenharmony_ci		if (err)
15762306a36Sopenharmony_ci			return err;
15862306a36Sopenharmony_ci	}
15962306a36Sopenharmony_ci	/* available blocks on this cluster */
16062306a36Sopenharmony_ci	mapped_blocks = sbi->sec_per_clus - offset;
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	*max_blocks = min(mapped_blocks, *max_blocks);
16362306a36Sopenharmony_ci	MSDOS_I(inode)->mmu_private += *max_blocks << sb->s_blocksize_bits;
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	err = fat_bmap(inode, iblock, &phys, &mapped_blocks, create, false);
16662306a36Sopenharmony_ci	if (err)
16762306a36Sopenharmony_ci		return err;
16862306a36Sopenharmony_ci	if (!phys) {
16962306a36Sopenharmony_ci		fat_fs_error(sb,
17062306a36Sopenharmony_ci			     "invalid FAT chain (i_pos %lld, last_block %llu)",
17162306a36Sopenharmony_ci			     MSDOS_I(inode)->i_pos,
17262306a36Sopenharmony_ci			     (unsigned long long)last_block);
17362306a36Sopenharmony_ci		return -EIO;
17462306a36Sopenharmony_ci	}
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	BUG_ON(*max_blocks != mapped_blocks);
17762306a36Sopenharmony_ci	set_buffer_new(bh_result);
17862306a36Sopenharmony_ci	map_bh(bh_result, sb, phys);
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	return 0;
18162306a36Sopenharmony_ci}
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_cistatic int fat_get_block(struct inode *inode, sector_t iblock,
18462306a36Sopenharmony_ci			 struct buffer_head *bh_result, int create)
18562306a36Sopenharmony_ci{
18662306a36Sopenharmony_ci	struct super_block *sb = inode->i_sb;
18762306a36Sopenharmony_ci	unsigned long max_blocks = bh_result->b_size >> inode->i_blkbits;
18862306a36Sopenharmony_ci	int err;
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci	err = __fat_get_block(inode, iblock, &max_blocks, bh_result, create);
19162306a36Sopenharmony_ci	if (err)
19262306a36Sopenharmony_ci		return err;
19362306a36Sopenharmony_ci	bh_result->b_size = max_blocks << sb->s_blocksize_bits;
19462306a36Sopenharmony_ci	return 0;
19562306a36Sopenharmony_ci}
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_cistatic int fat_writepages(struct address_space *mapping,
19862306a36Sopenharmony_ci			  struct writeback_control *wbc)
19962306a36Sopenharmony_ci{
20062306a36Sopenharmony_ci	return mpage_writepages(mapping, wbc, fat_get_block);
20162306a36Sopenharmony_ci}
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_cistatic int fat_read_folio(struct file *file, struct folio *folio)
20462306a36Sopenharmony_ci{
20562306a36Sopenharmony_ci	return mpage_read_folio(folio, fat_get_block);
20662306a36Sopenharmony_ci}
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_cistatic void fat_readahead(struct readahead_control *rac)
20962306a36Sopenharmony_ci{
21062306a36Sopenharmony_ci	mpage_readahead(rac, fat_get_block);
21162306a36Sopenharmony_ci}
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_cistatic void fat_write_failed(struct address_space *mapping, loff_t to)
21462306a36Sopenharmony_ci{
21562306a36Sopenharmony_ci	struct inode *inode = mapping->host;
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	if (to > inode->i_size) {
21862306a36Sopenharmony_ci		truncate_pagecache(inode, inode->i_size);
21962306a36Sopenharmony_ci		fat_truncate_blocks(inode, inode->i_size);
22062306a36Sopenharmony_ci	}
22162306a36Sopenharmony_ci}
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_cistatic int fat_write_begin(struct file *file, struct address_space *mapping,
22462306a36Sopenharmony_ci			loff_t pos, unsigned len,
22562306a36Sopenharmony_ci			struct page **pagep, void **fsdata)
22662306a36Sopenharmony_ci{
22762306a36Sopenharmony_ci	int err;
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci	*pagep = NULL;
23062306a36Sopenharmony_ci	err = cont_write_begin(file, mapping, pos, len,
23162306a36Sopenharmony_ci				pagep, fsdata, fat_get_block,
23262306a36Sopenharmony_ci				&MSDOS_I(mapping->host)->mmu_private);
23362306a36Sopenharmony_ci	if (err < 0)
23462306a36Sopenharmony_ci		fat_write_failed(mapping, pos + len);
23562306a36Sopenharmony_ci	return err;
23662306a36Sopenharmony_ci}
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_cistatic int fat_write_end(struct file *file, struct address_space *mapping,
23962306a36Sopenharmony_ci			loff_t pos, unsigned len, unsigned copied,
24062306a36Sopenharmony_ci			struct page *pagep, void *fsdata)
24162306a36Sopenharmony_ci{
24262306a36Sopenharmony_ci	struct inode *inode = mapping->host;
24362306a36Sopenharmony_ci	int err;
24462306a36Sopenharmony_ci	err = generic_write_end(file, mapping, pos, len, copied, pagep, fsdata);
24562306a36Sopenharmony_ci	if (err < len)
24662306a36Sopenharmony_ci		fat_write_failed(mapping, pos + len);
24762306a36Sopenharmony_ci	if (!(err < 0) && !(MSDOS_I(inode)->i_attrs & ATTR_ARCH)) {
24862306a36Sopenharmony_ci		fat_truncate_time(inode, NULL, S_CTIME|S_MTIME);
24962306a36Sopenharmony_ci		MSDOS_I(inode)->i_attrs |= ATTR_ARCH;
25062306a36Sopenharmony_ci		mark_inode_dirty(inode);
25162306a36Sopenharmony_ci	}
25262306a36Sopenharmony_ci	return err;
25362306a36Sopenharmony_ci}
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_cistatic ssize_t fat_direct_IO(struct kiocb *iocb, struct iov_iter *iter)
25662306a36Sopenharmony_ci{
25762306a36Sopenharmony_ci	struct file *file = iocb->ki_filp;
25862306a36Sopenharmony_ci	struct address_space *mapping = file->f_mapping;
25962306a36Sopenharmony_ci	struct inode *inode = mapping->host;
26062306a36Sopenharmony_ci	size_t count = iov_iter_count(iter);
26162306a36Sopenharmony_ci	loff_t offset = iocb->ki_pos;
26262306a36Sopenharmony_ci	ssize_t ret;
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci	if (iov_iter_rw(iter) == WRITE) {
26562306a36Sopenharmony_ci		/*
26662306a36Sopenharmony_ci		 * FIXME: blockdev_direct_IO() doesn't use ->write_begin(),
26762306a36Sopenharmony_ci		 * so we need to update the ->mmu_private to block boundary.
26862306a36Sopenharmony_ci		 *
26962306a36Sopenharmony_ci		 * But we must fill the remaining area or hole by nul for
27062306a36Sopenharmony_ci		 * updating ->mmu_private.
27162306a36Sopenharmony_ci		 *
27262306a36Sopenharmony_ci		 * Return 0, and fallback to normal buffered write.
27362306a36Sopenharmony_ci		 */
27462306a36Sopenharmony_ci		loff_t size = offset + count;
27562306a36Sopenharmony_ci		if (MSDOS_I(inode)->mmu_private < size)
27662306a36Sopenharmony_ci			return 0;
27762306a36Sopenharmony_ci	}
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci	/*
28062306a36Sopenharmony_ci	 * FAT need to use the DIO_LOCKING for avoiding the race
28162306a36Sopenharmony_ci	 * condition of fat_get_block() and ->truncate().
28262306a36Sopenharmony_ci	 */
28362306a36Sopenharmony_ci	ret = blockdev_direct_IO(iocb, inode, iter, fat_get_block);
28462306a36Sopenharmony_ci	if (ret < 0 && iov_iter_rw(iter) == WRITE)
28562306a36Sopenharmony_ci		fat_write_failed(mapping, offset + count);
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci	return ret;
28862306a36Sopenharmony_ci}
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_cistatic int fat_get_block_bmap(struct inode *inode, sector_t iblock,
29162306a36Sopenharmony_ci		struct buffer_head *bh_result, int create)
29262306a36Sopenharmony_ci{
29362306a36Sopenharmony_ci	struct super_block *sb = inode->i_sb;
29462306a36Sopenharmony_ci	unsigned long max_blocks = bh_result->b_size >> inode->i_blkbits;
29562306a36Sopenharmony_ci	int err;
29662306a36Sopenharmony_ci	sector_t bmap;
29762306a36Sopenharmony_ci	unsigned long mapped_blocks;
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci	BUG_ON(create != 0);
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci	err = fat_bmap(inode, iblock, &bmap, &mapped_blocks, create, true);
30262306a36Sopenharmony_ci	if (err)
30362306a36Sopenharmony_ci		return err;
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci	if (bmap) {
30662306a36Sopenharmony_ci		map_bh(bh_result, sb, bmap);
30762306a36Sopenharmony_ci		max_blocks = min(mapped_blocks, max_blocks);
30862306a36Sopenharmony_ci	}
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci	bh_result->b_size = max_blocks << sb->s_blocksize_bits;
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	return 0;
31362306a36Sopenharmony_ci}
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_cistatic sector_t _fat_bmap(struct address_space *mapping, sector_t block)
31662306a36Sopenharmony_ci{
31762306a36Sopenharmony_ci	sector_t blocknr;
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci	/* fat_get_cluster() assumes the requested blocknr isn't truncated. */
32062306a36Sopenharmony_ci	down_read(&MSDOS_I(mapping->host)->truncate_lock);
32162306a36Sopenharmony_ci	blocknr = generic_block_bmap(mapping, block, fat_get_block_bmap);
32262306a36Sopenharmony_ci	up_read(&MSDOS_I(mapping->host)->truncate_lock);
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci	return blocknr;
32562306a36Sopenharmony_ci}
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci/*
32862306a36Sopenharmony_ci * fat_block_truncate_page() zeroes out a mapping from file offset `from'
32962306a36Sopenharmony_ci * up to the end of the block which corresponds to `from'.
33062306a36Sopenharmony_ci * This is required during truncate to physically zeroout the tail end
33162306a36Sopenharmony_ci * of that block so it doesn't yield old data if the file is later grown.
33262306a36Sopenharmony_ci * Also, avoid causing failure from fsx for cases of "data past EOF"
33362306a36Sopenharmony_ci */
33462306a36Sopenharmony_ciint fat_block_truncate_page(struct inode *inode, loff_t from)
33562306a36Sopenharmony_ci{
33662306a36Sopenharmony_ci	return block_truncate_page(inode->i_mapping, from, fat_get_block);
33762306a36Sopenharmony_ci}
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_cistatic const struct address_space_operations fat_aops = {
34062306a36Sopenharmony_ci	.dirty_folio	= block_dirty_folio,
34162306a36Sopenharmony_ci	.invalidate_folio = block_invalidate_folio,
34262306a36Sopenharmony_ci	.read_folio	= fat_read_folio,
34362306a36Sopenharmony_ci	.readahead	= fat_readahead,
34462306a36Sopenharmony_ci	.writepages	= fat_writepages,
34562306a36Sopenharmony_ci	.write_begin	= fat_write_begin,
34662306a36Sopenharmony_ci	.write_end	= fat_write_end,
34762306a36Sopenharmony_ci	.direct_IO	= fat_direct_IO,
34862306a36Sopenharmony_ci	.bmap		= _fat_bmap,
34962306a36Sopenharmony_ci	.migrate_folio	= buffer_migrate_folio,
35062306a36Sopenharmony_ci};
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci/*
35362306a36Sopenharmony_ci * New FAT inode stuff. We do the following:
35462306a36Sopenharmony_ci *	a) i_ino is constant and has nothing with on-disk location.
35562306a36Sopenharmony_ci *	b) FAT manages its own cache of directory entries.
35662306a36Sopenharmony_ci *	c) *This* cache is indexed by on-disk location.
35762306a36Sopenharmony_ci *	d) inode has an associated directory entry, all right, but
35862306a36Sopenharmony_ci *		it may be unhashed.
35962306a36Sopenharmony_ci *	e) currently entries are stored within struct inode. That should
36062306a36Sopenharmony_ci *		change.
36162306a36Sopenharmony_ci *	f) we deal with races in the following way:
36262306a36Sopenharmony_ci *		1. readdir() and lookup() do FAT-dir-cache lookup.
36362306a36Sopenharmony_ci *		2. rename() unhashes the F-d-c entry and rehashes it in
36462306a36Sopenharmony_ci *			a new place.
36562306a36Sopenharmony_ci *		3. unlink() and rmdir() unhash F-d-c entry.
36662306a36Sopenharmony_ci *		4. fat_write_inode() checks whether the thing is unhashed.
36762306a36Sopenharmony_ci *			If it is we silently return. If it isn't we do bread(),
36862306a36Sopenharmony_ci *			check if the location is still valid and retry if it
36962306a36Sopenharmony_ci *			isn't. Otherwise we do changes.
37062306a36Sopenharmony_ci *		5. Spinlock is used to protect hash/unhash/location check/lookup
37162306a36Sopenharmony_ci *		6. fat_evict_inode() unhashes the F-d-c entry.
37262306a36Sopenharmony_ci *		7. lookup() and readdir() do igrab() if they find a F-d-c entry
37362306a36Sopenharmony_ci *			and consider negative result as cache miss.
37462306a36Sopenharmony_ci */
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_cistatic void fat_hash_init(struct super_block *sb)
37762306a36Sopenharmony_ci{
37862306a36Sopenharmony_ci	struct msdos_sb_info *sbi = MSDOS_SB(sb);
37962306a36Sopenharmony_ci	int i;
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci	spin_lock_init(&sbi->inode_hash_lock);
38262306a36Sopenharmony_ci	for (i = 0; i < FAT_HASH_SIZE; i++)
38362306a36Sopenharmony_ci		INIT_HLIST_HEAD(&sbi->inode_hashtable[i]);
38462306a36Sopenharmony_ci}
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_cistatic inline unsigned long fat_hash(loff_t i_pos)
38762306a36Sopenharmony_ci{
38862306a36Sopenharmony_ci	return hash_32(i_pos, FAT_HASH_BITS);
38962306a36Sopenharmony_ci}
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_cistatic void dir_hash_init(struct super_block *sb)
39262306a36Sopenharmony_ci{
39362306a36Sopenharmony_ci	struct msdos_sb_info *sbi = MSDOS_SB(sb);
39462306a36Sopenharmony_ci	int i;
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci	spin_lock_init(&sbi->dir_hash_lock);
39762306a36Sopenharmony_ci	for (i = 0; i < FAT_HASH_SIZE; i++)
39862306a36Sopenharmony_ci		INIT_HLIST_HEAD(&sbi->dir_hashtable[i]);
39962306a36Sopenharmony_ci}
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_civoid fat_attach(struct inode *inode, loff_t i_pos)
40262306a36Sopenharmony_ci{
40362306a36Sopenharmony_ci	struct msdos_sb_info *sbi = MSDOS_SB(inode->i_sb);
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	if (inode->i_ino != MSDOS_ROOT_INO) {
40662306a36Sopenharmony_ci		struct hlist_head *head =   sbi->inode_hashtable
40762306a36Sopenharmony_ci					  + fat_hash(i_pos);
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci		spin_lock(&sbi->inode_hash_lock);
41062306a36Sopenharmony_ci		MSDOS_I(inode)->i_pos = i_pos;
41162306a36Sopenharmony_ci		hlist_add_head(&MSDOS_I(inode)->i_fat_hash, head);
41262306a36Sopenharmony_ci		spin_unlock(&sbi->inode_hash_lock);
41362306a36Sopenharmony_ci	}
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci	/* If NFS support is enabled, cache the mapping of start cluster
41662306a36Sopenharmony_ci	 * to directory inode. This is used during reconnection of
41762306a36Sopenharmony_ci	 * dentries to the filesystem root.
41862306a36Sopenharmony_ci	 */
41962306a36Sopenharmony_ci	if (S_ISDIR(inode->i_mode) && sbi->options.nfs) {
42062306a36Sopenharmony_ci		struct hlist_head *d_head = sbi->dir_hashtable;
42162306a36Sopenharmony_ci		d_head += fat_dir_hash(MSDOS_I(inode)->i_logstart);
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci		spin_lock(&sbi->dir_hash_lock);
42462306a36Sopenharmony_ci		hlist_add_head(&MSDOS_I(inode)->i_dir_hash, d_head);
42562306a36Sopenharmony_ci		spin_unlock(&sbi->dir_hash_lock);
42662306a36Sopenharmony_ci	}
42762306a36Sopenharmony_ci}
42862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(fat_attach);
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_civoid fat_detach(struct inode *inode)
43162306a36Sopenharmony_ci{
43262306a36Sopenharmony_ci	struct msdos_sb_info *sbi = MSDOS_SB(inode->i_sb);
43362306a36Sopenharmony_ci	spin_lock(&sbi->inode_hash_lock);
43462306a36Sopenharmony_ci	MSDOS_I(inode)->i_pos = 0;
43562306a36Sopenharmony_ci	hlist_del_init(&MSDOS_I(inode)->i_fat_hash);
43662306a36Sopenharmony_ci	spin_unlock(&sbi->inode_hash_lock);
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ci	if (S_ISDIR(inode->i_mode) && sbi->options.nfs) {
43962306a36Sopenharmony_ci		spin_lock(&sbi->dir_hash_lock);
44062306a36Sopenharmony_ci		hlist_del_init(&MSDOS_I(inode)->i_dir_hash);
44162306a36Sopenharmony_ci		spin_unlock(&sbi->dir_hash_lock);
44262306a36Sopenharmony_ci	}
44362306a36Sopenharmony_ci}
44462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(fat_detach);
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_cistruct inode *fat_iget(struct super_block *sb, loff_t i_pos)
44762306a36Sopenharmony_ci{
44862306a36Sopenharmony_ci	struct msdos_sb_info *sbi = MSDOS_SB(sb);
44962306a36Sopenharmony_ci	struct hlist_head *head = sbi->inode_hashtable + fat_hash(i_pos);
45062306a36Sopenharmony_ci	struct msdos_inode_info *i;
45162306a36Sopenharmony_ci	struct inode *inode = NULL;
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci	spin_lock(&sbi->inode_hash_lock);
45462306a36Sopenharmony_ci	hlist_for_each_entry(i, head, i_fat_hash) {
45562306a36Sopenharmony_ci		BUG_ON(i->vfs_inode.i_sb != sb);
45662306a36Sopenharmony_ci		if (i->i_pos != i_pos)
45762306a36Sopenharmony_ci			continue;
45862306a36Sopenharmony_ci		inode = igrab(&i->vfs_inode);
45962306a36Sopenharmony_ci		if (inode)
46062306a36Sopenharmony_ci			break;
46162306a36Sopenharmony_ci	}
46262306a36Sopenharmony_ci	spin_unlock(&sbi->inode_hash_lock);
46362306a36Sopenharmony_ci	return inode;
46462306a36Sopenharmony_ci}
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_cistatic int is_exec(unsigned char *extension)
46762306a36Sopenharmony_ci{
46862306a36Sopenharmony_ci	unsigned char exe_extensions[] = "EXECOMBAT", *walk;
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci	for (walk = exe_extensions; *walk; walk += 3)
47162306a36Sopenharmony_ci		if (!strncmp(extension, walk, 3))
47262306a36Sopenharmony_ci			return 1;
47362306a36Sopenharmony_ci	return 0;
47462306a36Sopenharmony_ci}
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_cistatic int fat_calc_dir_size(struct inode *inode)
47762306a36Sopenharmony_ci{
47862306a36Sopenharmony_ci	struct msdos_sb_info *sbi = MSDOS_SB(inode->i_sb);
47962306a36Sopenharmony_ci	int ret, fclus, dclus;
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ci	inode->i_size = 0;
48262306a36Sopenharmony_ci	if (MSDOS_I(inode)->i_start == 0)
48362306a36Sopenharmony_ci		return 0;
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci	ret = fat_get_cluster(inode, FAT_ENT_EOF, &fclus, &dclus);
48662306a36Sopenharmony_ci	if (ret < 0)
48762306a36Sopenharmony_ci		return ret;
48862306a36Sopenharmony_ci	inode->i_size = (fclus + 1) << sbi->cluster_bits;
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci	return 0;
49162306a36Sopenharmony_ci}
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_cistatic int fat_validate_dir(struct inode *dir)
49462306a36Sopenharmony_ci{
49562306a36Sopenharmony_ci	struct super_block *sb = dir->i_sb;
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci	if (dir->i_nlink < 2) {
49862306a36Sopenharmony_ci		/* Directory should have "."/".." entries at least. */
49962306a36Sopenharmony_ci		fat_fs_error(sb, "corrupted directory (invalid entries)");
50062306a36Sopenharmony_ci		return -EIO;
50162306a36Sopenharmony_ci	}
50262306a36Sopenharmony_ci	if (MSDOS_I(dir)->i_start == 0 ||
50362306a36Sopenharmony_ci	    MSDOS_I(dir)->i_start == MSDOS_SB(sb)->root_cluster) {
50462306a36Sopenharmony_ci		/* Directory should point valid cluster. */
50562306a36Sopenharmony_ci		fat_fs_error(sb, "corrupted directory (invalid i_start)");
50662306a36Sopenharmony_ci		return -EIO;
50762306a36Sopenharmony_ci	}
50862306a36Sopenharmony_ci	return 0;
50962306a36Sopenharmony_ci}
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_ci/* doesn't deal with root inode */
51262306a36Sopenharmony_ciint fat_fill_inode(struct inode *inode, struct msdos_dir_entry *de)
51362306a36Sopenharmony_ci{
51462306a36Sopenharmony_ci	struct msdos_sb_info *sbi = MSDOS_SB(inode->i_sb);
51562306a36Sopenharmony_ci	int error;
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci	MSDOS_I(inode)->i_pos = 0;
51862306a36Sopenharmony_ci	inode->i_uid = sbi->options.fs_uid;
51962306a36Sopenharmony_ci	inode->i_gid = sbi->options.fs_gid;
52062306a36Sopenharmony_ci	inode_inc_iversion(inode);
52162306a36Sopenharmony_ci	inode->i_generation = get_random_u32();
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci	if ((de->attr & ATTR_DIR) && !IS_FREE(de->name)) {
52462306a36Sopenharmony_ci		inode->i_generation &= ~1;
52562306a36Sopenharmony_ci		inode->i_mode = fat_make_mode(sbi, de->attr, S_IRWXUGO);
52662306a36Sopenharmony_ci		inode->i_op = sbi->dir_ops;
52762306a36Sopenharmony_ci		inode->i_fop = &fat_dir_operations;
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_ci		MSDOS_I(inode)->i_start = fat_get_start(sbi, de);
53062306a36Sopenharmony_ci		MSDOS_I(inode)->i_logstart = MSDOS_I(inode)->i_start;
53162306a36Sopenharmony_ci		error = fat_calc_dir_size(inode);
53262306a36Sopenharmony_ci		if (error < 0)
53362306a36Sopenharmony_ci			return error;
53462306a36Sopenharmony_ci		MSDOS_I(inode)->mmu_private = inode->i_size;
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci		set_nlink(inode, fat_subdirs(inode));
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_ci		error = fat_validate_dir(inode);
53962306a36Sopenharmony_ci		if (error < 0)
54062306a36Sopenharmony_ci			return error;
54162306a36Sopenharmony_ci	} else { /* not a directory */
54262306a36Sopenharmony_ci		inode->i_generation |= 1;
54362306a36Sopenharmony_ci		inode->i_mode = fat_make_mode(sbi, de->attr,
54462306a36Sopenharmony_ci			((sbi->options.showexec && !is_exec(de->name + 8))
54562306a36Sopenharmony_ci			 ? S_IRUGO|S_IWUGO : S_IRWXUGO));
54662306a36Sopenharmony_ci		MSDOS_I(inode)->i_start = fat_get_start(sbi, de);
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci		MSDOS_I(inode)->i_logstart = MSDOS_I(inode)->i_start;
54962306a36Sopenharmony_ci		inode->i_size = le32_to_cpu(de->size);
55062306a36Sopenharmony_ci		inode->i_op = &fat_file_inode_operations;
55162306a36Sopenharmony_ci		inode->i_fop = &fat_file_operations;
55262306a36Sopenharmony_ci		inode->i_mapping->a_ops = &fat_aops;
55362306a36Sopenharmony_ci		MSDOS_I(inode)->mmu_private = inode->i_size;
55462306a36Sopenharmony_ci	}
55562306a36Sopenharmony_ci	if (de->attr & ATTR_SYS) {
55662306a36Sopenharmony_ci		if (sbi->options.sys_immutable)
55762306a36Sopenharmony_ci			inode->i_flags |= S_IMMUTABLE;
55862306a36Sopenharmony_ci	}
55962306a36Sopenharmony_ci	fat_save_attrs(inode, de->attr);
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_ci	inode->i_blocks = ((inode->i_size + (sbi->cluster_size - 1))
56262306a36Sopenharmony_ci			   & ~((loff_t)sbi->cluster_size - 1)) >> 9;
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_ci	fat_time_fat2unix(sbi, &inode->i_mtime, de->time, de->date, 0);
56562306a36Sopenharmony_ci	inode_set_ctime_to_ts(inode, inode->i_mtime);
56662306a36Sopenharmony_ci	if (sbi->options.isvfat) {
56762306a36Sopenharmony_ci		fat_time_fat2unix(sbi, &inode->i_atime, 0, de->adate, 0);
56862306a36Sopenharmony_ci		fat_time_fat2unix(sbi, &MSDOS_I(inode)->i_crtime, de->ctime,
56962306a36Sopenharmony_ci				  de->cdate, de->ctime_cs);
57062306a36Sopenharmony_ci	} else
57162306a36Sopenharmony_ci		inode->i_atime = fat_truncate_atime(sbi, &inode->i_mtime);
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_ci	return 0;
57462306a36Sopenharmony_ci}
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_cistatic inline void fat_lock_build_inode(struct msdos_sb_info *sbi)
57762306a36Sopenharmony_ci{
57862306a36Sopenharmony_ci	if (sbi->options.nfs == FAT_NFS_NOSTALE_RO)
57962306a36Sopenharmony_ci		mutex_lock(&sbi->nfs_build_inode_lock);
58062306a36Sopenharmony_ci}
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_cistatic inline void fat_unlock_build_inode(struct msdos_sb_info *sbi)
58362306a36Sopenharmony_ci{
58462306a36Sopenharmony_ci	if (sbi->options.nfs == FAT_NFS_NOSTALE_RO)
58562306a36Sopenharmony_ci		mutex_unlock(&sbi->nfs_build_inode_lock);
58662306a36Sopenharmony_ci}
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_cistruct inode *fat_build_inode(struct super_block *sb,
58962306a36Sopenharmony_ci			struct msdos_dir_entry *de, loff_t i_pos)
59062306a36Sopenharmony_ci{
59162306a36Sopenharmony_ci	struct inode *inode;
59262306a36Sopenharmony_ci	int err;
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_ci	fat_lock_build_inode(MSDOS_SB(sb));
59562306a36Sopenharmony_ci	inode = fat_iget(sb, i_pos);
59662306a36Sopenharmony_ci	if (inode)
59762306a36Sopenharmony_ci		goto out;
59862306a36Sopenharmony_ci	inode = new_inode(sb);
59962306a36Sopenharmony_ci	if (!inode) {
60062306a36Sopenharmony_ci		inode = ERR_PTR(-ENOMEM);
60162306a36Sopenharmony_ci		goto out;
60262306a36Sopenharmony_ci	}
60362306a36Sopenharmony_ci	inode->i_ino = iunique(sb, MSDOS_ROOT_INO);
60462306a36Sopenharmony_ci	inode_set_iversion(inode, 1);
60562306a36Sopenharmony_ci	err = fat_fill_inode(inode, de);
60662306a36Sopenharmony_ci	if (err) {
60762306a36Sopenharmony_ci		iput(inode);
60862306a36Sopenharmony_ci		inode = ERR_PTR(err);
60962306a36Sopenharmony_ci		goto out;
61062306a36Sopenharmony_ci	}
61162306a36Sopenharmony_ci	fat_attach(inode, i_pos);
61262306a36Sopenharmony_ci	insert_inode_hash(inode);
61362306a36Sopenharmony_ciout:
61462306a36Sopenharmony_ci	fat_unlock_build_inode(MSDOS_SB(sb));
61562306a36Sopenharmony_ci	return inode;
61662306a36Sopenharmony_ci}
61762306a36Sopenharmony_ci
61862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(fat_build_inode);
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_cistatic int __fat_write_inode(struct inode *inode, int wait);
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_cistatic void fat_free_eofblocks(struct inode *inode)
62362306a36Sopenharmony_ci{
62462306a36Sopenharmony_ci	/* Release unwritten fallocated blocks on inode eviction. */
62562306a36Sopenharmony_ci	if ((inode->i_blocks << 9) >
62662306a36Sopenharmony_ci			round_up(MSDOS_I(inode)->mmu_private,
62762306a36Sopenharmony_ci				MSDOS_SB(inode->i_sb)->cluster_size)) {
62862306a36Sopenharmony_ci		int err;
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_ci		fat_truncate_blocks(inode, MSDOS_I(inode)->mmu_private);
63162306a36Sopenharmony_ci		/* Fallocate results in updating the i_start/iogstart
63262306a36Sopenharmony_ci		 * for the zero byte file. So, make it return to
63362306a36Sopenharmony_ci		 * original state during evict and commit it to avoid
63462306a36Sopenharmony_ci		 * any corruption on the next access to the cluster
63562306a36Sopenharmony_ci		 * chain for the file.
63662306a36Sopenharmony_ci		 */
63762306a36Sopenharmony_ci		err = __fat_write_inode(inode, inode_needs_sync(inode));
63862306a36Sopenharmony_ci		if (err) {
63962306a36Sopenharmony_ci			fat_msg(inode->i_sb, KERN_WARNING, "Failed to "
64062306a36Sopenharmony_ci					"update on disk inode for unused "
64162306a36Sopenharmony_ci					"fallocated blocks, inode could be "
64262306a36Sopenharmony_ci					"corrupted. Please run fsck");
64362306a36Sopenharmony_ci		}
64462306a36Sopenharmony_ci
64562306a36Sopenharmony_ci	}
64662306a36Sopenharmony_ci}
64762306a36Sopenharmony_ci
64862306a36Sopenharmony_cistatic void fat_evict_inode(struct inode *inode)
64962306a36Sopenharmony_ci{
65062306a36Sopenharmony_ci	truncate_inode_pages_final(&inode->i_data);
65162306a36Sopenharmony_ci	if (!inode->i_nlink) {
65262306a36Sopenharmony_ci		inode->i_size = 0;
65362306a36Sopenharmony_ci		fat_truncate_blocks(inode, 0);
65462306a36Sopenharmony_ci	} else
65562306a36Sopenharmony_ci		fat_free_eofblocks(inode);
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_ci	invalidate_inode_buffers(inode);
65862306a36Sopenharmony_ci	clear_inode(inode);
65962306a36Sopenharmony_ci	fat_cache_inval_inode(inode);
66062306a36Sopenharmony_ci	fat_detach(inode);
66162306a36Sopenharmony_ci}
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_cistatic void fat_set_state(struct super_block *sb,
66462306a36Sopenharmony_ci			unsigned int set, unsigned int force)
66562306a36Sopenharmony_ci{
66662306a36Sopenharmony_ci	struct buffer_head *bh;
66762306a36Sopenharmony_ci	struct fat_boot_sector *b;
66862306a36Sopenharmony_ci	struct msdos_sb_info *sbi = MSDOS_SB(sb);
66962306a36Sopenharmony_ci
67062306a36Sopenharmony_ci	/* do not change any thing if mounted read only */
67162306a36Sopenharmony_ci	if (sb_rdonly(sb) && !force)
67262306a36Sopenharmony_ci		return;
67362306a36Sopenharmony_ci
67462306a36Sopenharmony_ci	/* do not change state if fs was dirty */
67562306a36Sopenharmony_ci	if (sbi->dirty) {
67662306a36Sopenharmony_ci		/* warn only on set (mount). */
67762306a36Sopenharmony_ci		if (set)
67862306a36Sopenharmony_ci			fat_msg(sb, KERN_WARNING, "Volume was not properly "
67962306a36Sopenharmony_ci				"unmounted. Some data may be corrupt. "
68062306a36Sopenharmony_ci				"Please run fsck.");
68162306a36Sopenharmony_ci		return;
68262306a36Sopenharmony_ci	}
68362306a36Sopenharmony_ci
68462306a36Sopenharmony_ci	bh = sb_bread(sb, 0);
68562306a36Sopenharmony_ci	if (bh == NULL) {
68662306a36Sopenharmony_ci		fat_msg(sb, KERN_ERR, "unable to read boot sector "
68762306a36Sopenharmony_ci			"to mark fs as dirty");
68862306a36Sopenharmony_ci		return;
68962306a36Sopenharmony_ci	}
69062306a36Sopenharmony_ci
69162306a36Sopenharmony_ci	b = (struct fat_boot_sector *) bh->b_data;
69262306a36Sopenharmony_ci
69362306a36Sopenharmony_ci	if (is_fat32(sbi)) {
69462306a36Sopenharmony_ci		if (set)
69562306a36Sopenharmony_ci			b->fat32.state |= FAT_STATE_DIRTY;
69662306a36Sopenharmony_ci		else
69762306a36Sopenharmony_ci			b->fat32.state &= ~FAT_STATE_DIRTY;
69862306a36Sopenharmony_ci	} else /* fat 16 and 12 */ {
69962306a36Sopenharmony_ci		if (set)
70062306a36Sopenharmony_ci			b->fat16.state |= FAT_STATE_DIRTY;
70162306a36Sopenharmony_ci		else
70262306a36Sopenharmony_ci			b->fat16.state &= ~FAT_STATE_DIRTY;
70362306a36Sopenharmony_ci	}
70462306a36Sopenharmony_ci
70562306a36Sopenharmony_ci	mark_buffer_dirty(bh);
70662306a36Sopenharmony_ci	sync_dirty_buffer(bh);
70762306a36Sopenharmony_ci	brelse(bh);
70862306a36Sopenharmony_ci}
70962306a36Sopenharmony_ci
71062306a36Sopenharmony_cistatic void fat_reset_iocharset(struct fat_mount_options *opts)
71162306a36Sopenharmony_ci{
71262306a36Sopenharmony_ci	if (opts->iocharset != fat_default_iocharset) {
71362306a36Sopenharmony_ci		/* Note: opts->iocharset can be NULL here */
71462306a36Sopenharmony_ci		kfree(opts->iocharset);
71562306a36Sopenharmony_ci		opts->iocharset = fat_default_iocharset;
71662306a36Sopenharmony_ci	}
71762306a36Sopenharmony_ci}
71862306a36Sopenharmony_ci
71962306a36Sopenharmony_cistatic void delayed_free(struct rcu_head *p)
72062306a36Sopenharmony_ci{
72162306a36Sopenharmony_ci	struct msdos_sb_info *sbi = container_of(p, struct msdos_sb_info, rcu);
72262306a36Sopenharmony_ci	unload_nls(sbi->nls_disk);
72362306a36Sopenharmony_ci	unload_nls(sbi->nls_io);
72462306a36Sopenharmony_ci	fat_reset_iocharset(&sbi->options);
72562306a36Sopenharmony_ci	kfree(sbi);
72662306a36Sopenharmony_ci}
72762306a36Sopenharmony_ci
72862306a36Sopenharmony_cistatic void fat_put_super(struct super_block *sb)
72962306a36Sopenharmony_ci{
73062306a36Sopenharmony_ci	struct msdos_sb_info *sbi = MSDOS_SB(sb);
73162306a36Sopenharmony_ci
73262306a36Sopenharmony_ci	fat_set_state(sb, 0, 0);
73362306a36Sopenharmony_ci
73462306a36Sopenharmony_ci	iput(sbi->fsinfo_inode);
73562306a36Sopenharmony_ci	iput(sbi->fat_inode);
73662306a36Sopenharmony_ci
73762306a36Sopenharmony_ci	call_rcu(&sbi->rcu, delayed_free);
73862306a36Sopenharmony_ci}
73962306a36Sopenharmony_ci
74062306a36Sopenharmony_cistatic struct kmem_cache *fat_inode_cachep;
74162306a36Sopenharmony_ci
74262306a36Sopenharmony_cistatic struct inode *fat_alloc_inode(struct super_block *sb)
74362306a36Sopenharmony_ci{
74462306a36Sopenharmony_ci	struct msdos_inode_info *ei;
74562306a36Sopenharmony_ci	ei = alloc_inode_sb(sb, fat_inode_cachep, GFP_NOFS);
74662306a36Sopenharmony_ci	if (!ei)
74762306a36Sopenharmony_ci		return NULL;
74862306a36Sopenharmony_ci
74962306a36Sopenharmony_ci	init_rwsem(&ei->truncate_lock);
75062306a36Sopenharmony_ci	/* Zeroing to allow iput() even if partial initialized inode. */
75162306a36Sopenharmony_ci	ei->mmu_private = 0;
75262306a36Sopenharmony_ci	ei->i_start = 0;
75362306a36Sopenharmony_ci	ei->i_logstart = 0;
75462306a36Sopenharmony_ci	ei->i_attrs = 0;
75562306a36Sopenharmony_ci	ei->i_pos = 0;
75662306a36Sopenharmony_ci	ei->i_crtime.tv_sec = 0;
75762306a36Sopenharmony_ci	ei->i_crtime.tv_nsec = 0;
75862306a36Sopenharmony_ci
75962306a36Sopenharmony_ci	return &ei->vfs_inode;
76062306a36Sopenharmony_ci}
76162306a36Sopenharmony_ci
76262306a36Sopenharmony_cistatic void fat_free_inode(struct inode *inode)
76362306a36Sopenharmony_ci{
76462306a36Sopenharmony_ci	kmem_cache_free(fat_inode_cachep, MSDOS_I(inode));
76562306a36Sopenharmony_ci}
76662306a36Sopenharmony_ci
76762306a36Sopenharmony_cistatic void init_once(void *foo)
76862306a36Sopenharmony_ci{
76962306a36Sopenharmony_ci	struct msdos_inode_info *ei = (struct msdos_inode_info *)foo;
77062306a36Sopenharmony_ci
77162306a36Sopenharmony_ci	spin_lock_init(&ei->cache_lru_lock);
77262306a36Sopenharmony_ci	ei->nr_caches = 0;
77362306a36Sopenharmony_ci	ei->cache_valid_id = FAT_CACHE_VALID + 1;
77462306a36Sopenharmony_ci	INIT_LIST_HEAD(&ei->cache_lru);
77562306a36Sopenharmony_ci	INIT_HLIST_NODE(&ei->i_fat_hash);
77662306a36Sopenharmony_ci	INIT_HLIST_NODE(&ei->i_dir_hash);
77762306a36Sopenharmony_ci	inode_init_once(&ei->vfs_inode);
77862306a36Sopenharmony_ci}
77962306a36Sopenharmony_ci
78062306a36Sopenharmony_cistatic int __init fat_init_inodecache(void)
78162306a36Sopenharmony_ci{
78262306a36Sopenharmony_ci	fat_inode_cachep = kmem_cache_create("fat_inode_cache",
78362306a36Sopenharmony_ci					     sizeof(struct msdos_inode_info),
78462306a36Sopenharmony_ci					     0, (SLAB_RECLAIM_ACCOUNT|
78562306a36Sopenharmony_ci						SLAB_MEM_SPREAD|SLAB_ACCOUNT),
78662306a36Sopenharmony_ci					     init_once);
78762306a36Sopenharmony_ci	if (fat_inode_cachep == NULL)
78862306a36Sopenharmony_ci		return -ENOMEM;
78962306a36Sopenharmony_ci	return 0;
79062306a36Sopenharmony_ci}
79162306a36Sopenharmony_ci
79262306a36Sopenharmony_cistatic void __exit fat_destroy_inodecache(void)
79362306a36Sopenharmony_ci{
79462306a36Sopenharmony_ci	/*
79562306a36Sopenharmony_ci	 * Make sure all delayed rcu free inodes are flushed before we
79662306a36Sopenharmony_ci	 * destroy cache.
79762306a36Sopenharmony_ci	 */
79862306a36Sopenharmony_ci	rcu_barrier();
79962306a36Sopenharmony_ci	kmem_cache_destroy(fat_inode_cachep);
80062306a36Sopenharmony_ci}
80162306a36Sopenharmony_ci
80262306a36Sopenharmony_cistatic int fat_remount(struct super_block *sb, int *flags, char *data)
80362306a36Sopenharmony_ci{
80462306a36Sopenharmony_ci	bool new_rdonly;
80562306a36Sopenharmony_ci	struct msdos_sb_info *sbi = MSDOS_SB(sb);
80662306a36Sopenharmony_ci	*flags |= SB_NODIRATIME | (sbi->options.isvfat ? 0 : SB_NOATIME);
80762306a36Sopenharmony_ci
80862306a36Sopenharmony_ci	sync_filesystem(sb);
80962306a36Sopenharmony_ci
81062306a36Sopenharmony_ci	/* make sure we update state on remount. */
81162306a36Sopenharmony_ci	new_rdonly = *flags & SB_RDONLY;
81262306a36Sopenharmony_ci	if (new_rdonly != sb_rdonly(sb)) {
81362306a36Sopenharmony_ci		if (new_rdonly)
81462306a36Sopenharmony_ci			fat_set_state(sb, 0, 0);
81562306a36Sopenharmony_ci		else
81662306a36Sopenharmony_ci			fat_set_state(sb, 1, 1);
81762306a36Sopenharmony_ci	}
81862306a36Sopenharmony_ci	return 0;
81962306a36Sopenharmony_ci}
82062306a36Sopenharmony_ci
82162306a36Sopenharmony_cistatic int fat_statfs(struct dentry *dentry, struct kstatfs *buf)
82262306a36Sopenharmony_ci{
82362306a36Sopenharmony_ci	struct super_block *sb = dentry->d_sb;
82462306a36Sopenharmony_ci	struct msdos_sb_info *sbi = MSDOS_SB(sb);
82562306a36Sopenharmony_ci	u64 id = huge_encode_dev(sb->s_bdev->bd_dev);
82662306a36Sopenharmony_ci
82762306a36Sopenharmony_ci	/* If the count of free cluster is still unknown, counts it here. */
82862306a36Sopenharmony_ci	if (sbi->free_clusters == -1 || !sbi->free_clus_valid) {
82962306a36Sopenharmony_ci		int err = fat_count_free_clusters(dentry->d_sb);
83062306a36Sopenharmony_ci		if (err)
83162306a36Sopenharmony_ci			return err;
83262306a36Sopenharmony_ci	}
83362306a36Sopenharmony_ci
83462306a36Sopenharmony_ci	buf->f_type = dentry->d_sb->s_magic;
83562306a36Sopenharmony_ci	buf->f_bsize = sbi->cluster_size;
83662306a36Sopenharmony_ci	buf->f_blocks = sbi->max_cluster - FAT_START_ENT;
83762306a36Sopenharmony_ci	buf->f_bfree = sbi->free_clusters;
83862306a36Sopenharmony_ci	buf->f_bavail = sbi->free_clusters;
83962306a36Sopenharmony_ci	buf->f_fsid = u64_to_fsid(id);
84062306a36Sopenharmony_ci	buf->f_namelen =
84162306a36Sopenharmony_ci		(sbi->options.isvfat ? FAT_LFN_LEN : 12) * NLS_MAX_CHARSET_SIZE;
84262306a36Sopenharmony_ci
84362306a36Sopenharmony_ci	return 0;
84462306a36Sopenharmony_ci}
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_cistatic int __fat_write_inode(struct inode *inode, int wait)
84762306a36Sopenharmony_ci{
84862306a36Sopenharmony_ci	struct super_block *sb = inode->i_sb;
84962306a36Sopenharmony_ci	struct msdos_sb_info *sbi = MSDOS_SB(sb);
85062306a36Sopenharmony_ci	struct buffer_head *bh;
85162306a36Sopenharmony_ci	struct msdos_dir_entry *raw_entry;
85262306a36Sopenharmony_ci	loff_t i_pos;
85362306a36Sopenharmony_ci	sector_t blocknr;
85462306a36Sopenharmony_ci	int err, offset;
85562306a36Sopenharmony_ci
85662306a36Sopenharmony_ci	if (inode->i_ino == MSDOS_ROOT_INO)
85762306a36Sopenharmony_ci		return 0;
85862306a36Sopenharmony_ci
85962306a36Sopenharmony_ciretry:
86062306a36Sopenharmony_ci	i_pos = fat_i_pos_read(sbi, inode);
86162306a36Sopenharmony_ci	if (!i_pos)
86262306a36Sopenharmony_ci		return 0;
86362306a36Sopenharmony_ci
86462306a36Sopenharmony_ci	fat_get_blknr_offset(sbi, i_pos, &blocknr, &offset);
86562306a36Sopenharmony_ci	bh = sb_bread(sb, blocknr);
86662306a36Sopenharmony_ci	if (!bh) {
86762306a36Sopenharmony_ci		fat_msg(sb, KERN_ERR, "unable to read inode block "
86862306a36Sopenharmony_ci		       "for updating (i_pos %lld)", i_pos);
86962306a36Sopenharmony_ci		return -EIO;
87062306a36Sopenharmony_ci	}
87162306a36Sopenharmony_ci	spin_lock(&sbi->inode_hash_lock);
87262306a36Sopenharmony_ci	if (i_pos != MSDOS_I(inode)->i_pos) {
87362306a36Sopenharmony_ci		spin_unlock(&sbi->inode_hash_lock);
87462306a36Sopenharmony_ci		brelse(bh);
87562306a36Sopenharmony_ci		goto retry;
87662306a36Sopenharmony_ci	}
87762306a36Sopenharmony_ci
87862306a36Sopenharmony_ci	raw_entry = &((struct msdos_dir_entry *) (bh->b_data))[offset];
87962306a36Sopenharmony_ci	if (S_ISDIR(inode->i_mode))
88062306a36Sopenharmony_ci		raw_entry->size = 0;
88162306a36Sopenharmony_ci	else
88262306a36Sopenharmony_ci		raw_entry->size = cpu_to_le32(inode->i_size);
88362306a36Sopenharmony_ci	raw_entry->attr = fat_make_attrs(inode);
88462306a36Sopenharmony_ci	fat_set_start(raw_entry, MSDOS_I(inode)->i_logstart);
88562306a36Sopenharmony_ci	fat_time_unix2fat(sbi, &inode->i_mtime, &raw_entry->time,
88662306a36Sopenharmony_ci			  &raw_entry->date, NULL);
88762306a36Sopenharmony_ci	if (sbi->options.isvfat) {
88862306a36Sopenharmony_ci		__le16 atime;
88962306a36Sopenharmony_ci		fat_time_unix2fat(sbi, &inode->i_atime, &atime,
89062306a36Sopenharmony_ci				  &raw_entry->adate, NULL);
89162306a36Sopenharmony_ci		fat_time_unix2fat(sbi, &MSDOS_I(inode)->i_crtime, &raw_entry->ctime,
89262306a36Sopenharmony_ci				  &raw_entry->cdate, &raw_entry->ctime_cs);
89362306a36Sopenharmony_ci	}
89462306a36Sopenharmony_ci	spin_unlock(&sbi->inode_hash_lock);
89562306a36Sopenharmony_ci	mark_buffer_dirty(bh);
89662306a36Sopenharmony_ci	err = 0;
89762306a36Sopenharmony_ci	if (wait)
89862306a36Sopenharmony_ci		err = sync_dirty_buffer(bh);
89962306a36Sopenharmony_ci	brelse(bh);
90062306a36Sopenharmony_ci	return err;
90162306a36Sopenharmony_ci}
90262306a36Sopenharmony_ci
90362306a36Sopenharmony_cistatic int fat_write_inode(struct inode *inode, struct writeback_control *wbc)
90462306a36Sopenharmony_ci{
90562306a36Sopenharmony_ci	int err;
90662306a36Sopenharmony_ci
90762306a36Sopenharmony_ci	if (inode->i_ino == MSDOS_FSINFO_INO) {
90862306a36Sopenharmony_ci		struct super_block *sb = inode->i_sb;
90962306a36Sopenharmony_ci
91062306a36Sopenharmony_ci		mutex_lock(&MSDOS_SB(sb)->s_lock);
91162306a36Sopenharmony_ci		err = fat_clusters_flush(sb);
91262306a36Sopenharmony_ci		mutex_unlock(&MSDOS_SB(sb)->s_lock);
91362306a36Sopenharmony_ci	} else
91462306a36Sopenharmony_ci		err = __fat_write_inode(inode, wbc->sync_mode == WB_SYNC_ALL);
91562306a36Sopenharmony_ci
91662306a36Sopenharmony_ci	return err;
91762306a36Sopenharmony_ci}
91862306a36Sopenharmony_ci
91962306a36Sopenharmony_ciint fat_sync_inode(struct inode *inode)
92062306a36Sopenharmony_ci{
92162306a36Sopenharmony_ci	return __fat_write_inode(inode, 1);
92262306a36Sopenharmony_ci}
92362306a36Sopenharmony_ci
92462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(fat_sync_inode);
92562306a36Sopenharmony_ci
92662306a36Sopenharmony_cistatic int fat_show_options(struct seq_file *m, struct dentry *root);
92762306a36Sopenharmony_cistatic const struct super_operations fat_sops = {
92862306a36Sopenharmony_ci	.alloc_inode	= fat_alloc_inode,
92962306a36Sopenharmony_ci	.free_inode	= fat_free_inode,
93062306a36Sopenharmony_ci	.write_inode	= fat_write_inode,
93162306a36Sopenharmony_ci	.evict_inode	= fat_evict_inode,
93262306a36Sopenharmony_ci	.put_super	= fat_put_super,
93362306a36Sopenharmony_ci	.statfs		= fat_statfs,
93462306a36Sopenharmony_ci	.remount_fs	= fat_remount,
93562306a36Sopenharmony_ci
93662306a36Sopenharmony_ci	.show_options	= fat_show_options,
93762306a36Sopenharmony_ci};
93862306a36Sopenharmony_ci
93962306a36Sopenharmony_cistatic int fat_show_options(struct seq_file *m, struct dentry *root)
94062306a36Sopenharmony_ci{
94162306a36Sopenharmony_ci	struct msdos_sb_info *sbi = MSDOS_SB(root->d_sb);
94262306a36Sopenharmony_ci	struct fat_mount_options *opts = &sbi->options;
94362306a36Sopenharmony_ci	int isvfat = opts->isvfat;
94462306a36Sopenharmony_ci
94562306a36Sopenharmony_ci	if (!uid_eq(opts->fs_uid, GLOBAL_ROOT_UID))
94662306a36Sopenharmony_ci		seq_printf(m, ",uid=%u",
94762306a36Sopenharmony_ci				from_kuid_munged(&init_user_ns, opts->fs_uid));
94862306a36Sopenharmony_ci	if (!gid_eq(opts->fs_gid, GLOBAL_ROOT_GID))
94962306a36Sopenharmony_ci		seq_printf(m, ",gid=%u",
95062306a36Sopenharmony_ci				from_kgid_munged(&init_user_ns, opts->fs_gid));
95162306a36Sopenharmony_ci	seq_printf(m, ",fmask=%04o", opts->fs_fmask);
95262306a36Sopenharmony_ci	seq_printf(m, ",dmask=%04o", opts->fs_dmask);
95362306a36Sopenharmony_ci	if (opts->allow_utime)
95462306a36Sopenharmony_ci		seq_printf(m, ",allow_utime=%04o", opts->allow_utime);
95562306a36Sopenharmony_ci	if (sbi->nls_disk)
95662306a36Sopenharmony_ci		/* strip "cp" prefix from displayed option */
95762306a36Sopenharmony_ci		seq_printf(m, ",codepage=%s", &sbi->nls_disk->charset[2]);
95862306a36Sopenharmony_ci	if (isvfat) {
95962306a36Sopenharmony_ci		if (sbi->nls_io)
96062306a36Sopenharmony_ci			seq_printf(m, ",iocharset=%s", sbi->nls_io->charset);
96162306a36Sopenharmony_ci
96262306a36Sopenharmony_ci		switch (opts->shortname) {
96362306a36Sopenharmony_ci		case VFAT_SFN_DISPLAY_WIN95 | VFAT_SFN_CREATE_WIN95:
96462306a36Sopenharmony_ci			seq_puts(m, ",shortname=win95");
96562306a36Sopenharmony_ci			break;
96662306a36Sopenharmony_ci		case VFAT_SFN_DISPLAY_WINNT | VFAT_SFN_CREATE_WINNT:
96762306a36Sopenharmony_ci			seq_puts(m, ",shortname=winnt");
96862306a36Sopenharmony_ci			break;
96962306a36Sopenharmony_ci		case VFAT_SFN_DISPLAY_WINNT | VFAT_SFN_CREATE_WIN95:
97062306a36Sopenharmony_ci			seq_puts(m, ",shortname=mixed");
97162306a36Sopenharmony_ci			break;
97262306a36Sopenharmony_ci		case VFAT_SFN_DISPLAY_LOWER | VFAT_SFN_CREATE_WIN95:
97362306a36Sopenharmony_ci			seq_puts(m, ",shortname=lower");
97462306a36Sopenharmony_ci			break;
97562306a36Sopenharmony_ci		default:
97662306a36Sopenharmony_ci			seq_puts(m, ",shortname=unknown");
97762306a36Sopenharmony_ci			break;
97862306a36Sopenharmony_ci		}
97962306a36Sopenharmony_ci	}
98062306a36Sopenharmony_ci	if (opts->name_check != 'n')
98162306a36Sopenharmony_ci		seq_printf(m, ",check=%c", opts->name_check);
98262306a36Sopenharmony_ci	if (opts->usefree)
98362306a36Sopenharmony_ci		seq_puts(m, ",usefree");
98462306a36Sopenharmony_ci	if (opts->quiet)
98562306a36Sopenharmony_ci		seq_puts(m, ",quiet");
98662306a36Sopenharmony_ci	if (opts->showexec)
98762306a36Sopenharmony_ci		seq_puts(m, ",showexec");
98862306a36Sopenharmony_ci	if (opts->sys_immutable)
98962306a36Sopenharmony_ci		seq_puts(m, ",sys_immutable");
99062306a36Sopenharmony_ci	if (!isvfat) {
99162306a36Sopenharmony_ci		if (opts->dotsOK)
99262306a36Sopenharmony_ci			seq_puts(m, ",dotsOK=yes");
99362306a36Sopenharmony_ci		if (opts->nocase)
99462306a36Sopenharmony_ci			seq_puts(m, ",nocase");
99562306a36Sopenharmony_ci	} else {
99662306a36Sopenharmony_ci		if (opts->utf8)
99762306a36Sopenharmony_ci			seq_puts(m, ",utf8");
99862306a36Sopenharmony_ci		if (opts->unicode_xlate)
99962306a36Sopenharmony_ci			seq_puts(m, ",uni_xlate");
100062306a36Sopenharmony_ci		if (!opts->numtail)
100162306a36Sopenharmony_ci			seq_puts(m, ",nonumtail");
100262306a36Sopenharmony_ci		if (opts->rodir)
100362306a36Sopenharmony_ci			seq_puts(m, ",rodir");
100462306a36Sopenharmony_ci	}
100562306a36Sopenharmony_ci	if (opts->flush)
100662306a36Sopenharmony_ci		seq_puts(m, ",flush");
100762306a36Sopenharmony_ci	if (opts->tz_set) {
100862306a36Sopenharmony_ci		if (opts->time_offset)
100962306a36Sopenharmony_ci			seq_printf(m, ",time_offset=%d", opts->time_offset);
101062306a36Sopenharmony_ci		else
101162306a36Sopenharmony_ci			seq_puts(m, ",tz=UTC");
101262306a36Sopenharmony_ci	}
101362306a36Sopenharmony_ci	if (opts->errors == FAT_ERRORS_CONT)
101462306a36Sopenharmony_ci		seq_puts(m, ",errors=continue");
101562306a36Sopenharmony_ci	else if (opts->errors == FAT_ERRORS_PANIC)
101662306a36Sopenharmony_ci		seq_puts(m, ",errors=panic");
101762306a36Sopenharmony_ci	else
101862306a36Sopenharmony_ci		seq_puts(m, ",errors=remount-ro");
101962306a36Sopenharmony_ci	if (opts->nfs == FAT_NFS_NOSTALE_RO)
102062306a36Sopenharmony_ci		seq_puts(m, ",nfs=nostale_ro");
102162306a36Sopenharmony_ci	else if (opts->nfs)
102262306a36Sopenharmony_ci		seq_puts(m, ",nfs=stale_rw");
102362306a36Sopenharmony_ci	if (opts->discard)
102462306a36Sopenharmony_ci		seq_puts(m, ",discard");
102562306a36Sopenharmony_ci	if (opts->dos1xfloppy)
102662306a36Sopenharmony_ci		seq_puts(m, ",dos1xfloppy");
102762306a36Sopenharmony_ci
102862306a36Sopenharmony_ci	return 0;
102962306a36Sopenharmony_ci}
103062306a36Sopenharmony_ci
103162306a36Sopenharmony_cienum {
103262306a36Sopenharmony_ci	Opt_check_n, Opt_check_r, Opt_check_s, Opt_uid, Opt_gid,
103362306a36Sopenharmony_ci	Opt_umask, Opt_dmask, Opt_fmask, Opt_allow_utime, Opt_codepage,
103462306a36Sopenharmony_ci	Opt_usefree, Opt_nocase, Opt_quiet, Opt_showexec, Opt_debug,
103562306a36Sopenharmony_ci	Opt_immutable, Opt_dots, Opt_nodots,
103662306a36Sopenharmony_ci	Opt_charset, Opt_shortname_lower, Opt_shortname_win95,
103762306a36Sopenharmony_ci	Opt_shortname_winnt, Opt_shortname_mixed, Opt_utf8_no, Opt_utf8_yes,
103862306a36Sopenharmony_ci	Opt_uni_xl_no, Opt_uni_xl_yes, Opt_nonumtail_no, Opt_nonumtail_yes,
103962306a36Sopenharmony_ci	Opt_obsolete, Opt_flush, Opt_tz_utc, Opt_rodir, Opt_err_cont,
104062306a36Sopenharmony_ci	Opt_err_panic, Opt_err_ro, Opt_discard, Opt_nfs, Opt_time_offset,
104162306a36Sopenharmony_ci	Opt_nfs_stale_rw, Opt_nfs_nostale_ro, Opt_err, Opt_dos1xfloppy,
104262306a36Sopenharmony_ci};
104362306a36Sopenharmony_ci
104462306a36Sopenharmony_cistatic const match_table_t fat_tokens = {
104562306a36Sopenharmony_ci	{Opt_check_r, "check=relaxed"},
104662306a36Sopenharmony_ci	{Opt_check_s, "check=strict"},
104762306a36Sopenharmony_ci	{Opt_check_n, "check=normal"},
104862306a36Sopenharmony_ci	{Opt_check_r, "check=r"},
104962306a36Sopenharmony_ci	{Opt_check_s, "check=s"},
105062306a36Sopenharmony_ci	{Opt_check_n, "check=n"},
105162306a36Sopenharmony_ci	{Opt_uid, "uid=%u"},
105262306a36Sopenharmony_ci	{Opt_gid, "gid=%u"},
105362306a36Sopenharmony_ci	{Opt_umask, "umask=%o"},
105462306a36Sopenharmony_ci	{Opt_dmask, "dmask=%o"},
105562306a36Sopenharmony_ci	{Opt_fmask, "fmask=%o"},
105662306a36Sopenharmony_ci	{Opt_allow_utime, "allow_utime=%o"},
105762306a36Sopenharmony_ci	{Opt_codepage, "codepage=%u"},
105862306a36Sopenharmony_ci	{Opt_usefree, "usefree"},
105962306a36Sopenharmony_ci	{Opt_nocase, "nocase"},
106062306a36Sopenharmony_ci	{Opt_quiet, "quiet"},
106162306a36Sopenharmony_ci	{Opt_showexec, "showexec"},
106262306a36Sopenharmony_ci	{Opt_debug, "debug"},
106362306a36Sopenharmony_ci	{Opt_immutable, "sys_immutable"},
106462306a36Sopenharmony_ci	{Opt_flush, "flush"},
106562306a36Sopenharmony_ci	{Opt_tz_utc, "tz=UTC"},
106662306a36Sopenharmony_ci	{Opt_time_offset, "time_offset=%d"},
106762306a36Sopenharmony_ci	{Opt_err_cont, "errors=continue"},
106862306a36Sopenharmony_ci	{Opt_err_panic, "errors=panic"},
106962306a36Sopenharmony_ci	{Opt_err_ro, "errors=remount-ro"},
107062306a36Sopenharmony_ci	{Opt_discard, "discard"},
107162306a36Sopenharmony_ci	{Opt_nfs_stale_rw, "nfs"},
107262306a36Sopenharmony_ci	{Opt_nfs_stale_rw, "nfs=stale_rw"},
107362306a36Sopenharmony_ci	{Opt_nfs_nostale_ro, "nfs=nostale_ro"},
107462306a36Sopenharmony_ci	{Opt_dos1xfloppy, "dos1xfloppy"},
107562306a36Sopenharmony_ci	{Opt_obsolete, "conv=binary"},
107662306a36Sopenharmony_ci	{Opt_obsolete, "conv=text"},
107762306a36Sopenharmony_ci	{Opt_obsolete, "conv=auto"},
107862306a36Sopenharmony_ci	{Opt_obsolete, "conv=b"},
107962306a36Sopenharmony_ci	{Opt_obsolete, "conv=t"},
108062306a36Sopenharmony_ci	{Opt_obsolete, "conv=a"},
108162306a36Sopenharmony_ci	{Opt_obsolete, "fat=%u"},
108262306a36Sopenharmony_ci	{Opt_obsolete, "blocksize=%u"},
108362306a36Sopenharmony_ci	{Opt_obsolete, "cvf_format=%20s"},
108462306a36Sopenharmony_ci	{Opt_obsolete, "cvf_options=%100s"},
108562306a36Sopenharmony_ci	{Opt_obsolete, "posix"},
108662306a36Sopenharmony_ci	{Opt_err, NULL},
108762306a36Sopenharmony_ci};
108862306a36Sopenharmony_cistatic const match_table_t msdos_tokens = {
108962306a36Sopenharmony_ci	{Opt_nodots, "nodots"},
109062306a36Sopenharmony_ci	{Opt_nodots, "dotsOK=no"},
109162306a36Sopenharmony_ci	{Opt_dots, "dots"},
109262306a36Sopenharmony_ci	{Opt_dots, "dotsOK=yes"},
109362306a36Sopenharmony_ci	{Opt_err, NULL}
109462306a36Sopenharmony_ci};
109562306a36Sopenharmony_cistatic const match_table_t vfat_tokens = {
109662306a36Sopenharmony_ci	{Opt_charset, "iocharset=%s"},
109762306a36Sopenharmony_ci	{Opt_shortname_lower, "shortname=lower"},
109862306a36Sopenharmony_ci	{Opt_shortname_win95, "shortname=win95"},
109962306a36Sopenharmony_ci	{Opt_shortname_winnt, "shortname=winnt"},
110062306a36Sopenharmony_ci	{Opt_shortname_mixed, "shortname=mixed"},
110162306a36Sopenharmony_ci	{Opt_utf8_no, "utf8=0"},		/* 0 or no or false */
110262306a36Sopenharmony_ci	{Opt_utf8_no, "utf8=no"},
110362306a36Sopenharmony_ci	{Opt_utf8_no, "utf8=false"},
110462306a36Sopenharmony_ci	{Opt_utf8_yes, "utf8=1"},		/* empty or 1 or yes or true */
110562306a36Sopenharmony_ci	{Opt_utf8_yes, "utf8=yes"},
110662306a36Sopenharmony_ci	{Opt_utf8_yes, "utf8=true"},
110762306a36Sopenharmony_ci	{Opt_utf8_yes, "utf8"},
110862306a36Sopenharmony_ci	{Opt_uni_xl_no, "uni_xlate=0"},		/* 0 or no or false */
110962306a36Sopenharmony_ci	{Opt_uni_xl_no, "uni_xlate=no"},
111062306a36Sopenharmony_ci	{Opt_uni_xl_no, "uni_xlate=false"},
111162306a36Sopenharmony_ci	{Opt_uni_xl_yes, "uni_xlate=1"},	/* empty or 1 or yes or true */
111262306a36Sopenharmony_ci	{Opt_uni_xl_yes, "uni_xlate=yes"},
111362306a36Sopenharmony_ci	{Opt_uni_xl_yes, "uni_xlate=true"},
111462306a36Sopenharmony_ci	{Opt_uni_xl_yes, "uni_xlate"},
111562306a36Sopenharmony_ci	{Opt_nonumtail_no, "nonumtail=0"},	/* 0 or no or false */
111662306a36Sopenharmony_ci	{Opt_nonumtail_no, "nonumtail=no"},
111762306a36Sopenharmony_ci	{Opt_nonumtail_no, "nonumtail=false"},
111862306a36Sopenharmony_ci	{Opt_nonumtail_yes, "nonumtail=1"},	/* empty or 1 or yes or true */
111962306a36Sopenharmony_ci	{Opt_nonumtail_yes, "nonumtail=yes"},
112062306a36Sopenharmony_ci	{Opt_nonumtail_yes, "nonumtail=true"},
112162306a36Sopenharmony_ci	{Opt_nonumtail_yes, "nonumtail"},
112262306a36Sopenharmony_ci	{Opt_rodir, "rodir"},
112362306a36Sopenharmony_ci	{Opt_err, NULL}
112462306a36Sopenharmony_ci};
112562306a36Sopenharmony_ci
112662306a36Sopenharmony_cistatic int parse_options(struct super_block *sb, char *options, int is_vfat,
112762306a36Sopenharmony_ci			 int silent, int *debug, struct fat_mount_options *opts)
112862306a36Sopenharmony_ci{
112962306a36Sopenharmony_ci	char *p;
113062306a36Sopenharmony_ci	substring_t args[MAX_OPT_ARGS];
113162306a36Sopenharmony_ci	int option;
113262306a36Sopenharmony_ci	char *iocharset;
113362306a36Sopenharmony_ci
113462306a36Sopenharmony_ci	opts->isvfat = is_vfat;
113562306a36Sopenharmony_ci
113662306a36Sopenharmony_ci	opts->fs_uid = current_uid();
113762306a36Sopenharmony_ci	opts->fs_gid = current_gid();
113862306a36Sopenharmony_ci	opts->fs_fmask = opts->fs_dmask = current_umask();
113962306a36Sopenharmony_ci	opts->allow_utime = -1;
114062306a36Sopenharmony_ci	opts->codepage = fat_default_codepage;
114162306a36Sopenharmony_ci	fat_reset_iocharset(opts);
114262306a36Sopenharmony_ci	if (is_vfat) {
114362306a36Sopenharmony_ci		opts->shortname = VFAT_SFN_DISPLAY_WINNT|VFAT_SFN_CREATE_WIN95;
114462306a36Sopenharmony_ci		opts->rodir = 0;
114562306a36Sopenharmony_ci	} else {
114662306a36Sopenharmony_ci		opts->shortname = 0;
114762306a36Sopenharmony_ci		opts->rodir = 1;
114862306a36Sopenharmony_ci	}
114962306a36Sopenharmony_ci	opts->name_check = 'n';
115062306a36Sopenharmony_ci	opts->quiet = opts->showexec = opts->sys_immutable = opts->dotsOK =  0;
115162306a36Sopenharmony_ci	opts->unicode_xlate = 0;
115262306a36Sopenharmony_ci	opts->numtail = 1;
115362306a36Sopenharmony_ci	opts->usefree = opts->nocase = 0;
115462306a36Sopenharmony_ci	opts->tz_set = 0;
115562306a36Sopenharmony_ci	opts->nfs = 0;
115662306a36Sopenharmony_ci	opts->errors = FAT_ERRORS_RO;
115762306a36Sopenharmony_ci	*debug = 0;
115862306a36Sopenharmony_ci
115962306a36Sopenharmony_ci	opts->utf8 = IS_ENABLED(CONFIG_FAT_DEFAULT_UTF8) && is_vfat;
116062306a36Sopenharmony_ci
116162306a36Sopenharmony_ci	if (!options)
116262306a36Sopenharmony_ci		goto out;
116362306a36Sopenharmony_ci
116462306a36Sopenharmony_ci	while ((p = strsep(&options, ",")) != NULL) {
116562306a36Sopenharmony_ci		int token;
116662306a36Sopenharmony_ci		if (!*p)
116762306a36Sopenharmony_ci			continue;
116862306a36Sopenharmony_ci
116962306a36Sopenharmony_ci		token = match_token(p, fat_tokens, args);
117062306a36Sopenharmony_ci		if (token == Opt_err) {
117162306a36Sopenharmony_ci			if (is_vfat)
117262306a36Sopenharmony_ci				token = match_token(p, vfat_tokens, args);
117362306a36Sopenharmony_ci			else
117462306a36Sopenharmony_ci				token = match_token(p, msdos_tokens, args);
117562306a36Sopenharmony_ci		}
117662306a36Sopenharmony_ci		switch (token) {
117762306a36Sopenharmony_ci		case Opt_check_s:
117862306a36Sopenharmony_ci			opts->name_check = 's';
117962306a36Sopenharmony_ci			break;
118062306a36Sopenharmony_ci		case Opt_check_r:
118162306a36Sopenharmony_ci			opts->name_check = 'r';
118262306a36Sopenharmony_ci			break;
118362306a36Sopenharmony_ci		case Opt_check_n:
118462306a36Sopenharmony_ci			opts->name_check = 'n';
118562306a36Sopenharmony_ci			break;
118662306a36Sopenharmony_ci		case Opt_usefree:
118762306a36Sopenharmony_ci			opts->usefree = 1;
118862306a36Sopenharmony_ci			break;
118962306a36Sopenharmony_ci		case Opt_nocase:
119062306a36Sopenharmony_ci			if (!is_vfat)
119162306a36Sopenharmony_ci				opts->nocase = 1;
119262306a36Sopenharmony_ci			else {
119362306a36Sopenharmony_ci				/* for backward compatibility */
119462306a36Sopenharmony_ci				opts->shortname = VFAT_SFN_DISPLAY_WIN95
119562306a36Sopenharmony_ci					| VFAT_SFN_CREATE_WIN95;
119662306a36Sopenharmony_ci			}
119762306a36Sopenharmony_ci			break;
119862306a36Sopenharmony_ci		case Opt_quiet:
119962306a36Sopenharmony_ci			opts->quiet = 1;
120062306a36Sopenharmony_ci			break;
120162306a36Sopenharmony_ci		case Opt_showexec:
120262306a36Sopenharmony_ci			opts->showexec = 1;
120362306a36Sopenharmony_ci			break;
120462306a36Sopenharmony_ci		case Opt_debug:
120562306a36Sopenharmony_ci			*debug = 1;
120662306a36Sopenharmony_ci			break;
120762306a36Sopenharmony_ci		case Opt_immutable:
120862306a36Sopenharmony_ci			opts->sys_immutable = 1;
120962306a36Sopenharmony_ci			break;
121062306a36Sopenharmony_ci		case Opt_uid:
121162306a36Sopenharmony_ci			if (match_int(&args[0], &option))
121262306a36Sopenharmony_ci				return -EINVAL;
121362306a36Sopenharmony_ci			opts->fs_uid = make_kuid(current_user_ns(), option);
121462306a36Sopenharmony_ci			if (!uid_valid(opts->fs_uid))
121562306a36Sopenharmony_ci				return -EINVAL;
121662306a36Sopenharmony_ci			break;
121762306a36Sopenharmony_ci		case Opt_gid:
121862306a36Sopenharmony_ci			if (match_int(&args[0], &option))
121962306a36Sopenharmony_ci				return -EINVAL;
122062306a36Sopenharmony_ci			opts->fs_gid = make_kgid(current_user_ns(), option);
122162306a36Sopenharmony_ci			if (!gid_valid(opts->fs_gid))
122262306a36Sopenharmony_ci				return -EINVAL;
122362306a36Sopenharmony_ci			break;
122462306a36Sopenharmony_ci		case Opt_umask:
122562306a36Sopenharmony_ci			if (match_octal(&args[0], &option))
122662306a36Sopenharmony_ci				return -EINVAL;
122762306a36Sopenharmony_ci			opts->fs_fmask = opts->fs_dmask = option;
122862306a36Sopenharmony_ci			break;
122962306a36Sopenharmony_ci		case Opt_dmask:
123062306a36Sopenharmony_ci			if (match_octal(&args[0], &option))
123162306a36Sopenharmony_ci				return -EINVAL;
123262306a36Sopenharmony_ci			opts->fs_dmask = option;
123362306a36Sopenharmony_ci			break;
123462306a36Sopenharmony_ci		case Opt_fmask:
123562306a36Sopenharmony_ci			if (match_octal(&args[0], &option))
123662306a36Sopenharmony_ci				return -EINVAL;
123762306a36Sopenharmony_ci			opts->fs_fmask = option;
123862306a36Sopenharmony_ci			break;
123962306a36Sopenharmony_ci		case Opt_allow_utime:
124062306a36Sopenharmony_ci			if (match_octal(&args[0], &option))
124162306a36Sopenharmony_ci				return -EINVAL;
124262306a36Sopenharmony_ci			opts->allow_utime = option & (S_IWGRP | S_IWOTH);
124362306a36Sopenharmony_ci			break;
124462306a36Sopenharmony_ci		case Opt_codepage:
124562306a36Sopenharmony_ci			if (match_int(&args[0], &option))
124662306a36Sopenharmony_ci				return -EINVAL;
124762306a36Sopenharmony_ci			opts->codepage = option;
124862306a36Sopenharmony_ci			break;
124962306a36Sopenharmony_ci		case Opt_flush:
125062306a36Sopenharmony_ci			opts->flush = 1;
125162306a36Sopenharmony_ci			break;
125262306a36Sopenharmony_ci		case Opt_time_offset:
125362306a36Sopenharmony_ci			if (match_int(&args[0], &option))
125462306a36Sopenharmony_ci				return -EINVAL;
125562306a36Sopenharmony_ci			/*
125662306a36Sopenharmony_ci			 * GMT+-12 zones may have DST corrections so at least
125762306a36Sopenharmony_ci			 * 13 hours difference is needed. Make the limit 24
125862306a36Sopenharmony_ci			 * just in case someone invents something unusual.
125962306a36Sopenharmony_ci			 */
126062306a36Sopenharmony_ci			if (option < -24 * 60 || option > 24 * 60)
126162306a36Sopenharmony_ci				return -EINVAL;
126262306a36Sopenharmony_ci			opts->tz_set = 1;
126362306a36Sopenharmony_ci			opts->time_offset = option;
126462306a36Sopenharmony_ci			break;
126562306a36Sopenharmony_ci		case Opt_tz_utc:
126662306a36Sopenharmony_ci			opts->tz_set = 1;
126762306a36Sopenharmony_ci			opts->time_offset = 0;
126862306a36Sopenharmony_ci			break;
126962306a36Sopenharmony_ci		case Opt_err_cont:
127062306a36Sopenharmony_ci			opts->errors = FAT_ERRORS_CONT;
127162306a36Sopenharmony_ci			break;
127262306a36Sopenharmony_ci		case Opt_err_panic:
127362306a36Sopenharmony_ci			opts->errors = FAT_ERRORS_PANIC;
127462306a36Sopenharmony_ci			break;
127562306a36Sopenharmony_ci		case Opt_err_ro:
127662306a36Sopenharmony_ci			opts->errors = FAT_ERRORS_RO;
127762306a36Sopenharmony_ci			break;
127862306a36Sopenharmony_ci		case Opt_nfs_stale_rw:
127962306a36Sopenharmony_ci			opts->nfs = FAT_NFS_STALE_RW;
128062306a36Sopenharmony_ci			break;
128162306a36Sopenharmony_ci		case Opt_nfs_nostale_ro:
128262306a36Sopenharmony_ci			opts->nfs = FAT_NFS_NOSTALE_RO;
128362306a36Sopenharmony_ci			break;
128462306a36Sopenharmony_ci		case Opt_dos1xfloppy:
128562306a36Sopenharmony_ci			opts->dos1xfloppy = 1;
128662306a36Sopenharmony_ci			break;
128762306a36Sopenharmony_ci
128862306a36Sopenharmony_ci		/* msdos specific */
128962306a36Sopenharmony_ci		case Opt_dots:
129062306a36Sopenharmony_ci			opts->dotsOK = 1;
129162306a36Sopenharmony_ci			break;
129262306a36Sopenharmony_ci		case Opt_nodots:
129362306a36Sopenharmony_ci			opts->dotsOK = 0;
129462306a36Sopenharmony_ci			break;
129562306a36Sopenharmony_ci
129662306a36Sopenharmony_ci		/* vfat specific */
129762306a36Sopenharmony_ci		case Opt_charset:
129862306a36Sopenharmony_ci			fat_reset_iocharset(opts);
129962306a36Sopenharmony_ci			iocharset = match_strdup(&args[0]);
130062306a36Sopenharmony_ci			if (!iocharset)
130162306a36Sopenharmony_ci				return -ENOMEM;
130262306a36Sopenharmony_ci			opts->iocharset = iocharset;
130362306a36Sopenharmony_ci			break;
130462306a36Sopenharmony_ci		case Opt_shortname_lower:
130562306a36Sopenharmony_ci			opts->shortname = VFAT_SFN_DISPLAY_LOWER
130662306a36Sopenharmony_ci					| VFAT_SFN_CREATE_WIN95;
130762306a36Sopenharmony_ci			break;
130862306a36Sopenharmony_ci		case Opt_shortname_win95:
130962306a36Sopenharmony_ci			opts->shortname = VFAT_SFN_DISPLAY_WIN95
131062306a36Sopenharmony_ci					| VFAT_SFN_CREATE_WIN95;
131162306a36Sopenharmony_ci			break;
131262306a36Sopenharmony_ci		case Opt_shortname_winnt:
131362306a36Sopenharmony_ci			opts->shortname = VFAT_SFN_DISPLAY_WINNT
131462306a36Sopenharmony_ci					| VFAT_SFN_CREATE_WINNT;
131562306a36Sopenharmony_ci			break;
131662306a36Sopenharmony_ci		case Opt_shortname_mixed:
131762306a36Sopenharmony_ci			opts->shortname = VFAT_SFN_DISPLAY_WINNT
131862306a36Sopenharmony_ci					| VFAT_SFN_CREATE_WIN95;
131962306a36Sopenharmony_ci			break;
132062306a36Sopenharmony_ci		case Opt_utf8_no:		/* 0 or no or false */
132162306a36Sopenharmony_ci			opts->utf8 = 0;
132262306a36Sopenharmony_ci			break;
132362306a36Sopenharmony_ci		case Opt_utf8_yes:		/* empty or 1 or yes or true */
132462306a36Sopenharmony_ci			opts->utf8 = 1;
132562306a36Sopenharmony_ci			break;
132662306a36Sopenharmony_ci		case Opt_uni_xl_no:		/* 0 or no or false */
132762306a36Sopenharmony_ci			opts->unicode_xlate = 0;
132862306a36Sopenharmony_ci			break;
132962306a36Sopenharmony_ci		case Opt_uni_xl_yes:		/* empty or 1 or yes or true */
133062306a36Sopenharmony_ci			opts->unicode_xlate = 1;
133162306a36Sopenharmony_ci			break;
133262306a36Sopenharmony_ci		case Opt_nonumtail_no:		/* 0 or no or false */
133362306a36Sopenharmony_ci			opts->numtail = 1;	/* negated option */
133462306a36Sopenharmony_ci			break;
133562306a36Sopenharmony_ci		case Opt_nonumtail_yes:		/* empty or 1 or yes or true */
133662306a36Sopenharmony_ci			opts->numtail = 0;	/* negated option */
133762306a36Sopenharmony_ci			break;
133862306a36Sopenharmony_ci		case Opt_rodir:
133962306a36Sopenharmony_ci			opts->rodir = 1;
134062306a36Sopenharmony_ci			break;
134162306a36Sopenharmony_ci		case Opt_discard:
134262306a36Sopenharmony_ci			opts->discard = 1;
134362306a36Sopenharmony_ci			break;
134462306a36Sopenharmony_ci
134562306a36Sopenharmony_ci		/* obsolete mount options */
134662306a36Sopenharmony_ci		case Opt_obsolete:
134762306a36Sopenharmony_ci			fat_msg(sb, KERN_INFO, "\"%s\" option is obsolete, "
134862306a36Sopenharmony_ci			       "not supported now", p);
134962306a36Sopenharmony_ci			break;
135062306a36Sopenharmony_ci		/* unknown option */
135162306a36Sopenharmony_ci		default:
135262306a36Sopenharmony_ci			if (!silent) {
135362306a36Sopenharmony_ci				fat_msg(sb, KERN_ERR,
135462306a36Sopenharmony_ci				       "Unrecognized mount option \"%s\" "
135562306a36Sopenharmony_ci				       "or missing value", p);
135662306a36Sopenharmony_ci			}
135762306a36Sopenharmony_ci			return -EINVAL;
135862306a36Sopenharmony_ci		}
135962306a36Sopenharmony_ci	}
136062306a36Sopenharmony_ci
136162306a36Sopenharmony_ciout:
136262306a36Sopenharmony_ci	/* UTF-8 doesn't provide FAT semantics */
136362306a36Sopenharmony_ci	if (!strcmp(opts->iocharset, "utf8")) {
136462306a36Sopenharmony_ci		fat_msg(sb, KERN_WARNING, "utf8 is not a recommended IO charset"
136562306a36Sopenharmony_ci		       " for FAT filesystems, filesystem will be "
136662306a36Sopenharmony_ci		       "case sensitive!");
136762306a36Sopenharmony_ci	}
136862306a36Sopenharmony_ci
136962306a36Sopenharmony_ci	/* If user doesn't specify allow_utime, it's initialized from dmask. */
137062306a36Sopenharmony_ci	if (opts->allow_utime == (unsigned short)-1)
137162306a36Sopenharmony_ci		opts->allow_utime = ~opts->fs_dmask & (S_IWGRP | S_IWOTH);
137262306a36Sopenharmony_ci	if (opts->unicode_xlate)
137362306a36Sopenharmony_ci		opts->utf8 = 0;
137462306a36Sopenharmony_ci	if (opts->nfs == FAT_NFS_NOSTALE_RO) {
137562306a36Sopenharmony_ci		sb->s_flags |= SB_RDONLY;
137662306a36Sopenharmony_ci		sb->s_export_op = &fat_export_ops_nostale;
137762306a36Sopenharmony_ci	}
137862306a36Sopenharmony_ci
137962306a36Sopenharmony_ci	return 0;
138062306a36Sopenharmony_ci}
138162306a36Sopenharmony_ci
138262306a36Sopenharmony_cistatic int fat_read_root(struct inode *inode)
138362306a36Sopenharmony_ci{
138462306a36Sopenharmony_ci	struct msdos_sb_info *sbi = MSDOS_SB(inode->i_sb);
138562306a36Sopenharmony_ci	int error;
138662306a36Sopenharmony_ci
138762306a36Sopenharmony_ci	MSDOS_I(inode)->i_pos = MSDOS_ROOT_INO;
138862306a36Sopenharmony_ci	inode->i_uid = sbi->options.fs_uid;
138962306a36Sopenharmony_ci	inode->i_gid = sbi->options.fs_gid;
139062306a36Sopenharmony_ci	inode_inc_iversion(inode);
139162306a36Sopenharmony_ci	inode->i_generation = 0;
139262306a36Sopenharmony_ci	inode->i_mode = fat_make_mode(sbi, ATTR_DIR, S_IRWXUGO);
139362306a36Sopenharmony_ci	inode->i_op = sbi->dir_ops;
139462306a36Sopenharmony_ci	inode->i_fop = &fat_dir_operations;
139562306a36Sopenharmony_ci	if (is_fat32(sbi)) {
139662306a36Sopenharmony_ci		MSDOS_I(inode)->i_start = sbi->root_cluster;
139762306a36Sopenharmony_ci		error = fat_calc_dir_size(inode);
139862306a36Sopenharmony_ci		if (error < 0)
139962306a36Sopenharmony_ci			return error;
140062306a36Sopenharmony_ci	} else {
140162306a36Sopenharmony_ci		MSDOS_I(inode)->i_start = 0;
140262306a36Sopenharmony_ci		inode->i_size = sbi->dir_entries * sizeof(struct msdos_dir_entry);
140362306a36Sopenharmony_ci	}
140462306a36Sopenharmony_ci	inode->i_blocks = ((inode->i_size + (sbi->cluster_size - 1))
140562306a36Sopenharmony_ci			   & ~((loff_t)sbi->cluster_size - 1)) >> 9;
140662306a36Sopenharmony_ci	MSDOS_I(inode)->i_logstart = 0;
140762306a36Sopenharmony_ci	MSDOS_I(inode)->mmu_private = inode->i_size;
140862306a36Sopenharmony_ci
140962306a36Sopenharmony_ci	fat_save_attrs(inode, ATTR_DIR);
141062306a36Sopenharmony_ci	inode->i_mtime = inode->i_atime = inode_set_ctime(inode, 0, 0);
141162306a36Sopenharmony_ci	set_nlink(inode, fat_subdirs(inode)+2);
141262306a36Sopenharmony_ci
141362306a36Sopenharmony_ci	return 0;
141462306a36Sopenharmony_ci}
141562306a36Sopenharmony_ci
141662306a36Sopenharmony_cistatic unsigned long calc_fat_clusters(struct super_block *sb)
141762306a36Sopenharmony_ci{
141862306a36Sopenharmony_ci	struct msdos_sb_info *sbi = MSDOS_SB(sb);
141962306a36Sopenharmony_ci
142062306a36Sopenharmony_ci	/* Divide first to avoid overflow */
142162306a36Sopenharmony_ci	if (!is_fat12(sbi)) {
142262306a36Sopenharmony_ci		unsigned long ent_per_sec = sb->s_blocksize * 8 / sbi->fat_bits;
142362306a36Sopenharmony_ci		return ent_per_sec * sbi->fat_length;
142462306a36Sopenharmony_ci	}
142562306a36Sopenharmony_ci
142662306a36Sopenharmony_ci	return sbi->fat_length * sb->s_blocksize * 8 / sbi->fat_bits;
142762306a36Sopenharmony_ci}
142862306a36Sopenharmony_ci
142962306a36Sopenharmony_cistatic bool fat_bpb_is_zero(struct fat_boot_sector *b)
143062306a36Sopenharmony_ci{
143162306a36Sopenharmony_ci	if (get_unaligned_le16(&b->sector_size))
143262306a36Sopenharmony_ci		return false;
143362306a36Sopenharmony_ci	if (b->sec_per_clus)
143462306a36Sopenharmony_ci		return false;
143562306a36Sopenharmony_ci	if (b->reserved)
143662306a36Sopenharmony_ci		return false;
143762306a36Sopenharmony_ci	if (b->fats)
143862306a36Sopenharmony_ci		return false;
143962306a36Sopenharmony_ci	if (get_unaligned_le16(&b->dir_entries))
144062306a36Sopenharmony_ci		return false;
144162306a36Sopenharmony_ci	if (get_unaligned_le16(&b->sectors))
144262306a36Sopenharmony_ci		return false;
144362306a36Sopenharmony_ci	if (b->media)
144462306a36Sopenharmony_ci		return false;
144562306a36Sopenharmony_ci	if (b->fat_length)
144662306a36Sopenharmony_ci		return false;
144762306a36Sopenharmony_ci	if (b->secs_track)
144862306a36Sopenharmony_ci		return false;
144962306a36Sopenharmony_ci	if (b->heads)
145062306a36Sopenharmony_ci		return false;
145162306a36Sopenharmony_ci	return true;
145262306a36Sopenharmony_ci}
145362306a36Sopenharmony_ci
145462306a36Sopenharmony_cistatic int fat_read_bpb(struct super_block *sb, struct fat_boot_sector *b,
145562306a36Sopenharmony_ci	int silent, struct fat_bios_param_block *bpb)
145662306a36Sopenharmony_ci{
145762306a36Sopenharmony_ci	int error = -EINVAL;
145862306a36Sopenharmony_ci
145962306a36Sopenharmony_ci	/* Read in BPB ... */
146062306a36Sopenharmony_ci	memset(bpb, 0, sizeof(*bpb));
146162306a36Sopenharmony_ci	bpb->fat_sector_size = get_unaligned_le16(&b->sector_size);
146262306a36Sopenharmony_ci	bpb->fat_sec_per_clus = b->sec_per_clus;
146362306a36Sopenharmony_ci	bpb->fat_reserved = le16_to_cpu(b->reserved);
146462306a36Sopenharmony_ci	bpb->fat_fats = b->fats;
146562306a36Sopenharmony_ci	bpb->fat_dir_entries = get_unaligned_le16(&b->dir_entries);
146662306a36Sopenharmony_ci	bpb->fat_sectors = get_unaligned_le16(&b->sectors);
146762306a36Sopenharmony_ci	bpb->fat_fat_length = le16_to_cpu(b->fat_length);
146862306a36Sopenharmony_ci	bpb->fat_total_sect = le32_to_cpu(b->total_sect);
146962306a36Sopenharmony_ci
147062306a36Sopenharmony_ci	bpb->fat16_state = b->fat16.state;
147162306a36Sopenharmony_ci	bpb->fat16_vol_id = get_unaligned_le32(b->fat16.vol_id);
147262306a36Sopenharmony_ci
147362306a36Sopenharmony_ci	bpb->fat32_length = le32_to_cpu(b->fat32.length);
147462306a36Sopenharmony_ci	bpb->fat32_root_cluster = le32_to_cpu(b->fat32.root_cluster);
147562306a36Sopenharmony_ci	bpb->fat32_info_sector = le16_to_cpu(b->fat32.info_sector);
147662306a36Sopenharmony_ci	bpb->fat32_state = b->fat32.state;
147762306a36Sopenharmony_ci	bpb->fat32_vol_id = get_unaligned_le32(b->fat32.vol_id);
147862306a36Sopenharmony_ci
147962306a36Sopenharmony_ci	/* Validate this looks like a FAT filesystem BPB */
148062306a36Sopenharmony_ci	if (!bpb->fat_reserved) {
148162306a36Sopenharmony_ci		if (!silent)
148262306a36Sopenharmony_ci			fat_msg(sb, KERN_ERR,
148362306a36Sopenharmony_ci				"bogus number of reserved sectors");
148462306a36Sopenharmony_ci		goto out;
148562306a36Sopenharmony_ci	}
148662306a36Sopenharmony_ci	if (!bpb->fat_fats) {
148762306a36Sopenharmony_ci		if (!silent)
148862306a36Sopenharmony_ci			fat_msg(sb, KERN_ERR, "bogus number of FAT structure");
148962306a36Sopenharmony_ci		goto out;
149062306a36Sopenharmony_ci	}
149162306a36Sopenharmony_ci
149262306a36Sopenharmony_ci	/*
149362306a36Sopenharmony_ci	 * Earlier we checked here that b->secs_track and b->head are nonzero,
149462306a36Sopenharmony_ci	 * but it turns out valid FAT filesystems can have zero there.
149562306a36Sopenharmony_ci	 */
149662306a36Sopenharmony_ci
149762306a36Sopenharmony_ci	if (!fat_valid_media(b->media)) {
149862306a36Sopenharmony_ci		if (!silent)
149962306a36Sopenharmony_ci			fat_msg(sb, KERN_ERR, "invalid media value (0x%02x)",
150062306a36Sopenharmony_ci				(unsigned)b->media);
150162306a36Sopenharmony_ci		goto out;
150262306a36Sopenharmony_ci	}
150362306a36Sopenharmony_ci
150462306a36Sopenharmony_ci	if (!is_power_of_2(bpb->fat_sector_size)
150562306a36Sopenharmony_ci	    || (bpb->fat_sector_size < 512)
150662306a36Sopenharmony_ci	    || (bpb->fat_sector_size > 4096)) {
150762306a36Sopenharmony_ci		if (!silent)
150862306a36Sopenharmony_ci			fat_msg(sb, KERN_ERR, "bogus logical sector size %u",
150962306a36Sopenharmony_ci			       (unsigned)bpb->fat_sector_size);
151062306a36Sopenharmony_ci		goto out;
151162306a36Sopenharmony_ci	}
151262306a36Sopenharmony_ci
151362306a36Sopenharmony_ci	if (!is_power_of_2(bpb->fat_sec_per_clus)) {
151462306a36Sopenharmony_ci		if (!silent)
151562306a36Sopenharmony_ci			fat_msg(sb, KERN_ERR, "bogus sectors per cluster %u",
151662306a36Sopenharmony_ci				(unsigned)bpb->fat_sec_per_clus);
151762306a36Sopenharmony_ci		goto out;
151862306a36Sopenharmony_ci	}
151962306a36Sopenharmony_ci
152062306a36Sopenharmony_ci	if (bpb->fat_fat_length == 0 && bpb->fat32_length == 0) {
152162306a36Sopenharmony_ci		if (!silent)
152262306a36Sopenharmony_ci			fat_msg(sb, KERN_ERR, "bogus number of FAT sectors");
152362306a36Sopenharmony_ci		goto out;
152462306a36Sopenharmony_ci	}
152562306a36Sopenharmony_ci
152662306a36Sopenharmony_ci	error = 0;
152762306a36Sopenharmony_ci
152862306a36Sopenharmony_ciout:
152962306a36Sopenharmony_ci	return error;
153062306a36Sopenharmony_ci}
153162306a36Sopenharmony_ci
153262306a36Sopenharmony_cistatic int fat_read_static_bpb(struct super_block *sb,
153362306a36Sopenharmony_ci	struct fat_boot_sector *b, int silent,
153462306a36Sopenharmony_ci	struct fat_bios_param_block *bpb)
153562306a36Sopenharmony_ci{
153662306a36Sopenharmony_ci	static const char *notdos1x = "This doesn't look like a DOS 1.x volume";
153762306a36Sopenharmony_ci	sector_t bd_sects = bdev_nr_sectors(sb->s_bdev);
153862306a36Sopenharmony_ci	struct fat_floppy_defaults *fdefaults = NULL;
153962306a36Sopenharmony_ci	int error = -EINVAL;
154062306a36Sopenharmony_ci	unsigned i;
154162306a36Sopenharmony_ci
154262306a36Sopenharmony_ci	/* 16-bit DOS 1.x reliably wrote bootstrap short-jmp code */
154362306a36Sopenharmony_ci	if (b->ignored[0] != 0xeb || b->ignored[2] != 0x90) {
154462306a36Sopenharmony_ci		if (!silent)
154562306a36Sopenharmony_ci			fat_msg(sb, KERN_ERR,
154662306a36Sopenharmony_ci				"%s; no bootstrapping code", notdos1x);
154762306a36Sopenharmony_ci		goto out;
154862306a36Sopenharmony_ci	}
154962306a36Sopenharmony_ci
155062306a36Sopenharmony_ci	/*
155162306a36Sopenharmony_ci	 * If any value in this region is non-zero, it isn't archaic
155262306a36Sopenharmony_ci	 * DOS.
155362306a36Sopenharmony_ci	 */
155462306a36Sopenharmony_ci	if (!fat_bpb_is_zero(b)) {
155562306a36Sopenharmony_ci		if (!silent)
155662306a36Sopenharmony_ci			fat_msg(sb, KERN_ERR,
155762306a36Sopenharmony_ci				"%s; DOS 2.x BPB is non-zero", notdos1x);
155862306a36Sopenharmony_ci		goto out;
155962306a36Sopenharmony_ci	}
156062306a36Sopenharmony_ci
156162306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(floppy_defaults); i++) {
156262306a36Sopenharmony_ci		if (floppy_defaults[i].nr_sectors == bd_sects) {
156362306a36Sopenharmony_ci			fdefaults = &floppy_defaults[i];
156462306a36Sopenharmony_ci			break;
156562306a36Sopenharmony_ci		}
156662306a36Sopenharmony_ci	}
156762306a36Sopenharmony_ci
156862306a36Sopenharmony_ci	if (fdefaults == NULL) {
156962306a36Sopenharmony_ci		if (!silent)
157062306a36Sopenharmony_ci			fat_msg(sb, KERN_WARNING,
157162306a36Sopenharmony_ci				"This looks like a DOS 1.x volume, but isn't a recognized floppy size (%llu sectors)",
157262306a36Sopenharmony_ci				(u64)bd_sects);
157362306a36Sopenharmony_ci		goto out;
157462306a36Sopenharmony_ci	}
157562306a36Sopenharmony_ci
157662306a36Sopenharmony_ci	if (!silent)
157762306a36Sopenharmony_ci		fat_msg(sb, KERN_INFO,
157862306a36Sopenharmony_ci			"This looks like a DOS 1.x volume; assuming default BPB values");
157962306a36Sopenharmony_ci
158062306a36Sopenharmony_ci	memset(bpb, 0, sizeof(*bpb));
158162306a36Sopenharmony_ci	bpb->fat_sector_size = SECTOR_SIZE;
158262306a36Sopenharmony_ci	bpb->fat_sec_per_clus = fdefaults->sec_per_clus;
158362306a36Sopenharmony_ci	bpb->fat_reserved = 1;
158462306a36Sopenharmony_ci	bpb->fat_fats = 2;
158562306a36Sopenharmony_ci	bpb->fat_dir_entries = fdefaults->dir_entries;
158662306a36Sopenharmony_ci	bpb->fat_sectors = fdefaults->nr_sectors;
158762306a36Sopenharmony_ci	bpb->fat_fat_length = fdefaults->fat_length;
158862306a36Sopenharmony_ci
158962306a36Sopenharmony_ci	error = 0;
159062306a36Sopenharmony_ci
159162306a36Sopenharmony_ciout:
159262306a36Sopenharmony_ci	return error;
159362306a36Sopenharmony_ci}
159462306a36Sopenharmony_ci
159562306a36Sopenharmony_ci/*
159662306a36Sopenharmony_ci * Read the super block of an MS-DOS FS.
159762306a36Sopenharmony_ci */
159862306a36Sopenharmony_ciint fat_fill_super(struct super_block *sb, void *data, int silent, int isvfat,
159962306a36Sopenharmony_ci		   void (*setup)(struct super_block *))
160062306a36Sopenharmony_ci{
160162306a36Sopenharmony_ci	struct inode *root_inode = NULL, *fat_inode = NULL;
160262306a36Sopenharmony_ci	struct inode *fsinfo_inode = NULL;
160362306a36Sopenharmony_ci	struct buffer_head *bh;
160462306a36Sopenharmony_ci	struct fat_bios_param_block bpb;
160562306a36Sopenharmony_ci	struct msdos_sb_info *sbi;
160662306a36Sopenharmony_ci	u16 logical_sector_size;
160762306a36Sopenharmony_ci	u32 total_sectors, total_clusters, fat_clusters, rootdir_sectors;
160862306a36Sopenharmony_ci	int debug;
160962306a36Sopenharmony_ci	long error;
161062306a36Sopenharmony_ci	char buf[50];
161162306a36Sopenharmony_ci	struct timespec64 ts;
161262306a36Sopenharmony_ci
161362306a36Sopenharmony_ci	/*
161462306a36Sopenharmony_ci	 * GFP_KERNEL is ok here, because while we do hold the
161562306a36Sopenharmony_ci	 * superblock lock, memory pressure can't call back into
161662306a36Sopenharmony_ci	 * the filesystem, since we're only just about to mount
161762306a36Sopenharmony_ci	 * it and have no inodes etc active!
161862306a36Sopenharmony_ci	 */
161962306a36Sopenharmony_ci	sbi = kzalloc(sizeof(struct msdos_sb_info), GFP_KERNEL);
162062306a36Sopenharmony_ci	if (!sbi)
162162306a36Sopenharmony_ci		return -ENOMEM;
162262306a36Sopenharmony_ci	sb->s_fs_info = sbi;
162362306a36Sopenharmony_ci
162462306a36Sopenharmony_ci	sb->s_flags |= SB_NODIRATIME;
162562306a36Sopenharmony_ci	sb->s_magic = MSDOS_SUPER_MAGIC;
162662306a36Sopenharmony_ci	sb->s_op = &fat_sops;
162762306a36Sopenharmony_ci	sb->s_export_op = &fat_export_ops;
162862306a36Sopenharmony_ci	/*
162962306a36Sopenharmony_ci	 * fat timestamps are complex and truncated by fat itself, so
163062306a36Sopenharmony_ci	 * we set 1 here to be fast
163162306a36Sopenharmony_ci	 */
163262306a36Sopenharmony_ci	sb->s_time_gran = 1;
163362306a36Sopenharmony_ci	mutex_init(&sbi->nfs_build_inode_lock);
163462306a36Sopenharmony_ci	ratelimit_state_init(&sbi->ratelimit, DEFAULT_RATELIMIT_INTERVAL,
163562306a36Sopenharmony_ci			     DEFAULT_RATELIMIT_BURST);
163662306a36Sopenharmony_ci
163762306a36Sopenharmony_ci	error = parse_options(sb, data, isvfat, silent, &debug, &sbi->options);
163862306a36Sopenharmony_ci	if (error)
163962306a36Sopenharmony_ci		goto out_fail;
164062306a36Sopenharmony_ci
164162306a36Sopenharmony_ci	setup(sb); /* flavour-specific stuff that needs options */
164262306a36Sopenharmony_ci
164362306a36Sopenharmony_ci	error = -EIO;
164462306a36Sopenharmony_ci	sb_min_blocksize(sb, 512);
164562306a36Sopenharmony_ci	bh = sb_bread(sb, 0);
164662306a36Sopenharmony_ci	if (bh == NULL) {
164762306a36Sopenharmony_ci		fat_msg(sb, KERN_ERR, "unable to read boot sector");
164862306a36Sopenharmony_ci		goto out_fail;
164962306a36Sopenharmony_ci	}
165062306a36Sopenharmony_ci
165162306a36Sopenharmony_ci	error = fat_read_bpb(sb, (struct fat_boot_sector *)bh->b_data, silent,
165262306a36Sopenharmony_ci		&bpb);
165362306a36Sopenharmony_ci	if (error == -EINVAL && sbi->options.dos1xfloppy)
165462306a36Sopenharmony_ci		error = fat_read_static_bpb(sb,
165562306a36Sopenharmony_ci			(struct fat_boot_sector *)bh->b_data, silent, &bpb);
165662306a36Sopenharmony_ci	brelse(bh);
165762306a36Sopenharmony_ci
165862306a36Sopenharmony_ci	if (error == -EINVAL)
165962306a36Sopenharmony_ci		goto out_invalid;
166062306a36Sopenharmony_ci	else if (error)
166162306a36Sopenharmony_ci		goto out_fail;
166262306a36Sopenharmony_ci
166362306a36Sopenharmony_ci	logical_sector_size = bpb.fat_sector_size;
166462306a36Sopenharmony_ci	sbi->sec_per_clus = bpb.fat_sec_per_clus;
166562306a36Sopenharmony_ci
166662306a36Sopenharmony_ci	error = -EIO;
166762306a36Sopenharmony_ci	if (logical_sector_size < sb->s_blocksize) {
166862306a36Sopenharmony_ci		fat_msg(sb, KERN_ERR, "logical sector size too small for device"
166962306a36Sopenharmony_ci		       " (logical sector size = %u)", logical_sector_size);
167062306a36Sopenharmony_ci		goto out_fail;
167162306a36Sopenharmony_ci	}
167262306a36Sopenharmony_ci
167362306a36Sopenharmony_ci	if (logical_sector_size > sb->s_blocksize) {
167462306a36Sopenharmony_ci		struct buffer_head *bh_resize;
167562306a36Sopenharmony_ci
167662306a36Sopenharmony_ci		if (!sb_set_blocksize(sb, logical_sector_size)) {
167762306a36Sopenharmony_ci			fat_msg(sb, KERN_ERR, "unable to set blocksize %u",
167862306a36Sopenharmony_ci			       logical_sector_size);
167962306a36Sopenharmony_ci			goto out_fail;
168062306a36Sopenharmony_ci		}
168162306a36Sopenharmony_ci
168262306a36Sopenharmony_ci		/* Verify that the larger boot sector is fully readable */
168362306a36Sopenharmony_ci		bh_resize = sb_bread(sb, 0);
168462306a36Sopenharmony_ci		if (bh_resize == NULL) {
168562306a36Sopenharmony_ci			fat_msg(sb, KERN_ERR, "unable to read boot sector"
168662306a36Sopenharmony_ci			       " (logical sector size = %lu)",
168762306a36Sopenharmony_ci			       sb->s_blocksize);
168862306a36Sopenharmony_ci			goto out_fail;
168962306a36Sopenharmony_ci		}
169062306a36Sopenharmony_ci		brelse(bh_resize);
169162306a36Sopenharmony_ci	}
169262306a36Sopenharmony_ci
169362306a36Sopenharmony_ci	mutex_init(&sbi->s_lock);
169462306a36Sopenharmony_ci	sbi->cluster_size = sb->s_blocksize * sbi->sec_per_clus;
169562306a36Sopenharmony_ci	sbi->cluster_bits = ffs(sbi->cluster_size) - 1;
169662306a36Sopenharmony_ci	sbi->fats = bpb.fat_fats;
169762306a36Sopenharmony_ci	sbi->fat_bits = 0;		/* Don't know yet */
169862306a36Sopenharmony_ci	sbi->fat_start = bpb.fat_reserved;
169962306a36Sopenharmony_ci	sbi->fat_length = bpb.fat_fat_length;
170062306a36Sopenharmony_ci	sbi->root_cluster = 0;
170162306a36Sopenharmony_ci	sbi->free_clusters = -1;	/* Don't know yet */
170262306a36Sopenharmony_ci	sbi->free_clus_valid = 0;
170362306a36Sopenharmony_ci	sbi->prev_free = FAT_START_ENT;
170462306a36Sopenharmony_ci	sb->s_maxbytes = 0xffffffff;
170562306a36Sopenharmony_ci	fat_time_fat2unix(sbi, &ts, 0, cpu_to_le16(FAT_DATE_MIN), 0);
170662306a36Sopenharmony_ci	sb->s_time_min = ts.tv_sec;
170762306a36Sopenharmony_ci
170862306a36Sopenharmony_ci	fat_time_fat2unix(sbi, &ts, cpu_to_le16(FAT_TIME_MAX),
170962306a36Sopenharmony_ci			  cpu_to_le16(FAT_DATE_MAX), 0);
171062306a36Sopenharmony_ci	sb->s_time_max = ts.tv_sec;
171162306a36Sopenharmony_ci
171262306a36Sopenharmony_ci	if (!sbi->fat_length && bpb.fat32_length) {
171362306a36Sopenharmony_ci		struct fat_boot_fsinfo *fsinfo;
171462306a36Sopenharmony_ci		struct buffer_head *fsinfo_bh;
171562306a36Sopenharmony_ci
171662306a36Sopenharmony_ci		/* Must be FAT32 */
171762306a36Sopenharmony_ci		sbi->fat_bits = 32;
171862306a36Sopenharmony_ci		sbi->fat_length = bpb.fat32_length;
171962306a36Sopenharmony_ci		sbi->root_cluster = bpb.fat32_root_cluster;
172062306a36Sopenharmony_ci
172162306a36Sopenharmony_ci		/* MC - if info_sector is 0, don't multiply by 0 */
172262306a36Sopenharmony_ci		sbi->fsinfo_sector = bpb.fat32_info_sector;
172362306a36Sopenharmony_ci		if (sbi->fsinfo_sector == 0)
172462306a36Sopenharmony_ci			sbi->fsinfo_sector = 1;
172562306a36Sopenharmony_ci
172662306a36Sopenharmony_ci		fsinfo_bh = sb_bread(sb, sbi->fsinfo_sector);
172762306a36Sopenharmony_ci		if (fsinfo_bh == NULL) {
172862306a36Sopenharmony_ci			fat_msg(sb, KERN_ERR, "bread failed, FSINFO block"
172962306a36Sopenharmony_ci			       " (sector = %lu)", sbi->fsinfo_sector);
173062306a36Sopenharmony_ci			goto out_fail;
173162306a36Sopenharmony_ci		}
173262306a36Sopenharmony_ci
173362306a36Sopenharmony_ci		fsinfo = (struct fat_boot_fsinfo *)fsinfo_bh->b_data;
173462306a36Sopenharmony_ci		if (!IS_FSINFO(fsinfo)) {
173562306a36Sopenharmony_ci			fat_msg(sb, KERN_WARNING, "Invalid FSINFO signature: "
173662306a36Sopenharmony_ci			       "0x%08x, 0x%08x (sector = %lu)",
173762306a36Sopenharmony_ci			       le32_to_cpu(fsinfo->signature1),
173862306a36Sopenharmony_ci			       le32_to_cpu(fsinfo->signature2),
173962306a36Sopenharmony_ci			       sbi->fsinfo_sector);
174062306a36Sopenharmony_ci		} else {
174162306a36Sopenharmony_ci			if (sbi->options.usefree)
174262306a36Sopenharmony_ci				sbi->free_clus_valid = 1;
174362306a36Sopenharmony_ci			sbi->free_clusters = le32_to_cpu(fsinfo->free_clusters);
174462306a36Sopenharmony_ci			sbi->prev_free = le32_to_cpu(fsinfo->next_cluster);
174562306a36Sopenharmony_ci		}
174662306a36Sopenharmony_ci
174762306a36Sopenharmony_ci		brelse(fsinfo_bh);
174862306a36Sopenharmony_ci	}
174962306a36Sopenharmony_ci
175062306a36Sopenharmony_ci	/* interpret volume ID as a little endian 32 bit integer */
175162306a36Sopenharmony_ci	if (is_fat32(sbi))
175262306a36Sopenharmony_ci		sbi->vol_id = bpb.fat32_vol_id;
175362306a36Sopenharmony_ci	else /* fat 16 or 12 */
175462306a36Sopenharmony_ci		sbi->vol_id = bpb.fat16_vol_id;
175562306a36Sopenharmony_ci
175662306a36Sopenharmony_ci	sbi->dir_per_block = sb->s_blocksize / sizeof(struct msdos_dir_entry);
175762306a36Sopenharmony_ci	sbi->dir_per_block_bits = ffs(sbi->dir_per_block) - 1;
175862306a36Sopenharmony_ci
175962306a36Sopenharmony_ci	sbi->dir_start = sbi->fat_start + sbi->fats * sbi->fat_length;
176062306a36Sopenharmony_ci	sbi->dir_entries = bpb.fat_dir_entries;
176162306a36Sopenharmony_ci	if (sbi->dir_entries & (sbi->dir_per_block - 1)) {
176262306a36Sopenharmony_ci		if (!silent)
176362306a36Sopenharmony_ci			fat_msg(sb, KERN_ERR, "bogus number of directory entries"
176462306a36Sopenharmony_ci			       " (%u)", sbi->dir_entries);
176562306a36Sopenharmony_ci		goto out_invalid;
176662306a36Sopenharmony_ci	}
176762306a36Sopenharmony_ci
176862306a36Sopenharmony_ci	rootdir_sectors = sbi->dir_entries
176962306a36Sopenharmony_ci		* sizeof(struct msdos_dir_entry) / sb->s_blocksize;
177062306a36Sopenharmony_ci	sbi->data_start = sbi->dir_start + rootdir_sectors;
177162306a36Sopenharmony_ci	total_sectors = bpb.fat_sectors;
177262306a36Sopenharmony_ci	if (total_sectors == 0)
177362306a36Sopenharmony_ci		total_sectors = bpb.fat_total_sect;
177462306a36Sopenharmony_ci
177562306a36Sopenharmony_ci	total_clusters = (total_sectors - sbi->data_start) / sbi->sec_per_clus;
177662306a36Sopenharmony_ci
177762306a36Sopenharmony_ci	if (!is_fat32(sbi))
177862306a36Sopenharmony_ci		sbi->fat_bits = (total_clusters > MAX_FAT12) ? 16 : 12;
177962306a36Sopenharmony_ci
178062306a36Sopenharmony_ci	/* some OSes set FAT_STATE_DIRTY and clean it on unmount. */
178162306a36Sopenharmony_ci	if (is_fat32(sbi))
178262306a36Sopenharmony_ci		sbi->dirty = bpb.fat32_state & FAT_STATE_DIRTY;
178362306a36Sopenharmony_ci	else /* fat 16 or 12 */
178462306a36Sopenharmony_ci		sbi->dirty = bpb.fat16_state & FAT_STATE_DIRTY;
178562306a36Sopenharmony_ci
178662306a36Sopenharmony_ci	/* check that FAT table does not overflow */
178762306a36Sopenharmony_ci	fat_clusters = calc_fat_clusters(sb);
178862306a36Sopenharmony_ci	total_clusters = min(total_clusters, fat_clusters - FAT_START_ENT);
178962306a36Sopenharmony_ci	if (total_clusters > max_fat(sb)) {
179062306a36Sopenharmony_ci		if (!silent)
179162306a36Sopenharmony_ci			fat_msg(sb, KERN_ERR, "count of clusters too big (%u)",
179262306a36Sopenharmony_ci			       total_clusters);
179362306a36Sopenharmony_ci		goto out_invalid;
179462306a36Sopenharmony_ci	}
179562306a36Sopenharmony_ci
179662306a36Sopenharmony_ci	sbi->max_cluster = total_clusters + FAT_START_ENT;
179762306a36Sopenharmony_ci	/* check the free_clusters, it's not necessarily correct */
179862306a36Sopenharmony_ci	if (sbi->free_clusters != -1 && sbi->free_clusters > total_clusters)
179962306a36Sopenharmony_ci		sbi->free_clusters = -1;
180062306a36Sopenharmony_ci	/* check the prev_free, it's not necessarily correct */
180162306a36Sopenharmony_ci	sbi->prev_free %= sbi->max_cluster;
180262306a36Sopenharmony_ci	if (sbi->prev_free < FAT_START_ENT)
180362306a36Sopenharmony_ci		sbi->prev_free = FAT_START_ENT;
180462306a36Sopenharmony_ci
180562306a36Sopenharmony_ci	/* set up enough so that it can read an inode */
180662306a36Sopenharmony_ci	fat_hash_init(sb);
180762306a36Sopenharmony_ci	dir_hash_init(sb);
180862306a36Sopenharmony_ci	fat_ent_access_init(sb);
180962306a36Sopenharmony_ci
181062306a36Sopenharmony_ci	/*
181162306a36Sopenharmony_ci	 * The low byte of the first FAT entry must have the same value as
181262306a36Sopenharmony_ci	 * the media field of the boot sector. But in real world, too many
181362306a36Sopenharmony_ci	 * devices are writing wrong values. So, removed that validity check.
181462306a36Sopenharmony_ci	 *
181562306a36Sopenharmony_ci	 * The removed check compared the first FAT entry to a value dependent
181662306a36Sopenharmony_ci	 * on the media field like this:
181762306a36Sopenharmony_ci	 * == (0x0F00 | media), for FAT12
181862306a36Sopenharmony_ci	 * == (0XFF00 | media), for FAT16
181962306a36Sopenharmony_ci	 * == (0x0FFFFF | media), for FAT32
182062306a36Sopenharmony_ci	 */
182162306a36Sopenharmony_ci
182262306a36Sopenharmony_ci	error = -EINVAL;
182362306a36Sopenharmony_ci	sprintf(buf, "cp%d", sbi->options.codepage);
182462306a36Sopenharmony_ci	sbi->nls_disk = load_nls(buf);
182562306a36Sopenharmony_ci	if (!sbi->nls_disk) {
182662306a36Sopenharmony_ci		fat_msg(sb, KERN_ERR, "codepage %s not found", buf);
182762306a36Sopenharmony_ci		goto out_fail;
182862306a36Sopenharmony_ci	}
182962306a36Sopenharmony_ci
183062306a36Sopenharmony_ci	/* FIXME: utf8 is using iocharset for upper/lower conversion */
183162306a36Sopenharmony_ci	if (sbi->options.isvfat) {
183262306a36Sopenharmony_ci		sbi->nls_io = load_nls(sbi->options.iocharset);
183362306a36Sopenharmony_ci		if (!sbi->nls_io) {
183462306a36Sopenharmony_ci			fat_msg(sb, KERN_ERR, "IO charset %s not found",
183562306a36Sopenharmony_ci			       sbi->options.iocharset);
183662306a36Sopenharmony_ci			goto out_fail;
183762306a36Sopenharmony_ci		}
183862306a36Sopenharmony_ci	}
183962306a36Sopenharmony_ci
184062306a36Sopenharmony_ci	error = -ENOMEM;
184162306a36Sopenharmony_ci	fat_inode = new_inode(sb);
184262306a36Sopenharmony_ci	if (!fat_inode)
184362306a36Sopenharmony_ci		goto out_fail;
184462306a36Sopenharmony_ci	sbi->fat_inode = fat_inode;
184562306a36Sopenharmony_ci
184662306a36Sopenharmony_ci	fsinfo_inode = new_inode(sb);
184762306a36Sopenharmony_ci	if (!fsinfo_inode)
184862306a36Sopenharmony_ci		goto out_fail;
184962306a36Sopenharmony_ci	fsinfo_inode->i_ino = MSDOS_FSINFO_INO;
185062306a36Sopenharmony_ci	sbi->fsinfo_inode = fsinfo_inode;
185162306a36Sopenharmony_ci	insert_inode_hash(fsinfo_inode);
185262306a36Sopenharmony_ci
185362306a36Sopenharmony_ci	root_inode = new_inode(sb);
185462306a36Sopenharmony_ci	if (!root_inode)
185562306a36Sopenharmony_ci		goto out_fail;
185662306a36Sopenharmony_ci	root_inode->i_ino = MSDOS_ROOT_INO;
185762306a36Sopenharmony_ci	inode_set_iversion(root_inode, 1);
185862306a36Sopenharmony_ci	error = fat_read_root(root_inode);
185962306a36Sopenharmony_ci	if (error < 0) {
186062306a36Sopenharmony_ci		iput(root_inode);
186162306a36Sopenharmony_ci		goto out_fail;
186262306a36Sopenharmony_ci	}
186362306a36Sopenharmony_ci	error = -ENOMEM;
186462306a36Sopenharmony_ci	insert_inode_hash(root_inode);
186562306a36Sopenharmony_ci	fat_attach(root_inode, 0);
186662306a36Sopenharmony_ci	sb->s_root = d_make_root(root_inode);
186762306a36Sopenharmony_ci	if (!sb->s_root) {
186862306a36Sopenharmony_ci		fat_msg(sb, KERN_ERR, "get root inode failed");
186962306a36Sopenharmony_ci		goto out_fail;
187062306a36Sopenharmony_ci	}
187162306a36Sopenharmony_ci
187262306a36Sopenharmony_ci	if (sbi->options.discard && !bdev_max_discard_sectors(sb->s_bdev))
187362306a36Sopenharmony_ci		fat_msg(sb, KERN_WARNING,
187462306a36Sopenharmony_ci			"mounting with \"discard\" option, but the device does not support discard");
187562306a36Sopenharmony_ci
187662306a36Sopenharmony_ci	fat_set_state(sb, 1, 0);
187762306a36Sopenharmony_ci	return 0;
187862306a36Sopenharmony_ci
187962306a36Sopenharmony_ciout_invalid:
188062306a36Sopenharmony_ci	error = -EINVAL;
188162306a36Sopenharmony_ci	if (!silent)
188262306a36Sopenharmony_ci		fat_msg(sb, KERN_INFO, "Can't find a valid FAT filesystem");
188362306a36Sopenharmony_ci
188462306a36Sopenharmony_ciout_fail:
188562306a36Sopenharmony_ci	iput(fsinfo_inode);
188662306a36Sopenharmony_ci	iput(fat_inode);
188762306a36Sopenharmony_ci	unload_nls(sbi->nls_io);
188862306a36Sopenharmony_ci	unload_nls(sbi->nls_disk);
188962306a36Sopenharmony_ci	fat_reset_iocharset(&sbi->options);
189062306a36Sopenharmony_ci	sb->s_fs_info = NULL;
189162306a36Sopenharmony_ci	kfree(sbi);
189262306a36Sopenharmony_ci	return error;
189362306a36Sopenharmony_ci}
189462306a36Sopenharmony_ci
189562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(fat_fill_super);
189662306a36Sopenharmony_ci
189762306a36Sopenharmony_ci/*
189862306a36Sopenharmony_ci * helper function for fat_flush_inodes.  This writes both the inode
189962306a36Sopenharmony_ci * and the file data blocks, waiting for in flight data blocks before
190062306a36Sopenharmony_ci * the start of the call.  It does not wait for any io started
190162306a36Sopenharmony_ci * during the call
190262306a36Sopenharmony_ci */
190362306a36Sopenharmony_cistatic int writeback_inode(struct inode *inode)
190462306a36Sopenharmony_ci{
190562306a36Sopenharmony_ci
190662306a36Sopenharmony_ci	int ret;
190762306a36Sopenharmony_ci
190862306a36Sopenharmony_ci	/* if we used wait=1, sync_inode_metadata waits for the io for the
190962306a36Sopenharmony_ci	* inode to finish.  So wait=0 is sent down to sync_inode_metadata
191062306a36Sopenharmony_ci	* and filemap_fdatawrite is used for the data blocks
191162306a36Sopenharmony_ci	*/
191262306a36Sopenharmony_ci	ret = sync_inode_metadata(inode, 0);
191362306a36Sopenharmony_ci	if (!ret)
191462306a36Sopenharmony_ci		ret = filemap_fdatawrite(inode->i_mapping);
191562306a36Sopenharmony_ci	return ret;
191662306a36Sopenharmony_ci}
191762306a36Sopenharmony_ci
191862306a36Sopenharmony_ci/*
191962306a36Sopenharmony_ci * write data and metadata corresponding to i1 and i2.  The io is
192062306a36Sopenharmony_ci * started but we do not wait for any of it to finish.
192162306a36Sopenharmony_ci *
192262306a36Sopenharmony_ci * filemap_flush is used for the block device, so if there is a dirty
192362306a36Sopenharmony_ci * page for a block already in flight, we will not wait and start the
192462306a36Sopenharmony_ci * io over again
192562306a36Sopenharmony_ci */
192662306a36Sopenharmony_ciint fat_flush_inodes(struct super_block *sb, struct inode *i1, struct inode *i2)
192762306a36Sopenharmony_ci{
192862306a36Sopenharmony_ci	int ret = 0;
192962306a36Sopenharmony_ci	if (!MSDOS_SB(sb)->options.flush)
193062306a36Sopenharmony_ci		return 0;
193162306a36Sopenharmony_ci	if (i1)
193262306a36Sopenharmony_ci		ret = writeback_inode(i1);
193362306a36Sopenharmony_ci	if (!ret && i2)
193462306a36Sopenharmony_ci		ret = writeback_inode(i2);
193562306a36Sopenharmony_ci	if (!ret)
193662306a36Sopenharmony_ci		ret = sync_blockdev_nowait(sb->s_bdev);
193762306a36Sopenharmony_ci	return ret;
193862306a36Sopenharmony_ci}
193962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(fat_flush_inodes);
194062306a36Sopenharmony_ci
194162306a36Sopenharmony_cistatic int __init init_fat_fs(void)
194262306a36Sopenharmony_ci{
194362306a36Sopenharmony_ci	int err;
194462306a36Sopenharmony_ci
194562306a36Sopenharmony_ci	err = fat_cache_init();
194662306a36Sopenharmony_ci	if (err)
194762306a36Sopenharmony_ci		return err;
194862306a36Sopenharmony_ci
194962306a36Sopenharmony_ci	err = fat_init_inodecache();
195062306a36Sopenharmony_ci	if (err)
195162306a36Sopenharmony_ci		goto failed;
195262306a36Sopenharmony_ci
195362306a36Sopenharmony_ci	return 0;
195462306a36Sopenharmony_ci
195562306a36Sopenharmony_cifailed:
195662306a36Sopenharmony_ci	fat_cache_destroy();
195762306a36Sopenharmony_ci	return err;
195862306a36Sopenharmony_ci}
195962306a36Sopenharmony_ci
196062306a36Sopenharmony_cistatic void __exit exit_fat_fs(void)
196162306a36Sopenharmony_ci{
196262306a36Sopenharmony_ci	fat_cache_destroy();
196362306a36Sopenharmony_ci	fat_destroy_inodecache();
196462306a36Sopenharmony_ci}
196562306a36Sopenharmony_ci
196662306a36Sopenharmony_cimodule_init(init_fat_fs)
196762306a36Sopenharmony_cimodule_exit(exit_fat_fs)
196862306a36Sopenharmony_ci
196962306a36Sopenharmony_ciMODULE_LICENSE("GPL");
1970