patch-2.1.78 linux/fs/hfs/super.c

Next file: linux/fs/hfs/sysdep.c
Previous file: linux/fs/hfs/string.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.1.77/linux/fs/hfs/super.c linux/fs/hfs/super.c
@@ -0,0 +1,517 @@
+/*
+ * 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/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.
+ */
+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 = -1; /* According to the statfs manual page, -1 is the  */
+	tmp.f_ffree = -1; /* 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;
+
+	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;
+
+	/* 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))
+{
+	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

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov