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