| /* |
| * linux/fs/hfs/super.c |
| * |
| * Copyright (C) 1995-1997 Paul H. Hargrove |
| * This file may be distributed under the terms of the GNU General Public License. |
| * |
| * This file contains hfs_read_super(), some of the super_ops and |
| * init_module() and cleanup_module(). The remaining super_ops are in |
| * inode.c since they deal with inodes. |
| * |
| * Based on the minix file system code, (C) 1991, 1992 by Linus Torvalds |
| * |
| * "XXX" in a comment is a note to myself to consider changing something. |
| * |
| * In function preconditions the term "valid" applied to a pointer to |
| * a structure means that the pointer is non-NULL and the structure it |
| * points to has all fields initialized to consistent values. |
| * |
| * The code in this file initializes some structures which contain |
| * pointers by calling memset(&foo, 0, sizeof(foo)). |
| * This produces the desired behavior only due to the non-ANSI |
| * assumption that the machine representation of NULL is all zeros. |
| */ |
| |
| #include "hfs.h" |
| #include <linux/hfs_fs_sb.h> |
| #include <linux/hfs_fs_i.h> |
| #include <linux/hfs_fs.h> |
| |
| #include <linux/config.h> /* for CONFIG_MAC_PARTITION */ |
| #include <linux/blkdev.h> |
| #include <linux/module.h> |
| #include <linux/init.h> |
| |
| /*================ Forward declarations ================*/ |
| |
| static void hfs_read_inode(struct inode *); |
| static void hfs_put_super(struct super_block *); |
| static int hfs_statfs(struct super_block *, struct statfs *); |
| static void hfs_write_super(struct super_block *); |
| |
| static kmem_cache_t * hfs_inode_cachep; |
| |
| static struct inode *hfs_alloc_inode(struct super_block *sb) |
| { |
| struct hfs_inode_info *ei; |
| ei = (struct hfs_inode_info *)kmem_cache_alloc(hfs_inode_cachep, SLAB_KERNEL); |
| if (!ei) |
| return NULL; |
| return &ei->vfs_inode; |
| } |
| |
| static void hfs_destroy_inode(struct inode *inode) |
| { |
| kmem_cache_free(hfs_inode_cachep, HFS_I(inode)); |
| } |
| |
| static void init_once(void * foo, kmem_cache_t * cachep, unsigned long flags) |
| { |
| struct hfs_inode_info *ei = (struct hfs_inode_info *) foo; |
| |
| if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) == |
| SLAB_CTOR_CONSTRUCTOR) |
| inode_init_once(&ei->vfs_inode); |
| } |
| |
| static int init_inodecache(void) |
| { |
| hfs_inode_cachep = kmem_cache_create("hfs_inode_cache", |
| sizeof(struct hfs_inode_info), |
| 0, SLAB_HWCACHE_ALIGN, |
| init_once, NULL); |
| if (hfs_inode_cachep == NULL) |
| return -ENOMEM; |
| return 0; |
| } |
| |
| static void destroy_inodecache(void) |
| { |
| if (kmem_cache_destroy(hfs_inode_cachep)) |
| printk(KERN_INFO "hfs_inode_cache: not all structures were freed\n"); |
| } |
| |
| /*================ Global variables ================*/ |
| |
| static struct super_operations hfs_super_operations = { |
| alloc_inode: hfs_alloc_inode, |
| destroy_inode: hfs_destroy_inode, |
| read_inode: hfs_read_inode, |
| put_inode: hfs_put_inode, |
| put_super: hfs_put_super, |
| write_super: hfs_write_super, |
| statfs: hfs_statfs, |
| }; |
| |
| /*================ File-local variables ================*/ |
| |
| static struct super_block *hfs_get_sb(struct file_system_type *fs_type, |
| int flags, char *dev_name, void *data) |
| { |
| return get_sb_bdev(fs_type, flags, dev_name, data, hfs_fill_super); |
| } |
| |
| static struct file_system_type hfs_fs = { |
| owner: THIS_MODULE, |
| name: "hfs", |
| get_sb: hfs_get_sb, |
| fs_flags: FS_REQUIRES_DEV, |
| }; |
| |
| /*================ File-local functions ================*/ |
| |
| /* |
| * hfs_read_inode() |
| * |
| * this doesn't actually do much. hfs_iget actually fills in the |
| * necessary inode information. |
| */ |
| static void hfs_read_inode(struct inode *inode) |
| { |
| inode->i_mode = 0; |
| } |
| |
| /* |
| * hfs_write_super() |
| * |
| * Description: |
| * This function is called by the VFS only. When the filesystem |
| * is mounted r/w it updates the MDB on disk. |
| * Input Variable(s): |
| * struct super_block *sb: Pointer to the hfs superblock |
| * Output Variable(s): |
| * NONE |
| * Returns: |
| * void |
| * Preconditions: |
| * 'sb' points to a "valid" (struct super_block). |
| * Postconditions: |
| * The MDB is marked 'unsuccessfully unmounted' by clearing bit 8 of drAtrb |
| * (hfs_put_super() must set this flag!). Some MDB fields are updated |
| * and the MDB buffer is written to disk by calling hfs_mdb_commit(). |
| */ |
| static void hfs_write_super(struct super_block *sb) |
| { |
| struct hfs_mdb *mdb = HFS_SB(sb)->s_mdb; |
| |
| /* is this a valid hfs superblock? */ |
| if (!sb || sb->s_magic != HFS_SUPER_MAGIC) { |
| return; |
| } |
| |
| if (!(sb->s_flags & MS_RDONLY)) { |
| /* sync everything to the buffers */ |
| hfs_mdb_commit(mdb, 0); |
| } |
| sb->s_dirt = 0; |
| } |
| |
| /* |
| * hfs_put_super() |
| * |
| * This is the put_super() entry in the super_operations structure for |
| * HFS filesystems. The purpose is to release the resources |
| * associated with the superblock sb. |
| */ |
| static void hfs_put_super(struct super_block *sb) |
| { |
| struct hfs_mdb *mdb = HFS_SB(sb)->s_mdb; |
| |
| if (!(sb->s_flags & MS_RDONLY)) { |
| hfs_mdb_commit(mdb, 0); |
| sb->s_dirt = 0; |
| } |
| |
| /* release the MDB's resources */ |
| hfs_mdb_put(mdb, sb->s_flags & MS_RDONLY); |
| |
| /* restore default blocksize for the device */ |
| set_blocksize(sb->s_dev, BLOCK_SIZE); |
| } |
| |
| /* |
| * hfs_statfs() |
| * |
| * This is the statfs() entry in the super_operations structure for |
| * HFS filesystems. The purpose is to return various data about the |
| * filesystem. |
| * |
| * changed f_files/f_ffree to reflect the fs_ablock/free_ablocks. |
| */ |
| static int hfs_statfs(struct super_block *sb, struct statfs *buf) |
| { |
| struct hfs_mdb *mdb = HFS_SB(sb)->s_mdb; |
| |
| buf->f_type = HFS_SUPER_MAGIC; |
| buf->f_bsize = HFS_SECTOR_SIZE; |
| buf->f_blocks = mdb->alloc_blksz * mdb->fs_ablocks; |
| buf->f_bfree = mdb->alloc_blksz * mdb->free_ablocks; |
| buf->f_bavail = buf->f_bfree; |
| buf->f_files = mdb->fs_ablocks; |
| buf->f_ffree = mdb->free_ablocks; |
| buf->f_namelen = HFS_NAMELEN; |
| |
| return 0; |
| } |
| |
| /* |
| * parse_options() |
| * |
| * adapted from linux/fs/msdos/inode.c written 1992,93 by Werner Almesberger |
| * This function is called by hfs_read_super() to parse the mount options. |
| */ |
| static int parse_options(char *options, struct hfs_sb_info *hsb, int *part) |
| { |
| char *this_char, *value; |
| char names, fork; |
| |
| /* initialize the sb with defaults */ |
| memset(hsb, 0, sizeof(*hsb)); |
| hsb->magic = HFS_SB_MAGIC; |
| hsb->s_uid = current->uid; |
| hsb->s_gid = current->gid; |
| hsb->s_umask = current->fs->umask; |
| hsb->s_type = 0x3f3f3f3f; /* == '????' */ |
| hsb->s_creator = 0x3f3f3f3f; /* == '????' */ |
| hsb->s_lowercase = 0; |
| hsb->s_quiet = 0; |
| hsb->s_afpd = 0; |
| /* default version. 0 just selects the defaults */ |
| hsb->s_version = 0; |
| hsb->s_conv = 'b'; |
| names = '?'; |
| fork = '?'; |
| *part = 0; |
| |
| if (!options) { |
| goto done; |
| } |
| for (this_char = strtok(options,","); this_char; |
| this_char = strtok(NULL,",")) { |
| if ((value = strchr(this_char,'=')) != NULL) { |
| *value++ = 0; |
| } |
| /* Numeric-valued options */ |
| if (!strcmp(this_char, "version")) { |
| if (!value || !*value) { |
| return 0; |
| } |
| hsb->s_version = simple_strtoul(value,&value,0); |
| if (*value) { |
| return 0; |
| } |
| } else if (!strcmp(this_char,"uid")) { |
| if (!value || !*value) { |
| return 0; |
| } |
| hsb->s_uid = simple_strtoul(value,&value,0); |
| if (*value) { |
| return 0; |
| } |
| } else if (!strcmp(this_char,"gid")) { |
| if (!value || !*value) { |
| return 0; |
| } |
| hsb->s_gid = simple_strtoul(value,&value,0); |
| if (*value) { |
| return 0; |
| } |
| } else if (!strcmp(this_char,"umask")) { |
| if (!value || !*value) { |
| return 0; |
| } |
| hsb->s_umask = simple_strtoul(value,&value,8); |
| if (*value) { |
| return 0; |
| } |
| } else if (!strcmp(this_char,"part")) { |
| if (!value || !*value) { |
| return 0; |
| } |
| *part = simple_strtoul(value,&value,0); |
| if (*value) { |
| return 0; |
| } |
| /* String-valued options */ |
| } else if (!strcmp(this_char,"type") && value) { |
| if (strlen(value) != 4) { |
| return 0; |
| } |
| hsb->s_type = hfs_get_nl(value); |
| } else if (!strcmp(this_char,"creator") && value) { |
| if (strlen(value) != 4) { |
| return 0; |
| } |
| hsb->s_creator = hfs_get_nl(value); |
| /* Boolean-valued options */ |
| } else if (!strcmp(this_char,"quiet")) { |
| if (value) { |
| return 0; |
| } |
| hsb->s_quiet = 1; |
| } else if (!strcmp(this_char,"afpd")) { |
| if (value) { |
| return 0; |
| } |
| hsb->s_afpd = 1; |
| /* Multiple choice options */ |
| } else if (!strcmp(this_char,"names") && value) { |
| if ((*value && !value[1] && strchr("ntal78c",*value)) || |
| !strcmp(value,"netatalk") || |
| !strcmp(value,"trivial") || |
| !strcmp(value,"alpha") || |
| !strcmp(value,"latin") || |
| !strcmp(value,"7bit") || |
| !strcmp(value,"8bit") || |
| !strcmp(value,"cap")) { |
| names = *value; |
| } else { |
| return 0; |
| } |
| } else if (!strcmp(this_char,"fork") && value) { |
| if ((*value && !value[1] && strchr("nsdc",*value)) || |
| !strcmp(value,"netatalk") || |
| !strcmp(value,"single") || |
| !strcmp(value,"double") || |
| !strcmp(value,"cap")) { |
| fork = *value; |
| } else { |
| return 0; |
| } |
| } else if (!strcmp(this_char,"case") && value) { |
| if ((*value && !value[1] && strchr("la",*value)) || |
| !strcmp(value,"lower") || |
| !strcmp(value,"asis")) { |
| hsb->s_lowercase = (*value == 'l'); |
| } else { |
| return 0; |
| } |
| } else if (!strcmp(this_char,"conv") && value) { |
| if ((*value && !value[1] && strchr("bta",*value)) || |
| !strcmp(value,"binary") || |
| !strcmp(value,"text") || |
| !strcmp(value,"auto")) { |
| hsb->s_conv = *value; |
| } else { |
| return 0; |
| } |
| } else { |
| return 0; |
| } |
| } |
| |
| done: |
| /* Parse the "fork" and "names" options */ |
| if (fork == '?') { |
| fork = hsb->s_afpd ? 'n' : 'c'; |
| } |
| switch (fork) { |
| default: |
| case 'c': |
| hsb->s_ifill = hfs_cap_ifill; |
| hsb->s_reserved1 = hfs_cap_reserved1; |
| hsb->s_reserved2 = hfs_cap_reserved2; |
| break; |
| |
| case 's': |
| hfs_warn("hfs_fs: AppleSingle not yet implemented.\n"); |
| return 0; |
| /* break; */ |
| |
| case 'd': |
| hsb->s_ifill = hfs_dbl_ifill; |
| hsb->s_reserved1 = hfs_dbl_reserved1; |
| hsb->s_reserved2 = hfs_dbl_reserved2; |
| break; |
| |
| case 'n': |
| hsb->s_ifill = hfs_nat_ifill; |
| hsb->s_reserved1 = hfs_nat_reserved1; |
| hsb->s_reserved2 = hfs_nat_reserved2; |
| break; |
| } |
| |
| if (names == '?') { |
| names = fork; |
| } |
| switch (names) { |
| default: |
| case 'n': |
| hsb->s_nameout = hfs_colon2mac; |
| hsb->s_namein = hfs_mac2nat; |
| break; |
| |
| case 'c': |
| hsb->s_nameout = hfs_colon2mac; |
| hsb->s_namein = hfs_mac2cap; |
| break; |
| |
| case 't': |
| hsb->s_nameout = hfs_triv2mac; |
| hsb->s_namein = hfs_mac2triv; |
| break; |
| |
| case '7': |
| hsb->s_nameout = hfs_prcnt2mac; |
| hsb->s_namein = hfs_mac2seven; |
| break; |
| |
| case '8': |
| hsb->s_nameout = hfs_prcnt2mac; |
| hsb->s_namein = hfs_mac2eight; |
| break; |
| |
| case 'l': |
| hsb->s_nameout = hfs_latin2mac; |
| hsb->s_namein = hfs_mac2latin; |
| break; |
| |
| case 'a': /* 's' and 'd' are unadvertised aliases for 'alpha', */ |
| case 's': /* since 'alpha' is the default if fork=s or fork=d. */ |
| case 'd': /* (It is also helpful for poor typists!) */ |
| hsb->s_nameout = hfs_prcnt2mac; |
| hsb->s_namein = hfs_mac2alpha; |
| break; |
| } |
| |
| return 1; |
| } |
| |
| /*================ Global functions ================*/ |
| |
| /* |
| * hfs_read_super() |
| * |
| * This is the function that is responsible for mounting an HFS |
| * filesystem. It performs all the tasks necessary to get enough data |
| * from the disk to read the root inode. This includes parsing the |
| * mount options, dealing with Macintosh partitions, reading the |
| * superblock and the allocation bitmap blocks, calling |
| * hfs_btree_init() to get the necessary data about the extents and |
| * catalog B-trees and, finally, reading the root inode into memory. |
| */ |
| int hfs_fill_super(struct super_block *s, void *data, int silent) |
| { |
| struct hfs_mdb *mdb; |
| struct hfs_cat_key key; |
| kdev_t dev = s->s_dev; |
| hfs_s32 part_size, part_start; |
| struct inode *root_inode; |
| int part; |
| |
| if (!parse_options((char *)data, HFS_SB(s), &part)) { |
| hfs_warn("hfs_fs: unable to parse mount options.\n"); |
| goto bail3; |
| } |
| |
| /* set the device driver to 512-byte blocks */ |
| sb_set_blocksize(s, HFS_SECTOR_SIZE); |
| |
| #ifdef CONFIG_MAC_PARTITION |
| /* check to see if we're in a partition */ |
| mdb = hfs_mdb_get(s, s->s_flags & MS_RDONLY, 0); |
| |
| /* erk. try parsing the partition table ourselves */ |
| if (!mdb) { |
| if (hfs_part_find(s, part, silent, &part_size, &part_start)) { |
| goto bail2; |
| } |
| mdb = hfs_mdb_get(s, s->s_flags & MS_RDONLY, part_start); |
| } |
| #else |
| if (hfs_part_find(s, part, silent, &part_size, &part_start)) { |
| goto bail2; |
| } |
| |
| mdb = hfs_mdb_get(s, s->s_flags & MS_RDONLY, part_start); |
| #endif |
| |
| if (!mdb) { |
| if (!silent) { |
| hfs_warn("VFS: Can't find a HFS filesystem on dev %s.\n", |
| s->s_id); |
| } |
| goto bail2; |
| } |
| |
| HFS_SB(s)->s_mdb = mdb; |
| if (HFS_ITYPE(mdb->next_id) != 0) { |
| hfs_warn("hfs_fs: too many files.\n"); |
| goto bail1; |
| } |
| |
| s->s_magic = HFS_SUPER_MAGIC; |
| s->s_op = &hfs_super_operations; |
| |
| /* try to get the root inode */ |
| hfs_cat_build_key(htonl(HFS_POR_CNID), |
| (struct hfs_name *)(mdb->vname), &key); |
| |
| root_inode = hfs_iget(hfs_cat_get(mdb, &key), HFS_ITYPE_NORM, NULL); |
| if (!root_inode) |
| goto bail_no_root; |
| |
| s->s_root = d_alloc_root(root_inode); |
| if (!s->s_root) |
| goto bail_no_root; |
| |
| /* fix up pointers. */ |
| HFS_I(root_inode)->entry->sys_entry[HFS_ITYPE_TO_INT(HFS_ITYPE_NORM)] = |
| s->s_root; |
| s->s_root->d_op = &hfs_dentry_operations; |
| |
| /* everything's okay */ |
| return 0; |
| |
| bail_no_root: |
| hfs_warn("hfs_fs: get root inode failed.\n"); |
| iput(root_inode); |
| bail1: |
| hfs_mdb_put(mdb, s->s_flags & MS_RDONLY); |
| bail2: |
| set_blocksize(dev, BLOCK_SIZE); |
| bail3: |
| return -EINVAL; |
| } |
| |
| static int __init init_hfs_fs(void) |
| { |
| int err = init_inodecache(); |
| if (err) |
| goto out1; |
| hfs_cat_init(); |
| err = register_filesystem(&hfs_fs); |
| if (err) |
| goto out; |
| return 0; |
| out: |
| hfs_cat_free(); |
| destroy_inodecache(); |
| out1: |
| return err; |
| } |
| |
| static void __exit exit_hfs_fs(void) { |
| hfs_cat_free(); |
| unregister_filesystem(&hfs_fs); |
| destroy_inodecache(); |
| } |
| |
| module_init(init_hfs_fs) |
| module_exit(exit_hfs_fs) |
| |
| #if defined(DEBUG_ALL) || defined(DEBUG_MEM) |
| long int hfs_alloc = 0; |
| #endif |