📄 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/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/bitops.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/slab.h>#include <linux/smp_lock.h>#include <asm/uaccess.h>#include <asm/unaligned.h>extern struct cvf_format default_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); list_for_each(walk, p) { 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); if (inode) break; } 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){ if (!is_bad_inode(inode)) { lock_kernel(); inode->i_size = 0; fat_truncate(inode); unlock_kernel(); } clear_inode(inode);}void fat_clear_inode(struct inode *inode){ if (is_bad_inode(inode)) return; 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 *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->shortname = 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")) { printk("FAT: blocksize option is obsolete, " "not supported now\n"); } 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; if (opts->iocharset != NULL) { kfree(opts->iocharset); opts->iocharset = NULL; } buffer = kmalloc(len + 1, GFP_KERNEL); if (buffer != NULL) { 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_generation = 0; 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 += 1 << sbi->cluster_bits; 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 = 1 << sbi->cluster_bits; inode->i_blocks = ((inode->i_size + inode->i_blksize - 1) & ~(inode->i_blksize - 1)) >> 9; 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;}/* * a FAT file handle with fhtype 3 is * 0/ i_ino - for fast, reliable lookup if still in the cache * 1/ i_generation - to see if i_ino is still valid * bit 0 == 0 iff directory * 2/ i_location - if ino has changed, but still in cache * 3/ i_logstart - to semi-verify inode found at i_location * 4/ parent->i_logstart - maybe used to hunt for the file on disc * */struct dentry *fat_fh_to_dentry(struct super_block *sb, __u32 *fh, int len, int fhtype, int parent){ struct inode *inode = NULL; struct list_head *lp; struct dentry *result; if (fhtype != 3) return NULL; if (len < 5) return NULL; if (parent) return NULL; /* We cannot find the parent, It better just *be* there */ inode = iget(sb, fh[0]); if (!inode || is_bad_inode(inode) || inode->i_generation != fh[1]) { if (inode) iput(inode); inode = NULL; } if (!inode) { /* try 2 - see if i_location is in F-d-c * require i_logstart to be the same * Will fail if you truncate and then re-write */ inode = fat_iget(sb, fh[2]); if (inode && MSDOS_I(inode)->i_logstart != fh[3]) { iput(inode); inode = NULL; } } if (!inode) { /* For now, do nothing * What we could do is: * follow the file starting at fh[4], and record * the ".." entry, and the name of the fh[2] entry. * The follow the ".." file finding the next step up. * This way we build a path to the root of * the tree. If this works, we lookup the path and so * get this inode into the cache. * Finally try the fat_iget lookup again * If that fails, then weare totally out of luck * But all that is for another day */ } if (!inode) return ERR_PTR(-ESTALE); /* now to find a dentry. * If possible, get a well-connected one * * Given the way that we found the inode, it *MUST* be * well-connected, but it is easiest to just copy the * code. */ spin_lock(&dcache_lock); for (lp = inode->i_dentry.next; lp != &inode->i_dentry ; lp=lp->next) { result = list_entry(lp,struct dentry, d_alias); if (! (result->d_flags & DCACHE_NFSD_DISCONNECTED)) { dget_locked(result); result->d_vfs_flags |= DCACHE_REFERENCED; spin_unlock(&dcache_lock); iput(inode); return result; } } spin_unlock(&dcache_lock); result = d_alloc_root(inode); if (result == NULL) { iput(inode); return ERR_PTR(-ENOMEM); } result->d_flags |= DCACHE_NFSD_DISCONNECTED; return result; }int fat_dentry_to_fh(struct dentry *de, __u32 *fh, int *lenp, int needparent){ int len = *lenp; struct inode *inode = de->d_inode; if (len < 5) return 255; /* no room */ *lenp = 5; fh[0] = inode->i_ino; fh[1] = inode->i_generation; fh[2] = MSDOS_I(inode)->i_location; fh[3] = MSDOS_I(inode)->i_logstart; fh[4] = MSDOS_I(de->d_parent->d_inode)->i_logstart; return 3;}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,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -