diff options
Diffstat (limited to 'fs/cramfs/inode.c')
| -rw-r--r-- | fs/cramfs/inode.c | 221 | 
1 files changed, 128 insertions, 93 deletions
diff --git a/fs/cramfs/inode.c b/fs/cramfs/inode.c index 32fd5fe9ca0..ddcfe590b8a 100644 --- a/fs/cramfs/inode.c +++ b/fs/cramfs/inode.c @@ -17,15 +17,30 @@  #include <linux/init.h>  #include <linux/string.h>  #include <linux/blkdev.h> -#include <linux/cramfs_fs.h>  #include <linux/slab.h> -#include <linux/cramfs_fs_sb.h> -#include <linux/buffer_head.h>  #include <linux/vfs.h>  #include <linux/mutex.h> - +#include <uapi/linux/cramfs_fs.h>  #include <asm/uaccess.h> +#include "internal.h" + +/* + * cramfs super-block data in memory + */ +struct cramfs_sb_info { +	unsigned long magic; +	unsigned long size; +	unsigned long blocks; +	unsigned long files; +	unsigned long flags; +}; + +static inline struct cramfs_sb_info *CRAMFS_SB(struct super_block *sb) +{ +	return sb->s_fs_info; +} +  static const struct super_operations cramfs_ops;  static const struct inode_operations cramfs_dir_inode_operations;  static const struct file_operations cramfs_directory_operations; @@ -34,57 +49,81 @@ static const struct address_space_operations cramfs_aops;  static DEFINE_MUTEX(read_mutex); -/* These two macros may change in future, to provide better st_ino -   semantics. */ -#define CRAMINO(x)	(((x)->offset && (x)->size)?(x)->offset<<2:1) +/* These macros may change in future, to provide better st_ino semantics. */  #define OFFSET(x)	((x)->i_ino) -static void setup_inode(struct inode *inode, struct cramfs_inode * cramfs_inode) +static unsigned long cramino(const struct cramfs_inode *cino, unsigned int offset) +{ +	if (!cino->offset) +		return offset + 1; +	if (!cino->size) +		return offset + 1; + +	/* +	 * The file mode test fixes buggy mkcramfs implementations where +	 * cramfs_inode->offset is set to a non zero value for entries +	 * which did not contain data, like devices node and fifos. +	 */ +	switch (cino->mode & S_IFMT) { +	case S_IFREG: +	case S_IFDIR: +	case S_IFLNK: +		return cino->offset << 2; +	default: +		break; +	} +	return offset + 1; +} + +static struct inode *get_cramfs_inode(struct super_block *sb, +	const struct cramfs_inode *cramfs_inode, unsigned int offset)  { +	struct inode *inode;  	static struct timespec zerotime; -	inode->i_mode = cramfs_inode->mode; -	inode->i_uid = cramfs_inode->uid; -	inode->i_size = cramfs_inode->size; -	inode->i_blocks = (cramfs_inode->size - 1) / 512 + 1; -	inode->i_gid = cramfs_inode->gid; -	/* Struct copy intentional */ -	inode->i_mtime = inode->i_atime = inode->i_ctime = zerotime; -	/* inode->i_nlink is left 1 - arguably wrong for directories, -	   but it's the best we can do without reading the directory -	   contents.  1 yields the right result in GNU find, even -	   without -noleaf option. */ -	if (S_ISREG(inode->i_mode)) { + +	inode = iget_locked(sb, cramino(cramfs_inode, offset)); +	if (!inode) +		return ERR_PTR(-ENOMEM); +	if (!(inode->i_state & I_NEW)) +		return inode; + +	switch (cramfs_inode->mode & S_IFMT) { +	case S_IFREG:  		inode->i_fop = &generic_ro_fops;  		inode->i_data.a_ops = &cramfs_aops; -	} else if (S_ISDIR(inode->i_mode)) { +		break; +	case S_IFDIR:  		inode->i_op = &cramfs_dir_inode_operations;  		inode->i_fop = &cramfs_directory_operations; -	} else if (S_ISLNK(inode->i_mode)) { +		break; +	case S_IFLNK:  		inode->i_op = &page_symlink_inode_operations;  		inode->i_data.a_ops = &cramfs_aops; -	} else { -		init_special_inode(inode, inode->i_mode, -			old_decode_dev(cramfs_inode->size)); +		break; +	default: +		init_special_inode(inode, cramfs_inode->mode, +				old_decode_dev(cramfs_inode->size));  	} -} -static struct inode *get_cramfs_inode(struct super_block *sb, -				struct cramfs_inode * cramfs_inode) -{ -	struct inode *inode; -	if (CRAMINO(cramfs_inode) == 1) { -		inode = new_inode(sb); -		if (inode) { -			inode->i_ino = 1; -			setup_inode(inode, cramfs_inode); -		} -	} else { -		inode = iget_locked(sb, CRAMINO(cramfs_inode)); -		if (inode && (inode->i_state & I_NEW)) { -			setup_inode(inode, cramfs_inode); -			unlock_new_inode(inode); -		} +	inode->i_mode = cramfs_inode->mode; +	i_uid_write(inode, cramfs_inode->uid); +	i_gid_write(inode, cramfs_inode->gid); + +	/* if the lower 2 bits are zero, the inode contains data */ +	if (!(inode->i_ino & 3)) { +		inode->i_size = cramfs_inode->size; +		inode->i_blocks = (cramfs_inode->size - 1) / 512 + 1;  	} + +	/* Struct copy intentional */ +	inode->i_mtime = inode->i_atime = inode->i_ctime = zerotime; +	/* inode->i_nlink is left 1 - arguably wrong for directories, +	   but it's the best we can do without reading the directory +	   contents.  1 yields the right result in GNU find, even +	   without -noleaf option. */ + +	unlock_new_inode(inode); +  	return inode;  } @@ -156,8 +195,7 @@ static void *cramfs_read(struct super_block *sb, unsigned int offset, unsigned i  		struct page *page = NULL;  		if (blocknr + i < devsize) { -			page = read_mapping_page_async(mapping, blocknr + i, -									NULL); +			page = read_mapping_page(mapping, blocknr + i, NULL);  			/* synchronous error? */  			if (IS_ERR(page))  				page = NULL; @@ -196,14 +234,16 @@ static void *cramfs_read(struct super_block *sb, unsigned int offset, unsigned i  	return read_buffers[buffer] + offset;  } -static void cramfs_put_super(struct super_block *sb) +static void cramfs_kill_sb(struct super_block *sb)  { -	kfree(sb->s_fs_info); -	sb->s_fs_info = NULL; +	struct cramfs_sb_info *sbi = CRAMFS_SB(sb); +	kill_block_super(sb); +	kfree(sbi);  }  static int cramfs_remount(struct super_block *sb, int *flags, char *data)  { +	sync_filesystem(sb);  	*flags |= MS_RDONLY;  	return 0;  } @@ -234,11 +274,11 @@ static int cramfs_fill_super(struct super_block *sb, void *data, int silent)  	/* Do sanity checks on the superblock */  	if (super.magic != CRAMFS_MAGIC) { -		/* check for wrong endianess */ +		/* check for wrong endianness */  		if (super.magic == CRAMFS_MAGIC_WEND) {  			if (!silent) -				printk(KERN_ERR "cramfs: wrong endianess\n"); -			goto out; +				printk(KERN_ERR "cramfs: wrong endianness\n"); +			return -EINVAL;  		}  		/* check at 512 byte offset */ @@ -247,24 +287,27 @@ static int cramfs_fill_super(struct super_block *sb, void *data, int silent)  		mutex_unlock(&read_mutex);  		if (super.magic != CRAMFS_MAGIC) {  			if (super.magic == CRAMFS_MAGIC_WEND && !silent) -				printk(KERN_ERR "cramfs: wrong endianess\n"); +				printk(KERN_ERR "cramfs: wrong endianness\n");  			else if (!silent)  				printk(KERN_ERR "cramfs: wrong magic\n"); -			goto out; +			return -EINVAL;  		}  	}  	/* get feature flags first */  	if (super.flags & ~CRAMFS_SUPPORTED_FLAGS) {  		printk(KERN_ERR "cramfs: unsupported filesystem features\n"); -		goto out; +		return -EINVAL;  	}  	/* Check that the root inode is in a sane state */  	if (!S_ISDIR(super.root.mode)) {  		printk(KERN_ERR "cramfs: root is not a directory\n"); -		goto out; +		return -EINVAL;  	} +	/* correct strange, hard-coded permissions of mkcramfs */ +	super.root.mode |= (S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); +  	root_offset = super.root.offset << 2;  	if (super.flags & CRAMFS_FLAG_FSID_VERSION_2) {  		sbi->size=super.size; @@ -284,24 +327,18 @@ static int cramfs_fill_super(struct super_block *sb, void *data, int silent)  		  (root_offset != 512 + sizeof(struct cramfs_super))))  	{  		printk(KERN_ERR "cramfs: bad root offset %lu\n", root_offset); -		goto out; +		return -EINVAL;  	}  	/* Set it all up.. */  	sb->s_op = &cramfs_ops; -	root = get_cramfs_inode(sb, &super.root); -	if (!root) -		goto out; -	sb->s_root = d_alloc_root(root); -	if (!sb->s_root) { -		iput(root); -		goto out; -	} +	root = get_cramfs_inode(sb, &super.root, 0); +	if (IS_ERR(root)) +		return PTR_ERR(root); +	sb->s_root = d_make_root(root); +	if (!sb->s_root) +		return -ENOMEM;  	return 0; -out: -	kfree(sbi); -	sb->s_fs_info = NULL; -	return -EINVAL;  }  static int cramfs_statfs(struct dentry *dentry, struct kstatfs *buf) @@ -325,18 +362,17 @@ static int cramfs_statfs(struct dentry *dentry, struct kstatfs *buf)  /*   * Read a cramfs directory entry.   */ -static int cramfs_readdir(struct file *filp, void *dirent, filldir_t filldir) +static int cramfs_readdir(struct file *file, struct dir_context *ctx)  { -	struct inode *inode = filp->f_path.dentry->d_inode; +	struct inode *inode = file_inode(file);  	struct super_block *sb = inode->i_sb;  	char *buf;  	unsigned int offset; -	int copied;  	/* Offset within the thing. */ -	offset = filp->f_pos; -	if (offset >= inode->i_size) +	if (ctx->pos >= inode->i_size)  		return 0; +	offset = ctx->pos;  	/* Directory entries are always 4-byte aligned */  	if (offset & 3)  		return -EINVAL; @@ -345,14 +381,13 @@ static int cramfs_readdir(struct file *filp, void *dirent, filldir_t filldir)  	if (!buf)  		return -ENOMEM; -	copied = 0;  	while (offset < inode->i_size) {  		struct cramfs_inode *de;  		unsigned long nextoffset;  		char *name;  		ino_t ino; -		mode_t mode; -		int namelen, error; +		umode_t mode; +		int namelen;  		mutex_lock(&read_mutex);  		de = cramfs_read(sb, OFFSET(inode) + offset, sizeof(*de)+CRAMFS_MAXPATHLEN); @@ -365,7 +400,7 @@ static int cramfs_readdir(struct file *filp, void *dirent, filldir_t filldir)  		 */  		namelen = de->namelen << 2;  		memcpy(buf, name, namelen); -		ino = CRAMINO(de); +		ino = cramino(de, OFFSET(inode) + offset);  		mode = de->mode;  		mutex_unlock(&read_mutex);  		nextoffset = offset + sizeof(*de) + namelen; @@ -378,13 +413,10 @@ static int cramfs_readdir(struct file *filp, void *dirent, filldir_t filldir)  				break;  			namelen--;  		} -		error = filldir(dirent, buf, namelen, offset, ino, mode >> 12); -		if (error) +		if (!dir_emit(ctx, buf, namelen, ino, mode >> 12))  			break; -		offset = nextoffset; -		filp->f_pos = offset; -		copied++; +		ctx->pos = offset = nextoffset;  	}  	kfree(buf);  	return 0; @@ -393,9 +425,10 @@ static int cramfs_readdir(struct file *filp, void *dirent, filldir_t filldir)  /*   * Lookup and fill in the inode data..   */ -static struct dentry * cramfs_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd) +static struct dentry * cramfs_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags)  {  	unsigned int offset = 0; +	struct inode *inode = NULL;  	int sorted;  	mutex_lock(&read_mutex); @@ -404,8 +437,9 @@ static struct dentry * cramfs_lookup(struct inode *dir, struct dentry *dentry, s  		struct cramfs_inode *de;  		char *name;  		int namelen, retval; +		int dir_off = OFFSET(dir) + offset; -		de = cramfs_read(dir->i_sb, OFFSET(dir) + offset, sizeof(*de)+CRAMFS_MAXPATHLEN); +		de = cramfs_read(dir->i_sb, dir_off, sizeof(*de)+CRAMFS_MAXPATHLEN);  		name = (char *)(de+1);  		/* Try to take advantage of sorted directories */ @@ -421,8 +455,8 @@ static struct dentry * cramfs_lookup(struct inode *dir, struct dentry *dentry, s  		for (;;) {  			if (!namelen) { -				mutex_unlock(&read_mutex); -				return ERR_PTR(-EIO); +				inode = ERR_PTR(-EIO); +				goto out;  			}  			if (name[namelen-1])  				break; @@ -434,17 +468,18 @@ static struct dentry * cramfs_lookup(struct inode *dir, struct dentry *dentry, s  		if (retval > 0)  			continue;  		if (!retval) { -			struct cramfs_inode entry = *de; -			mutex_unlock(&read_mutex); -			d_add(dentry, get_cramfs_inode(dir->i_sb, &entry)); -			return NULL; +			inode = get_cramfs_inode(dir->i_sb, de, dir_off); +			break;  		}  		/* else (retval < 0) */  		if (sorted)  			break;  	} +out:  	mutex_unlock(&read_mutex); -	d_add(dentry, NULL); +	if (IS_ERR(inode)) +		return ERR_CAST(inode); +	d_add(dentry, inode);  	return NULL;  } @@ -520,7 +555,7 @@ static const struct address_space_operations cramfs_aops = {  static const struct file_operations cramfs_directory_operations = {  	.llseek		= generic_file_llseek,  	.read		= generic_read_dir, -	.readdir	= cramfs_readdir, +	.iterate	= cramfs_readdir,  };  static const struct inode_operations cramfs_dir_inode_operations = { @@ -528,7 +563,6 @@ static const struct inode_operations cramfs_dir_inode_operations = {  };  static const struct super_operations cramfs_ops = { -	.put_super	= cramfs_put_super,  	.remount_fs	= cramfs_remount,  	.statfs		= cramfs_statfs,  }; @@ -543,9 +577,10 @@ static struct file_system_type cramfs_fs_type = {  	.owner		= THIS_MODULE,  	.name		= "cramfs",  	.mount		= cramfs_mount, -	.kill_sb	= kill_block_super, +	.kill_sb	= cramfs_kill_sb,  	.fs_flags	= FS_REQUIRES_DEV,  }; +MODULE_ALIAS_FS("cramfs");  static int __init init_cramfs_fs(void)  {  | 
