diff options
Diffstat (limited to 'fs/hfs/super.c')
-rw-r--r-- | fs/hfs/super.c | 527 |
1 files changed, 527 insertions, 0 deletions
diff --git a/fs/hfs/super.c b/fs/hfs/super.c new file mode 100644 index 000000000..897130297 --- /dev/null +++ b/fs/hfs/super.c @@ -0,0 +1,527 @@ +/* + * linux/fs/hfs/super.c + * + * Copyright (C) 1995-1997 Paul H. Hargrove + * This file may be distributed under the terms of the GNU Public License. + * + * This file contains hfs_read_super() some of the 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 *inode); +static void hfs_put_super(struct super_block *); +static int hfs_statfs(struct super_block *, struct statfs *, int); +static void hfs_write_super(struct super_block *); + +/*================ Global variables ================*/ + +static struct super_operations hfs_super_operations = { + hfs_read_inode, /* read_inode */ + NULL, /* write_inode */ + hfs_put_inode, /* put_inode - in inode.c */ + NULL, /* delete inode */ + hfs_notify_change, /* notify_change - in inode.c */ + hfs_put_super, /* put_super */ + hfs_write_super, /* write_super */ + hfs_statfs, /* statfs */ + NULL /* remount_fs */ +}; + +/*================ File-local variables ================*/ + +static struct file_system_type hfs_fs = { + "hfs", + FS_REQUIRES_DEV, + hfs_read_super, + NULL}; + +/*================ 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; + inode->i_op = NULL; +} + + +/* + * 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; + + lock_super(sb); + + 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); + + /* invalidate the superblock */ + sb->s_dev = 0; + + MOD_DEC_USE_COUNT; + + unlock_super(sb); + return; +} + +/* + * 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. + * + * XXX: changed f_files/f_ffree to reflect the fs_ablock/free_ablocks. + */ +static int hfs_statfs(struct super_block *sb, struct statfs *buf, int len) +{ + struct hfs_mdb *mdb = HFS_SB(sb)->s_mdb; + struct statfs tmp; + + tmp.f_type = HFS_SUPER_MAGIC; + tmp.f_bsize = HFS_SECTOR_SIZE; + tmp.f_blocks = mdb->alloc_blksz * mdb->fs_ablocks; + tmp.f_bfree = mdb->alloc_blksz * mdb->free_ablocks; + tmp.f_bavail = tmp.f_bfree; + tmp.f_files = mdb->fs_ablocks; /* According to the statfs manual page, -1 is the */ + tmp.f_ffree = mdb->free_ablocks; /* correct value when the meaning is undefined. */ + tmp.f_namelen = HFS_NAMELEN; + + return copy_to_user(buf, &tmp, len) ? -EFAULT : 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; + 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,"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. + */ +struct super_block *hfs_read_super(struct super_block *s, void *data, + int silent) +{ + struct hfs_mdb *mdb; + struct hfs_cat_key key; + kdev_t dev = s->s_dev; +#ifndef CONFIG_MAC_PARTITION + hfs_s32 part_size, part_start; +#endif + 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; + } + + /* in case someone tries to unload the module while we wait on I/O */ + MOD_INC_USE_COUNT; + + lock_super(s); + + /* set the device driver to 512-byte blocks */ + set_blocksize(dev, HFS_SECTOR_SIZE); + + /* look for a partition table and find the correct partition */ +#ifndef CONFIG_MAC_PARTITION + 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 + mdb = hfs_mdb_get(s, s->s_flags & MS_RDONLY, 0); +#endif + if (!mdb) { + if (!silent) { + printk("VFS: Can't find a HFS filesystem on dev %s.\n", + kdevname(dev)); + } + goto bail2; + } + HFS_SB(s)->s_mdb = mdb; + INIT_LIST_HEAD(&mdb->entry_dirty); + + 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_blocksize_bits = HFS_SECTOR_SIZE_BITS; + s->s_blocksize = HFS_SECTOR_SIZE; + 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; + + /* cache the dentry in the inode */ + s->s_root = + HFS_I(root_inode)->entry->sys_entry[HFS_ITYPE_TO_INT(HFS_ITYPE_NORM)] = + d_alloc_root(root_inode, NULL); + if (!s->s_root) + goto bail_no_root; + + /* HFS_SUPERBLK prevents the root inode from being flushed + * inadvertantly. */ + HFS_I(root_inode)->entry->state = HFS_SUPERBLK; + s->s_root->d_op = &hfs_dentry_operations; + + /* everything's okay */ + unlock_super(s); + return s; + +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); + MOD_DEC_USE_COUNT; + unlock_super(s); +bail3: + s->s_dev = 0; + return NULL; +} + +__initfunc(int init_hfs_fs(void)) +{ + hfs_cat_init(); + return register_filesystem(&hfs_fs); +} + +#ifdef MODULE +int init_module(void) { + int error; + +#if defined(DEBUG_SIZES) || defined(DEBUG_ALL) + hfs_warn("HFS inode: %d bytes available\n", + sizeof(struct ext2_inode_info)-sizeof(struct hfs_inode_info)); + hfs_warn("HFS super_block: %d bytes available\n", + sizeof(struct ext2_sb_info)-sizeof(struct hfs_sb_info)); + if ((sizeof(struct hfs_inode_info)>sizeof(struct ext2_inode_info)) || + (sizeof(struct hfs_sb_info)>sizeof(struct ext2_sb_info))) { + return -ENOMEM; /* well sort of */ + } +#endif + error = init_hfs_fs(); + if (!error) { + /* register_symtab(NULL); */ + } + return error; +} + +void cleanup_module(void) { + hfs_cat_free(); + unregister_filesystem(&hfs_fs); +} +#endif + +#if defined(DEBUG_ALL) || defined(DEBUG_MEM) +long int hfs_alloc = 0; +#endif |