📄 inode.c
字号:
/* * linux/fs/fat/inode.c * * Written 1992,1993 by Werner Almesberger * VFAT extensions by Gordon Chaffee, merged with msdos fs by Henrik Storner * Rewritten for the constant inumbers support by Al Viro * * Fixes: * * Max Cohan: Fixed invalid FSINFO offset when info_sector is 0 */#include <linux/config.h>#include <linux/version.h>#define __NO_VERSION__#include <linux/module.h>#include <linux/msdos_fs.h>#include <linux/nls.h>#include <linux/kernel.h>#include <linux/sched.h>#include <linux/errno.h>#include <linux/string.h>#include <linux/major.h>#include <linux/blkdev.h>#include <linux/fs.h>#include <linux/stat.h>#include <linux/locks.h>#include <linux/fat_cvf.h>#include <linux/malloc.h>#include <linux/smp_lock.h>#include "msbuffer.h"#include <asm/uaccess.h>#include <asm/unaligned.h>extern struct cvf_format default_cvf, bigblock_cvf;/* #define FAT_PARANOIA 1 */#define DEBUG_LEVEL 0#ifdef FAT_DEBUG# define PRINTK(x) printk x#else# define PRINTK(x)#endif#if (DEBUG_LEVEL >= 1)# define PRINTK1(x) printk x#else# define PRINTK1(x)#endif/* * New FAT inode stuff. We do the following: * a) i_ino is constant and has nothing with on-disk location. * b) FAT manages its own cache of directory entries. * c) *This* cache is indexed by on-disk location. * d) inode has an associated directory entry, all right, but * it may be unhashed. * e) currently entries are stored within struct inode. That should * change. * f) we deal with races in the following way: * 1. readdir() and lookup() do FAT-dir-cache lookup. * 2. rename() unhashes the F-d-c entry and rehashes it in * a new place. * 3. unlink() and rmdir() unhash F-d-c entry. * 4. fat_write_inode() checks whether the thing is unhashed. * If it is we silently return. If it isn't we do bread(), * check if the location is still valid and retry if it * isn't. Otherwise we do changes. * 5. Spinlock is used to protect hash/unhash/location check/lookup * 6. fat_clear_inode() unhashes the F-d-c entry. * 7. lookup() and readdir() do igrab() if they find a F-d-c entry * and consider negative result as cache miss. */#define FAT_HASH_BITS 8#define FAT_HASH_SIZE (1UL << FAT_HASH_BITS)#define FAT_HASH_MASK (FAT_HASH_SIZE-1)static struct list_head fat_inode_hashtable[FAT_HASH_SIZE];spinlock_t fat_inode_lock = SPIN_LOCK_UNLOCKED;void fat_hash_init(void) { int i; for(i=0;i<FAT_HASH_SIZE;i++) { INIT_LIST_HEAD(&fat_inode_hashtable[i]); }}static inline unsigned long fat_hash(struct super_block *sb, int i_pos){ unsigned long tmp = (unsigned long)i_pos | (unsigned long) sb; tmp = tmp + (tmp >> FAT_HASH_BITS) + (tmp >> FAT_HASH_BITS*2); return tmp & FAT_HASH_MASK;}void fat_attach(struct inode *inode, int i_pos) { spin_lock(&fat_inode_lock); MSDOS_I(inode)->i_location = i_pos; list_add(&MSDOS_I(inode)->i_fat_hash, fat_inode_hashtable+fat_hash(inode->i_sb, i_pos)); spin_unlock(&fat_inode_lock);}void fat_detach(struct inode *inode) { spin_lock(&fat_inode_lock); MSDOS_I(inode)->i_location = 0; list_del(&MSDOS_I(inode)->i_fat_hash); INIT_LIST_HEAD(&MSDOS_I(inode)->i_fat_hash); spin_unlock(&fat_inode_lock);}struct inode *fat_iget(struct super_block *sb, int i_pos) { struct list_head *p = fat_inode_hashtable + fat_hash(sb, i_pos); struct list_head *walk; struct msdos_inode_info *i; struct inode *inode = NULL; spin_lock(&fat_inode_lock); for(walk=p->next;walk!=p;walk=walk->next) { i = list_entry(walk, struct msdos_inode_info, i_fat_hash); if (i->i_fat_inode->i_sb != sb) continue; if (i->i_location != i_pos) continue; inode = igrab(i->i_fat_inode); } spin_unlock(&fat_inode_lock); return inode;}static void fat_fill_inode(struct inode *inode, struct msdos_dir_entry *de);struct inode *fat_build_inode(struct super_block *sb, struct msdos_dir_entry *de, int ino, int *res){ struct inode *inode; *res = 0; inode = fat_iget(sb, ino); if (inode) goto out; inode = new_inode(sb); *res = -ENOMEM; if (!inode) goto out; *res = 0; inode->i_ino = iunique(sb, MSDOS_ROOT_INO); fat_fill_inode(inode, de); fat_attach(inode, ino); insert_inode_hash(inode);out: return inode;}void fat_delete_inode(struct inode *inode){ lock_kernel(); inode->i_size = 0; fat_truncate(inode); unlock_kernel(); clear_inode(inode);}void fat_clear_inode(struct inode *inode){ lock_kernel(); spin_lock(&fat_inode_lock); fat_cache_inval_inode(inode); list_del(&MSDOS_I(inode)->i_fat_hash); spin_unlock(&fat_inode_lock); unlock_kernel();}void fat_put_super(struct super_block *sb){ if (MSDOS_SB(sb)->cvf_format->cvf_version) { dec_cvf_format_use_count_by_version(MSDOS_SB(sb)->cvf_format->cvf_version); MSDOS_SB(sb)->cvf_format->unmount_cvf(sb); } if (MSDOS_SB(sb)->fat_bits == 32) { fat_clusters_flush(sb); } fat_cache_inval_dev(sb->s_dev); set_blocksize (sb->s_dev,BLOCK_SIZE); if (MSDOS_SB(sb)->nls_disk) { unload_nls(MSDOS_SB(sb)->nls_disk); MSDOS_SB(sb)->nls_disk = NULL; MSDOS_SB(sb)->options.codepage = 0; } if (MSDOS_SB(sb)->nls_io) { unload_nls(MSDOS_SB(sb)->nls_io); MSDOS_SB(sb)->nls_io = NULL; } /* * Note: the iocharset option might have been specified * without enabling nls_io, so check for it here. */ if (MSDOS_SB(sb)->options.iocharset) { kfree(MSDOS_SB(sb)->options.iocharset); MSDOS_SB(sb)->options.iocharset = NULL; }}static int parse_options(char *options,int *fat, int *blksize, int *debug, struct fat_mount_options *opts, char *cvf_format, char *cvf_options){ char *this_char,*value,save,*savep; char *p; int ret = 1, len; opts->name_check = 'n'; opts->conversion = 'b'; opts->fs_uid = current->uid; opts->fs_gid = current->gid; opts->fs_umask = current->fs->umask; opts->quiet = opts->sys_immutable = opts->dotsOK = opts->showexec = 0; opts->codepage = 0; opts->nocase = 0; opts->utf8 = 0; opts->iocharset = NULL; *debug = *fat = 0; if (!options) goto out; save = 0; savep = NULL; for (this_char = strtok(options,","); this_char; this_char = strtok(NULL,",")) { if ((value = strchr(this_char,'=')) != NULL) { save = *value; savep = value; *value++ = 0; } if (!strcmp(this_char,"check") && value) { if (value[0] && !value[1] && strchr("rns",*value)) opts->name_check = *value; else if (!strcmp(value,"relaxed")) opts->name_check = 'r'; else if (!strcmp(value,"normal")) opts->name_check = 'n'; else if (!strcmp(value,"strict")) opts->name_check = 's'; else ret = 0; } else if (!strcmp(this_char,"conv") && value) { if (value[0] && !value[1] && strchr("bta",*value)) opts->conversion = *value; else if (!strcmp(value,"binary")) opts->conversion = 'b'; else if (!strcmp(value,"text")) opts->conversion = 't'; else if (!strcmp(value,"auto")) opts->conversion = 'a'; else ret = 0; } else if (!strcmp(this_char,"dots")) { opts->dotsOK = 1; } else if (!strcmp(this_char,"nocase")) { opts->nocase = 1; } else if (!strcmp(this_char,"nodots")) { opts->dotsOK = 0; } else if (!strcmp(this_char,"showexec")) { opts->showexec = 1; } else if (!strcmp(this_char,"dotsOK") && value) { if (!strcmp(value,"yes")) opts->dotsOK = 1; else if (!strcmp(value,"no")) opts->dotsOK = 0; else ret = 0; } else if (!strcmp(this_char,"uid")) { if (!value || !*value) ret = 0; else { opts->fs_uid = simple_strtoul(value,&value,0); if (*value) ret = 0; } } else if (!strcmp(this_char,"gid")) { if (!value || !*value) ret= 0; else { opts->fs_gid = simple_strtoul(value,&value,0); if (*value) ret = 0; } } else if (!strcmp(this_char,"umask")) { if (!value || !*value) ret = 0; else { opts->fs_umask = simple_strtoul(value,&value,8); if (*value) ret = 0; } } else if (!strcmp(this_char,"debug")) { if (value) ret = 0; else *debug = 1; } else if (!strcmp(this_char,"fat")) { if (!value || !*value) ret = 0; else { *fat = simple_strtoul(value,&value,0); if (*value || (*fat != 12 && *fat != 16 && *fat != 32)) ret = 0; } } else if (!strcmp(this_char,"quiet")) { if (value) ret = 0; else opts->quiet = 1; } else if (!strcmp(this_char,"blocksize")) { if (!value || !*value) ret = 0; else { *blksize = simple_strtoul(value,&value,0); if (*value || (*blksize != 512 && *blksize != 1024 && *blksize != 2048)) ret = 0; } } else if (!strcmp(this_char,"sys_immutable")) { if (value) ret = 0; else opts->sys_immutable = 1; } else if (!strcmp(this_char,"codepage") && value) { opts->codepage = simple_strtoul(value,&value,0); if (*value) ret = 0; else printk ("MSDOS FS: Using codepage %d\n", opts->codepage); } else if (!strcmp(this_char,"iocharset") && value) { p = value; while (*value && *value != ',') value++; len = value - p; if (len) { char * buffer = kmalloc(len+1, GFP_KERNEL); if (buffer) { opts->iocharset = buffer; memcpy(buffer, p, len); buffer[len] = 0; printk("MSDOS FS: IO charset %s\n", buffer); } else ret = 0; } } else if (!strcmp(this_char,"cvf_format")) { if (!value) return 0; strncpy(cvf_format,value,20); } else if (!strcmp(this_char,"cvf_options")) { if (!value) return 0; strncpy(cvf_options,value,100); } if (this_char != options) *(this_char-1) = ','; if (value) *savep = save; if (ret == 0) break; }out: return ret;}static void fat_read_root(struct inode *inode){ struct super_block *sb = inode->i_sb; struct msdos_sb_info *sbi = MSDOS_SB(sb); int nr; INIT_LIST_HEAD(&MSDOS_I(inode)->i_fat_hash); MSDOS_I(inode)->i_location = 0; MSDOS_I(inode)->i_fat_inode = inode; inode->i_uid = sbi->options.fs_uid; inode->i_gid = sbi->options.fs_gid; inode->i_version = ++event; inode->i_mode = (S_IRWXUGO & ~sbi->options.fs_umask) | S_IFDIR; inode->i_op = sbi->dir_ops; inode->i_fop = &fat_dir_operations; if (sbi->fat_bits == 32) { MSDOS_I(inode)->i_start = sbi->root_cluster; if ((nr = MSDOS_I(inode)->i_start) != 0) { while (nr != -1) { inode->i_size += SECTOR_SIZE*sbi->cluster_size; if (!(nr = fat_access(sb,nr,-1))) { printk("Directory %ld: bad FAT\n", inode->i_ino); break; } } } } else { MSDOS_I(inode)->i_start = 0; inode->i_size = sbi->dir_entries* sizeof(struct msdos_dir_entry); } inode->i_blksize = sbi->cluster_size* SECTOR_SIZE; inode->i_blocks = ((inode->i_size+inode->i_blksize-1)>>sbi->cluster_bits) * sbi->cluster_size; MSDOS_I(inode)->i_logstart = 0; MSDOS_I(inode)->mmu_private = inode->i_size; MSDOS_I(inode)->i_attrs = 0; inode->i_mtime = inode->i_atime = inode->i_ctime = 0; MSDOS_I(inode)->i_ctime_ms = 0; inode->i_nlink = fat_subdirs(inode)+2;}static struct super_operations fat_sops = { write_inode: fat_write_inode, delete_inode: fat_delete_inode, put_super: fat_put_super, statfs: fat_statfs, clear_inode: fat_clear_inode,};/* * Read the super block of an MS-DOS FS. * * Note that this may be called from vfat_read_super * with some fields already initialized. */struct super_block *fat_read_super(struct super_block *sb, void *data, int silent, struct inode_operations *fs_dir_inode_ops){ struct inode *root_inode; struct buffer_head *bh; struct fat_boot_sector *b; struct msdos_sb_info *sbi = MSDOS_SB(sb); char *p; int data_sectors,logical_sector_size,sector_mult,fat_clusters=0; int debug,error,fat,cp; int blksize = 512; int fat32; struct fat_mount_options opts; char buf[50]; int i; char cvf_format[21]; char cvf_options[101]; cvf_format[0] = '\0'; cvf_options[0] = '\0'; sbi->cvf_format = NULL; sbi->private_data = NULL; sbi->dir_ops = fs_dir_inode_ops; sb->s_op = &fat_sops; if (hardsect_size[MAJOR(sb->s_dev)] != NULL){ blksize = hardsect_size[MAJOR(sb->s_dev)][MINOR(sb->s_dev)]; if (blksize != 512){ printk ("MSDOS: Hardware sector size is %d\n",blksize); } } opts.isvfat = sbi->options.isvfat; if (!parse_options((char *) data, &fat, &blksize, &debug, &opts, cvf_format, cvf_options) || (blksize != 512 && blksize != 1024 && blksize != 2048)) goto out_fail; /* N.B. we should parse directly into the sb structure */ memcpy(&(sbi->options), &opts, sizeof(struct fat_mount_options)); fat_cache_init(); if( blksize > 1024 ) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -