+ if (sbi->nls_disk) {
+ unload_nls(sbi->nls_disk);
+ sbi->nls_disk = NULL;
+ sbi->options.codepage = fat_default_codepage;
+ }
+ if (sbi->nls_io) {
+ unload_nls(sbi->nls_io);
+ sbi->nls_io = NULL;
+ }
+ if (sbi->options.iocharset != fat_default_iocharset) {
+ kfree(sbi->options.iocharset);
+ sbi->options.iocharset = fat_default_iocharset;
+ }
+
+ sb->s_fs_info = NULL;
+ kfree(sbi);
+}
+
+static kmem_cache_t *fat_inode_cachep;
+
+static struct inode *fat_alloc_inode(struct super_block *sb)
+{
+ struct msdos_inode_info *ei;
+ ei = kmem_cache_alloc(fat_inode_cachep, SLAB_KERNEL);
+ if (!ei)
+ return NULL;
+ return &ei->vfs_inode;
+}
+
+static void fat_destroy_inode(struct inode *inode)
+{
+ kmem_cache_free(fat_inode_cachep, MSDOS_I(inode));
+}
+
+static void init_once(void * foo, kmem_cache_t * cachep, unsigned long flags)
+{
+ struct msdos_inode_info *ei = (struct msdos_inode_info *)foo;
+
+ if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) ==
+ SLAB_CTOR_CONSTRUCTOR) {
+ spin_lock_init(&ei->cache_lru_lock);
+ ei->nr_caches = 0;
+ ei->cache_valid_id = FAT_CACHE_VALID + 1;
+ INIT_LIST_HEAD(&ei->cache_lru);
+ INIT_HLIST_NODE(&ei->i_fat_hash);
+ inode_init_once(&ei->vfs_inode);
+ }
+}
+
+static int __init fat_init_inodecache(void)
+{
+ fat_inode_cachep = kmem_cache_create("fat_inode_cache",
+ sizeof(struct msdos_inode_info),
+ 0, SLAB_RECLAIM_ACCOUNT,
+ init_once, NULL);
+ if (fat_inode_cachep == NULL)
+ return -ENOMEM;
+ return 0;
+}
+
+static void __exit fat_destroy_inodecache(void)
+{
+ if (kmem_cache_destroy(fat_inode_cachep))
+ printk(KERN_INFO "fat_inode_cache: not all structures were freed\n");
+}
+
+static int fat_remount(struct super_block *sb, int *flags, char *data)
+{
+ *flags |= MS_NODIRATIME;
+ return 0;
+}
+
+static int fat_statfs(struct super_block *sb, struct kstatfs *buf)
+{
+ struct msdos_sb_info *sbi = MSDOS_SB(sb);
+ int free, nr, ret;
+
+ if (sbi->free_clusters != -1)
+ free = sbi->free_clusters;
+ else {
+ lock_fat(sb);
+ if (sbi->free_clusters != -1)
+ free = sbi->free_clusters;
+ else {
+ free = 0;
+ for (nr = FAT_START_ENT; nr < sbi->max_cluster; nr++) {
+ ret = fat_access(sb, nr, -1);
+ if (ret < 0) {
+ unlock_fat(sb);
+ return ret;
+ } else if (ret == FAT_ENT_FREE)
+ free++;
+ }
+ sbi->free_clusters = free;
+ }
+ unlock_fat(sb);
+ }
+
+ buf->f_type = sb->s_magic;
+ buf->f_bsize = sbi->cluster_size;
+ buf->f_blocks = sbi->max_cluster - FAT_START_ENT;
+ buf->f_bfree = free;
+ buf->f_bavail = free;
+ buf->f_namelen = sbi->options.isvfat ? 260 : 12;
+
+ return 0;
+}
+
+static int fat_write_inode(struct inode *inode, int wait)
+{
+ struct super_block *sb = inode->i_sb;
+ struct msdos_sb_info *sbi = MSDOS_SB(sb);
+ struct buffer_head *bh;
+ struct msdos_dir_entry *raw_entry;
+ loff_t i_pos;
+
+retry:
+ i_pos = MSDOS_I(inode)->i_pos;
+ if (inode->i_ino == MSDOS_ROOT_INO || !i_pos) {
+ return 0;
+ }
+ lock_kernel();
+ if (!(bh = sb_bread(sb, i_pos >> sbi->dir_per_block_bits))) {
+ printk(KERN_ERR "FAT: unable to read inode block "
+ "for updating (i_pos %lld)\n", i_pos);
+ unlock_kernel();
+ return -EIO;
+ }
+ spin_lock(&sbi->inode_hash_lock);
+ if (i_pos != MSDOS_I(inode)->i_pos) {
+ spin_unlock(&sbi->inode_hash_lock);
+ brelse(bh);
+ unlock_kernel();
+ goto retry;
+ }
+
+ raw_entry = &((struct msdos_dir_entry *) (bh->b_data))
+ [i_pos & (sbi->dir_per_block - 1)];
+ if (S_ISDIR(inode->i_mode)) {
+ raw_entry->attr = ATTR_DIR;
+ raw_entry->size = 0;
+ }
+ else {
+ raw_entry->attr = ATTR_NONE;
+ raw_entry->size = cpu_to_le32(inode->i_size);
+ }
+ raw_entry->attr |= MSDOS_MKATTR(inode->i_mode) |
+ MSDOS_I(inode)->i_attrs;
+ raw_entry->start = cpu_to_le16(MSDOS_I(inode)->i_logstart);
+ raw_entry->starthi = cpu_to_le16(MSDOS_I(inode)->i_logstart >> 16);
+ fat_date_unix2dos(inode->i_mtime.tv_sec, &raw_entry->time, &raw_entry->date);
+ if (sbi->options.isvfat) {
+ fat_date_unix2dos(inode->i_ctime.tv_sec,&raw_entry->ctime,&raw_entry->cdate);
+ raw_entry->ctime_ms = MSDOS_I(inode)->i_ctime_ms; /* use i_ctime.tv_nsec? */
+ }
+ spin_unlock(&sbi->inode_hash_lock);
+ mark_buffer_dirty(bh);
+ brelse(bh);
+ unlock_kernel();
+ return 0;
+}
+
+static int fat_show_options(struct seq_file *m, struct vfsmount *mnt);
+static struct super_operations fat_sops = {
+ .alloc_inode = fat_alloc_inode,
+ .destroy_inode = fat_destroy_inode,
+ .write_inode = fat_write_inode,
+ .delete_inode = fat_delete_inode,
+ .put_super = fat_put_super,
+ .statfs = fat_statfs,
+ .clear_inode = fat_clear_inode,
+ .remount_fs = fat_remount,
+
+ .read_inode = make_bad_inode,
+
+ .show_options = fat_show_options,
+};
+
+/*
+ * 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_pos(8-39) - if ino has changed, but still in cache
+ * 3/ i_pos(4-7)|i_logstart - to semi-verify inode found at i_pos
+ * 4/ i_pos(0-3)|parent->i_logstart - maybe used to hunt for the file on disc
+ *
+ * Hack for NFSv2: Maximum FAT entry number is 28bits and maximum
+ * i_pos is 40bits (blocknr(32) + dir offset(8)), so two 4bits
+ * of i_logstart is used to store the directory entry offset.
+ */
+
+static struct dentry *
+fat_decode_fh(struct super_block *sb, __u32 *fh, int len, int fhtype,
+ int (*acceptable)(void *context, struct dentry *de),
+ void *context)
+{
+ if (fhtype != 3)
+ return ERR_PTR(-ESTALE);
+ if (len < 5)
+ return ERR_PTR(-ESTALE);
+
+ return sb->s_export_op->find_exported_dentry(sb, fh, NULL, acceptable, context);
+}
+
+static struct dentry *fat_get_dentry(struct super_block *sb, void *inump)
+{
+ struct inode *inode = NULL;
+ struct dentry *result;
+ __u32 *fh = inump;
+
+ inode = iget(sb, fh[0]);
+ if (!inode || is_bad_inode(inode) || inode->i_generation != fh[1]) {
+ if (inode)
+ iput(inode);
+ inode = NULL;
+ }
+ if (!inode) {
+ loff_t i_pos;
+ int i_logstart = fh[3] & 0x0fffffff;
+
+ i_pos = (loff_t)fh[2] << 8;
+ i_pos |= ((fh[3] >> 24) & 0xf0) | (fh[4] >> 28);
+
+ /* try 2 - see if i_pos 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, i_pos);
+ if (inode && MSDOS_I(inode)->i_logstart != i_logstart) {
+ 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
+ */
+ result = d_alloc_anon(inode);
+ if (result == NULL) {
+ iput(inode);
+ return ERR_PTR(-ENOMEM);
+ }
+ result->d_op = sb->s_root->d_op;
+ return result;
+}
+
+static int
+fat_encode_fh(struct dentry *de, __u32 *fh, int *lenp, int connectable)
+{
+ int len = *lenp;
+ struct inode *inode = de->d_inode;
+ u32 ipos_h, ipos_m, ipos_l;
+
+ if (len < 5)
+ return 255; /* no room */
+
+ ipos_h = MSDOS_I(inode)->i_pos >> 8;
+ ipos_m = (MSDOS_I(inode)->i_pos & 0xf0) << 24;
+ ipos_l = (MSDOS_I(inode)->i_pos & 0x0f) << 28;
+ *lenp = 5;
+ fh[0] = inode->i_ino;
+ fh[1] = inode->i_generation;
+ fh[2] = ipos_h;
+ fh[3] = ipos_m | MSDOS_I(inode)->i_logstart;
+ spin_lock(&de->d_lock);
+ fh[4] = ipos_l | MSDOS_I(de->d_parent->d_inode)->i_logstart;
+ spin_unlock(&de->d_lock);
+ return 3;
+}
+
+static struct dentry *fat_get_parent(struct dentry *child)
+{
+ struct buffer_head *bh=NULL;
+ struct msdos_dir_entry *de = NULL;
+ struct dentry *parent = NULL;
+ int res;
+ loff_t i_pos = 0;
+ struct inode *inode;
+
+ lock_kernel();
+ res = fat_scan(child->d_inode, MSDOS_DOTDOT, &bh, &de, &i_pos);
+
+ if (res < 0)
+ goto out;
+ inode = fat_build_inode(child->d_sb, de, i_pos, &res);
+ if (res)
+ goto out;
+ if (!inode)
+ res = -EACCES;
+ else {
+ parent = d_alloc_anon(inode);
+ if (!parent) {
+ iput(inode);
+ res = -ENOMEM;
+ }