diff options
Diffstat (limited to 'fs/adfs')
| -rw-r--r-- | fs/adfs/Kconfig | 5 | ||||
| -rw-r--r-- | fs/adfs/adfs.h | 38 | ||||
| -rw-r--r-- | fs/adfs/dir.c | 60 | ||||
| -rw-r--r-- | fs/adfs/dir_f.c | 23 | ||||
| -rw-r--r-- | fs/adfs/dir_fplus.c | 119 | ||||
| -rw-r--r-- | fs/adfs/file.c | 8 | ||||
| -rw-r--r-- | fs/adfs/inode.c | 90 | ||||
| -rw-r--r-- | fs/adfs/map.c | 2 | ||||
| -rw-r--r-- | fs/adfs/super.c | 88 | 
9 files changed, 271 insertions, 162 deletions
diff --git a/fs/adfs/Kconfig b/fs/adfs/Kconfig index 1dd5f34b3cf..c5a7787dd5e 100644 --- a/fs/adfs/Kconfig +++ b/fs/adfs/Kconfig @@ -1,7 +1,6 @@  config ADFS_FS -	tristate "ADFS file system support (EXPERIMENTAL)" -	depends on BLOCK && EXPERIMENTAL -	depends on BKL # need to fix +	tristate "ADFS file system support" +	depends on BLOCK  	help  	  The Acorn Disc Filing System is the standard file system of the  	  RiscOS operating system which runs on Acorn's ARM-based Risc PC diff --git a/fs/adfs/adfs.h b/fs/adfs/adfs.h index 2ff622f6f54..c770337c4b4 100644 --- a/fs/adfs/adfs.h +++ b/fs/adfs/adfs.h @@ -43,13 +43,17 @@ struct adfs_dir_ops;   * ADFS file system superblock data in memory   */  struct adfs_sb_info { -	struct adfs_discmap *s_map;	/* bh list containing map		 */ -	struct adfs_dir_ops *s_dir;	/* directory operations			 */ - -	uid_t		s_uid;		/* owner uid				 */ -	gid_t		s_gid;		/* owner gid				 */ +	union { struct { +		struct adfs_discmap *s_map;	/* bh list containing map	 */ +		struct adfs_dir_ops *s_dir;	/* directory operations		 */ +		}; +		struct rcu_head rcu;		/* used only at shutdown time	 */ +	}; +	kuid_t		s_uid;		/* owner uid				 */ +	kgid_t		s_gid;		/* owner gid				 */  	umode_t		s_owner_mask;	/* ADFS owner perm -> unix perm		 */  	umode_t		s_other_mask;	/* ADFS other perm -> unix perm		 */ +	int		s_ftsuffix;	/* ,xyz hex filetype suffix option */  	__u32		s_ids_per_zone;	/* max. no ids in one zone		 */  	__u32		s_idlen;	/* length of ID in map			 */ @@ -79,6 +83,10 @@ struct adfs_dir {  	int			nr_buffers;  	struct buffer_head	*bh[4]; + +	/* big directories need allocated buffers */ +	struct buffer_head	**bh_fplus; +  	unsigned int		pos;  	unsigned int		parent_id; @@ -89,7 +97,7 @@ struct adfs_dir {  /*   * This is the overall maximum name length   */ -#define ADFS_MAX_NAME_LEN	256 +#define ADFS_MAX_NAME_LEN	(256 + 4) /* +4 for ,xyz hex filetype suffix */  struct object_info {  	__u32		parent_id;		/* parent object id	*/  	__u32		file_id;		/* object id		*/ @@ -97,10 +105,26 @@ struct object_info {  	__u32		execaddr;		/* execution address	*/  	__u32		size;			/* size			*/  	__u8		attr;			/* RISC OS attributes	*/ -	unsigned char	name_len;		/* name length		*/ +	unsigned int	name_len;		/* name length		*/  	char		name[ADFS_MAX_NAME_LEN];/* file name		*/ + +	/* RISC OS file type (12-bit: derived from loadaddr) */ +	__u16		filetype;  }; +/* RISC OS 12-bit filetype converts to ,xyz hex filename suffix */ +static inline int append_filetype_suffix(char *buf, __u16 filetype) +{ +	if (filetype == 0xffff)	/* no explicit 12-bit file type was set */ +		return 0; + +	*buf++ = ','; +	*buf++ = hex_asc_lo(filetype >> 8); +	*buf++ = hex_asc_lo(filetype >> 4); +	*buf++ = hex_asc_lo(filetype >> 0); +	return 4; +} +  struct adfs_dir_ops {  	int	(*read)(struct super_block *sb, unsigned int id, unsigned int sz, struct adfs_dir *dir);  	int	(*setpos)(struct adfs_dir *dir, unsigned int fpos); diff --git a/fs/adfs/dir.c b/fs/adfs/dir.c index f4287e4de74..0d138c0de29 100644 --- a/fs/adfs/dir.c +++ b/fs/adfs/dir.c @@ -9,7 +9,6 @@   *   *  Common directory handling for ADFS   */ -#include <linux/smp_lock.h>  #include "adfs.h"  /* @@ -18,49 +17,43 @@  static DEFINE_RWLOCK(adfs_dir_lock);  static int -adfs_readdir(struct file *filp, void *dirent, filldir_t filldir) +adfs_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;  	struct adfs_dir_ops *ops = ADFS_SB(sb)->s_dir;  	struct object_info obj;  	struct adfs_dir dir;  	int ret = 0; -	lock_kernel();	 - -	if (filp->f_pos >> 32) -		goto out; +	if (ctx->pos >> 32) +		return 0;  	ret = ops->read(sb, inode->i_ino, inode->i_size, &dir);  	if (ret) -		goto out; +		return ret; -	switch ((unsigned long)filp->f_pos) { -	case 0: -		if (filldir(dirent, ".", 1, 0, inode->i_ino, DT_DIR) < 0) +	if (ctx->pos == 0) { +		if (!dir_emit_dot(file, ctx))  			goto free_out; -		filp->f_pos += 1; - -	case 1: -		if (filldir(dirent, "..", 2, 1, dir.parent_id, DT_DIR) < 0) +		ctx->pos = 1; +	} +	if (ctx->pos == 1) { +		if (!dir_emit(ctx, "..", 2, dir.parent_id, DT_DIR))  			goto free_out; -		filp->f_pos += 1; - -	default: -		break; +		ctx->pos = 2;  	}  	read_lock(&adfs_dir_lock); -	ret = ops->setpos(&dir, filp->f_pos - 2); +	ret = ops->setpos(&dir, ctx->pos - 2);  	if (ret)  		goto unlock_out;  	while (ops->getnext(&dir, &obj) == 0) { -		if (filldir(dirent, obj.name, obj.name_len, -			    filp->f_pos, obj.file_id, DT_UNKNOWN) < 0) -			goto unlock_out; -		filp->f_pos += 1; +		if (!dir_emit(ctx, obj.name, obj.name_len, +			    obj.file_id, DT_UNKNOWN)) +			break; +		ctx->pos++;  	}  unlock_out: @@ -68,9 +61,6 @@ unlock_out:  free_out:  	ops->free(&dir); - -out: -	unlock_kernel();  	return ret;  } @@ -196,12 +186,12 @@ out:  const struct file_operations adfs_dir_operations = {  	.read		= generic_read_dir,  	.llseek		= generic_file_llseek, -	.readdir	= adfs_readdir, +	.iterate	= adfs_readdir,  	.fsync		= generic_file_fsync,  };  static int -adfs_hash(struct dentry *parent, struct qstr *qstr) +adfs_hash(const struct dentry *parent, struct qstr *qstr)  {  	const unsigned int name_len = ADFS_SB(parent->d_sb)->s_namelen;  	const unsigned char *name; @@ -237,17 +227,18 @@ adfs_hash(struct dentry *parent, struct qstr *qstr)   * requirements of the underlying filesystem.   */  static int -adfs_compare(struct dentry *parent, struct qstr *entry, struct qstr *name) +adfs_compare(const struct dentry *parent, const struct dentry *dentry, +		unsigned int len, const char *str, const struct qstr *name)  {  	int i; -	if (entry->len != name->len) +	if (len != name->len)  		return 1;  	for (i = 0; i < name->len; i++) {  		char a, b; -		a = entry->name[i]; +		a = str[i];  		b = name->name[i];  		if (a >= 'A' && a <= 'Z') @@ -267,14 +258,12 @@ const struct dentry_operations adfs_dentry_operations = {  };  static struct dentry * -adfs_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd) +adfs_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags)  {  	struct inode *inode = NULL;  	struct object_info obj;  	int error; -	dentry->d_op = &adfs_dentry_operations;	 -	lock_kernel();  	error = adfs_dir_lookup_byname(dir, &dentry->d_name, &obj);  	if (error == 0) {  		error = -EACCES; @@ -286,7 +275,6 @@ adfs_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd)  		if (inode)  			error = 0;  	} -	unlock_kernel();  	d_add(dentry, inode);  	return ERR_PTR(error);  } diff --git a/fs/adfs/dir_f.c b/fs/adfs/dir_f.c index bafc71222e2..4bbe853ee50 100644 --- a/fs/adfs/dir_f.c +++ b/fs/adfs/dir_f.c @@ -52,7 +52,6 @@ static inline int adfs_readname(char *buf, char *ptr, int maxlen)  			*buf++ = *ptr;  		ptr++;  	} -	*buf = '\0';  	return buf - old_buf;  } @@ -208,7 +207,8 @@ release_buffers:   * convert a disk-based directory entry to a Linux ADFS directory entry   */  static inline void -adfs_dir2obj(struct object_info *obj, struct adfs_direntry *de) +adfs_dir2obj(struct adfs_dir *dir, struct object_info *obj, +	struct adfs_direntry *de)  {  	obj->name_len =	adfs_readname(obj->name, de->dirobname, ADFS_F_NAME_LEN);  	obj->file_id  = adfs_readval(de->dirinddiscadd, 3); @@ -216,6 +216,23 @@ adfs_dir2obj(struct object_info *obj, struct adfs_direntry *de)  	obj->execaddr = adfs_readval(de->direxec, 4);  	obj->size     = adfs_readval(de->dirlen,  4);  	obj->attr     = de->newdiratts; +	obj->filetype = -1; + +	/* +	 * object is a file and is filetyped and timestamped? +	 * RISC OS 12-bit filetype is stored in load_address[19:8] +	 */ +	if ((0 == (obj->attr & ADFS_NDA_DIRECTORY)) && +		(0xfff00000 == (0xfff00000 & obj->loadaddr))) { +		obj->filetype = (__u16) ((0x000fff00 & obj->loadaddr) >> 8); + +		/* optionally append the ,xyz hex filetype suffix */ +		if (ADFS_SB(dir->sb)->s_ftsuffix) +			obj->name_len += +				append_filetype_suffix( +					&obj->name[obj->name_len], +					obj->filetype); +	}  }  /* @@ -260,7 +277,7 @@ __adfs_dir_get(struct adfs_dir *dir, int pos, struct object_info *obj)  	if (!de.dirobname[0])  		return -ENOENT; -	adfs_dir2obj(obj, &de); +	adfs_dir2obj(dir, obj, &de);  	return 0;  } diff --git a/fs/adfs/dir_fplus.c b/fs/adfs/dir_fplus.c index 1796bb352d0..d9e3bee4e65 100644 --- a/fs/adfs/dir_fplus.c +++ b/fs/adfs/dir_fplus.c @@ -8,6 +8,7 @@   * published by the Free Software Foundation.   */  #include <linux/buffer_head.h> +#include <linux/slab.h>  #include "adfs.h"  #include "dir_fplus.h" @@ -22,30 +23,53 @@ adfs_fplus_read(struct super_block *sb, unsigned int id, unsigned int sz, struct  	dir->nr_buffers = 0; +	/* start off using fixed bh set - only alloc for big dirs */ +	dir->bh_fplus = &dir->bh[0]; +  	block = __adfs_block_map(sb, id, 0);  	if (!block) {  		adfs_error(sb, "dir object %X has a hole at offset 0", id);  		goto out;  	} -	dir->bh[0] = sb_bread(sb, block); -	if (!dir->bh[0]) +	dir->bh_fplus[0] = sb_bread(sb, block); +	if (!dir->bh_fplus[0])  		goto out;  	dir->nr_buffers += 1; -	h = (struct adfs_bigdirheader *)dir->bh[0]->b_data; +	h = (struct adfs_bigdirheader *)dir->bh_fplus[0]->b_data;  	size = le32_to_cpu(h->bigdirsize);  	if (size != sz) { -		printk(KERN_WARNING "adfs: adfs_fplus_read: directory header size\n" -				" does not match directory size\n"); +		printk(KERN_WARNING "adfs: adfs_fplus_read:" +					" directory header size %X\n" +					" does not match directory size %X\n", +					size, sz);  	}  	if (h->bigdirversion[0] != 0 || h->bigdirversion[1] != 0 ||  	    h->bigdirversion[2] != 0 || size & 2047 || -	    h->bigdirstartname != cpu_to_le32(BIGDIRSTARTNAME)) +	    h->bigdirstartname != cpu_to_le32(BIGDIRSTARTNAME)) { +		printk(KERN_WARNING "adfs: dir object %X has" +					" malformed dir header\n", id);  		goto out; +	}  	size >>= sb->s_blocksize_bits; +	if (size > sizeof(dir->bh)/sizeof(dir->bh[0])) { +		/* this directory is too big for fixed bh set, must allocate */ +		struct buffer_head **bh_fplus = +			kzalloc(size * sizeof(struct buffer_head *), +				GFP_KERNEL); +		if (!bh_fplus) { +			adfs_error(sb, "not enough memory for" +					" dir object %X (%d blocks)", id, size); +			goto out; +		} +		dir->bh_fplus = bh_fplus; +		/* copy over the pointer to the block that we've already read */ +		dir->bh_fplus[0] = dir->bh[0]; +	} +  	for (blk = 1; blk < size; blk++) {  		block = __adfs_block_map(sb, id, blk);  		if (!block) { @@ -53,25 +77,44 @@ adfs_fplus_read(struct super_block *sb, unsigned int id, unsigned int sz, struct  			goto out;  		} -		dir->bh[blk] = sb_bread(sb, block); -		if (!dir->bh[blk]) +		dir->bh_fplus[blk] = sb_bread(sb, block); +		if (!dir->bh_fplus[blk]) { +			adfs_error(sb,	"dir object %X failed read for" +					" offset %d, mapped block %X", +					id, blk, block);  			goto out; -		dir->nr_buffers = blk; +		} + +		dir->nr_buffers += 1;  	} -	t = (struct adfs_bigdirtail *)(dir->bh[size - 1]->b_data + (sb->s_blocksize - 8)); +	t = (struct adfs_bigdirtail *) +		(dir->bh_fplus[size - 1]->b_data + (sb->s_blocksize - 8));  	if (t->bigdirendname != cpu_to_le32(BIGDIRENDNAME) ||  	    t->bigdirendmasseq != h->startmasseq || -	    t->reserved[0] != 0 || t->reserved[1] != 0) +	    t->reserved[0] != 0 || t->reserved[1] != 0) { +		printk(KERN_WARNING "adfs: dir object %X has " +					"malformed dir end\n", id);  		goto out; +	}  	dir->parent_id = le32_to_cpu(h->bigdirparent);  	dir->sb = sb;  	return 0; +  out: -	for (i = 0; i < dir->nr_buffers; i++) -		brelse(dir->bh[i]); +	if (dir->bh_fplus) { +		for (i = 0; i < dir->nr_buffers; i++) +			brelse(dir->bh_fplus[i]); + +		if (&dir->bh[0] != dir->bh_fplus) +			kfree(dir->bh_fplus); + +		dir->bh_fplus = NULL; +	} + +	dir->nr_buffers = 0;  	dir->sb = NULL;  	return ret;  } @@ -79,7 +122,8 @@ out:  static int  adfs_fplus_setpos(struct adfs_dir *dir, unsigned int fpos)  { -	struct adfs_bigdirheader *h = (struct adfs_bigdirheader *)dir->bh[0]->b_data; +	struct adfs_bigdirheader *h = +		(struct adfs_bigdirheader *) dir->bh_fplus[0]->b_data;  	int ret = -ENOENT;  	if (fpos <= le32_to_cpu(h->bigdirentries)) { @@ -102,21 +146,27 @@ dir_memcpy(struct adfs_dir *dir, unsigned int offset, void *to, int len)  	partial = sb->s_blocksize - offset;  	if (partial >= len) -		memcpy(to, dir->bh[buffer]->b_data + offset, len); +		memcpy(to, dir->bh_fplus[buffer]->b_data + offset, len);  	else {  		char *c = (char *)to;  		remainder = len - partial; -		memcpy(c, dir->bh[buffer]->b_data + offset, partial); -		memcpy(c + partial, dir->bh[buffer + 1]->b_data, remainder); +		memcpy(c, +			dir->bh_fplus[buffer]->b_data + offset, +			partial); + +		memcpy(c + partial, +			dir->bh_fplus[buffer + 1]->b_data, +			remainder);  	}  }  static int  adfs_fplus_getnext(struct adfs_dir *dir, struct object_info *obj)  { -	struct adfs_bigdirheader *h = (struct adfs_bigdirheader *)dir->bh[0]->b_data; +	struct adfs_bigdirheader *h = +		(struct adfs_bigdirheader *) dir->bh_fplus[0]->b_data;  	struct adfs_bigdirentry bde;  	unsigned int offset;  	int i, ret = -ENOENT; @@ -147,6 +197,24 @@ adfs_fplus_getnext(struct adfs_dir *dir, struct object_info *obj)  		if (obj->name[i] == '/')  			obj->name[i] = '.'; +	obj->filetype = -1; + +	/* +	 * object is a file and is filetyped and timestamped? +	 * RISC OS 12-bit filetype is stored in load_address[19:8] +	 */ +	if ((0 == (obj->attr & ADFS_NDA_DIRECTORY)) && +		(0xfff00000 == (0xfff00000 & obj->loadaddr))) { +		obj->filetype = (__u16) ((0x000fff00 & obj->loadaddr) >> 8); + +		/* optionally append the ,xyz hex filetype suffix */ +		if (ADFS_SB(dir->sb)->s_ftsuffix) +			obj->name_len += +				append_filetype_suffix( +					&obj->name[obj->name_len], +					obj->filetype); +	} +  	dir->pos += 1;  	ret = 0;  out: @@ -160,7 +228,7 @@ adfs_fplus_sync(struct adfs_dir *dir)  	int i;  	for (i = dir->nr_buffers - 1; i >= 0; i--) { -		struct buffer_head *bh = dir->bh[i]; +		struct buffer_head *bh = dir->bh_fplus[i];  		sync_dirty_buffer(bh);  		if (buffer_req(bh) && !buffer_uptodate(bh))  			err = -EIO; @@ -174,8 +242,17 @@ adfs_fplus_free(struct adfs_dir *dir)  {  	int i; -	for (i = 0; i < dir->nr_buffers; i++) -		brelse(dir->bh[i]); +	if (dir->bh_fplus) { +		for (i = 0; i < dir->nr_buffers; i++) +			brelse(dir->bh_fplus[i]); + +		if (&dir->bh[0] != dir->bh_fplus) +			kfree(dir->bh_fplus); + +		dir->bh_fplus = NULL; +	} + +	dir->nr_buffers = 0;  	dir->sb = NULL;  } diff --git a/fs/adfs/file.c b/fs/adfs/file.c index a36da5382b4..07c9edce5aa 100644 --- a/fs/adfs/file.c +++ b/fs/adfs/file.c @@ -23,12 +23,12 @@  const struct file_operations adfs_file_operations = {  	.llseek		= generic_file_llseek, -	.read		= do_sync_read, -	.aio_read	= generic_file_aio_read, +	.read		= new_sync_read, +	.read_iter	= generic_file_read_iter,  	.mmap		= generic_file_mmap,  	.fsync		= generic_file_fsync, -	.write		= do_sync_write, -	.aio_write	= generic_file_aio_write, +	.write		= new_sync_write, +	.write_iter	= generic_file_write_iter,  	.splice_read	= generic_file_splice_read,  }; diff --git a/fs/adfs/inode.c b/fs/adfs/inode.c index 65794b8fe79..b9acadafa4a 100644 --- a/fs/adfs/inode.c +++ b/fs/adfs/inode.c @@ -7,7 +7,6 @@   * it under the terms of the GNU General Public License version 2 as   * published by the Free Software Foundation.   */ -#include <linux/smp_lock.h>  #include <linux/buffer_head.h>  #include <linux/writeback.h>  #include "adfs.h" @@ -46,6 +45,14 @@ static int adfs_readpage(struct file *file, struct page *page)  	return block_read_full_page(page, adfs_get_block);  } +static void adfs_write_failed(struct address_space *mapping, loff_t to) +{ +	struct inode *inode = mapping->host; + +	if (to > inode->i_size) +		truncate_pagecache(inode, inode->i_size); +} +  static int adfs_write_begin(struct file *file, struct address_space *mapping,  			loff_t pos, unsigned len, unsigned flags,  			struct page **pagep, void **fsdata) @@ -56,11 +63,8 @@ static int adfs_write_begin(struct file *file, struct address_space *mapping,  	ret = cont_write_begin(file, mapping, pos, len, flags, pagep, fsdata,  				adfs_get_block,  				&ADFS_I(mapping->host)->mmu_private); -	if (unlikely(ret)) { -		loff_t isize = mapping->host->i_size; -		if (pos + len > isize) -			vmtruncate(mapping->host, isize); -	} +	if (unlikely(ret)) +		adfs_write_failed(mapping, pos + len);  	return ret;  } @@ -73,32 +77,18 @@ static sector_t _adfs_bmap(struct address_space *mapping, sector_t block)  static const struct address_space_operations adfs_aops = {  	.readpage	= adfs_readpage,  	.writepage	= adfs_writepage, -	.sync_page	= block_sync_page,  	.write_begin	= adfs_write_begin,  	.write_end	= generic_write_end,  	.bmap		= _adfs_bmap  }; -static inline unsigned int -adfs_filetype(struct inode *inode) -{ -	unsigned int type; - -	if (ADFS_I(inode)->stamped) -		type = (ADFS_I(inode)->loadaddr >> 8) & 0xfff; -	else -		type = (unsigned int) -1; - -	return type; -} -  /*   * Convert ADFS attributes and filetype to Linux permission.   */  static umode_t  adfs_atts2mode(struct super_block *sb, struct inode *inode)  { -	unsigned int filetype, attr = ADFS_I(inode)->attr; +	unsigned int attr = ADFS_I(inode)->attr;  	umode_t mode, rmask;  	struct adfs_sb_info *asb = ADFS_SB(sb); @@ -107,9 +97,7 @@ adfs_atts2mode(struct super_block *sb, struct inode *inode)  		return S_IFDIR | S_IXUGO | mode;  	} -	filetype = adfs_filetype(inode); - -	switch (filetype) { +	switch (ADFS_I(inode)->filetype) {  	case 0xfc0:	/* LinkFS */  		return S_IFLNK|S_IRWXUGO; @@ -175,50 +163,48 @@ adfs_mode2atts(struct super_block *sb, struct inode *inode)  /*   * Convert an ADFS time to Unix time.  ADFS has a 40-bit centi-second time - * referenced to 1 Jan 1900 (til 2248) + * referenced to 1 Jan 1900 (til 2248) so we need to discard 2208988800 seconds + * of time to convert from RISC OS epoch to Unix epoch.   */  static void  adfs_adfs2unix_time(struct timespec *tv, struct inode *inode)  {  	unsigned int high, low; +	/* 01 Jan 1970 00:00:00 (Unix epoch) as nanoseconds since +	 * 01 Jan 1900 00:00:00 (RISC OS epoch) +	 */ +	static const s64 nsec_unix_epoch_diff_risc_os_epoch = +							2208988800000000000LL; +	s64 nsec;  	if (ADFS_I(inode)->stamped == 0)  		goto cur_time; -	high = ADFS_I(inode)->loadaddr << 24; -	low  = ADFS_I(inode)->execaddr; +	high = ADFS_I(inode)->loadaddr & 0xFF; /* top 8 bits of timestamp */ +	low  = ADFS_I(inode)->execaddr;    /* bottom 32 bits of timestamp */ -	high |= low >> 8; -	low  &= 255; +	/* convert 40-bit centi-seconds to 32-bit seconds +	 * going via nanoseconds to retain precision +	 */ +	nsec = (((s64) high << 32) | (s64) low) * 10000000; /* cs to ns */  	/* Files dated pre  01 Jan 1970 00:00:00. */ -	if (high < 0x336e996a) +	if (nsec < nsec_unix_epoch_diff_risc_os_epoch)  		goto too_early; -	/* Files dated post 18 Jan 2038 03:14:05. */ -	if (high >= 0x656e9969) -		goto too_late; +	/* convert from RISC OS to Unix epoch */ +	nsec -= nsec_unix_epoch_diff_risc_os_epoch; -	/* discard 2208988800 (0x336e996a00) seconds of time */ -	high -= 0x336e996a; - -	/* convert 40-bit centi-seconds to 32-bit seconds */ -	tv->tv_sec = (((high % 100) << 8) + low) / 100 + (high / 100 << 8); -	tv->tv_nsec = 0; +	*tv = ns_to_timespec(nsec);  	return;   cur_time: -	*tv = CURRENT_TIME_SEC; +	*tv = CURRENT_TIME;  	return;   too_early:  	tv->tv_sec = tv->tv_nsec = 0;  	return; - - too_late: -	tv->tv_sec = 0x7ffffffd; -	tv->tv_nsec = 0; -	return;  }  /* @@ -266,7 +252,7 @@ adfs_iget(struct super_block *sb, struct object_info *obj)  	inode->i_gid	 = ADFS_SB(sb)->s_gid;  	inode->i_ino	 = obj->file_id;  	inode->i_size	 = obj->size; -	inode->i_nlink	 = 2; +	set_nlink(inode, 2);  	inode->i_blocks	 = (inode->i_size + sb->s_blocksize - 1) >>  			    sb->s_blocksize_bits; @@ -280,7 +266,8 @@ adfs_iget(struct super_block *sb, struct object_info *obj)  	ADFS_I(inode)->loadaddr  = obj->loadaddr;  	ADFS_I(inode)->execaddr  = obj->execaddr;  	ADFS_I(inode)->attr      = obj->attr; -	ADFS_I(inode)->stamped	  = ((obj->loadaddr & 0xfff00000) == 0xfff00000); +	ADFS_I(inode)->filetype  = obj->filetype; +	ADFS_I(inode)->stamped   = ((obj->loadaddr & 0xfff00000) == 0xfff00000);  	inode->i_mode	 = adfs_atts2mode(sb, inode);  	adfs_adfs2unix_time(&inode->i_mtime, inode); @@ -316,16 +303,14 @@ adfs_notify_change(struct dentry *dentry, struct iattr *attr)  	unsigned int ia_valid = attr->ia_valid;  	int error; -	lock_kernel(); -  	error = inode_change_ok(inode, attr);  	/*  	 * we can't change the UID or GID of any file -  	 * we have a global UID/GID in the superblock  	 */ -	if ((ia_valid & ATTR_UID && attr->ia_uid != ADFS_SB(sb)->s_uid) || -	    (ia_valid & ATTR_GID && attr->ia_gid != ADFS_SB(sb)->s_gid)) +	if ((ia_valid & ATTR_UID && !uid_eq(attr->ia_uid, ADFS_SB(sb)->s_uid)) || +	    (ia_valid & ATTR_GID && !gid_eq(attr->ia_gid, ADFS_SB(sb)->s_gid)))  		error = -EPERM;  	if (error) @@ -359,7 +344,6 @@ adfs_notify_change(struct dentry *dentry, struct iattr *attr)  	if (ia_valid & (ATTR_SIZE | ATTR_MTIME | ATTR_MODE))  		mark_inode_dirty(inode);  out: -	unlock_kernel();  	return error;  } @@ -374,7 +358,6 @@ int adfs_write_inode(struct inode *inode, struct writeback_control *wbc)  	struct object_info obj;  	int ret; -	lock_kernel();  	obj.file_id	= inode->i_ino;  	obj.name_len	= 0;  	obj.parent_id	= ADFS_I(inode)->parent_id; @@ -384,6 +367,5 @@ int adfs_write_inode(struct inode *inode, struct writeback_control *wbc)  	obj.size	= inode->i_size;  	ret = adfs_dir_update(sb, &obj, wbc->sync_mode == WB_SYNC_ALL); -	unlock_kernel();  	return ret;  } diff --git a/fs/adfs/map.c b/fs/adfs/map.c index d1a5932bb0f..6935f05202a 100644 --- a/fs/adfs/map.c +++ b/fs/adfs/map.c @@ -51,7 +51,7 @@ static DEFINE_RWLOCK(adfs_map_lock);  /*   * This is fun.  We need to load up to 19 bits from the map at an - * arbitary bit alignment.  (We're limited to 19 bits by F+ version 2). + * arbitrary bit alignment.  (We're limited to 19 bits by F+ version 2).   */  #define GET_FRAG_ID(_map,_start,_idmask)				\  	({								\ diff --git a/fs/adfs/super.c b/fs/adfs/super.c index 959dbff2d42..9852bdf34d7 100644 --- a/fs/adfs/super.c +++ b/fs/adfs/super.c @@ -14,8 +14,8 @@  #include <linux/mount.h>  #include <linux/seq_file.h>  #include <linux/slab.h> -#include <linux/smp_lock.h>  #include <linux/statfs.h> +#include <linux/user_namespace.h>  #include "adfs.h"  #include "dir_f.h"  #include "dir_fplus.h" @@ -120,40 +120,38 @@ static void adfs_put_super(struct super_block *sb)  	int i;  	struct adfs_sb_info *asb = ADFS_SB(sb); -	lock_kernel(); -  	for (i = 0; i < asb->s_map_size; i++)  		brelse(asb->s_map[i].dm_bh);  	kfree(asb->s_map); -	kfree(asb); -	sb->s_fs_info = NULL; - -	unlock_kernel(); +	kfree_rcu(asb, rcu);  } -static int adfs_show_options(struct seq_file *seq, struct vfsmount *mnt) +static int adfs_show_options(struct seq_file *seq, struct dentry *root)  { -	struct adfs_sb_info *asb = ADFS_SB(mnt->mnt_sb); +	struct adfs_sb_info *asb = ADFS_SB(root->d_sb); -	if (asb->s_uid != 0) -		seq_printf(seq, ",uid=%u", asb->s_uid); -	if (asb->s_gid != 0) -		seq_printf(seq, ",gid=%u", asb->s_gid); +	if (!uid_eq(asb->s_uid, GLOBAL_ROOT_UID)) +		seq_printf(seq, ",uid=%u", from_kuid_munged(&init_user_ns, asb->s_uid)); +	if (!gid_eq(asb->s_gid, GLOBAL_ROOT_GID)) +		seq_printf(seq, ",gid=%u", from_kgid_munged(&init_user_ns, asb->s_gid));  	if (asb->s_owner_mask != ADFS_DEFAULT_OWNER_MASK)  		seq_printf(seq, ",ownmask=%o", asb->s_owner_mask);  	if (asb->s_other_mask != ADFS_DEFAULT_OTHER_MASK)  		seq_printf(seq, ",othmask=%o", asb->s_other_mask); +	if (asb->s_ftsuffix != 0) +		seq_printf(seq, ",ftsuffix=%u", asb->s_ftsuffix);  	return 0;  } -enum {Opt_uid, Opt_gid, Opt_ownmask, Opt_othmask, Opt_err}; +enum {Opt_uid, Opt_gid, Opt_ownmask, Opt_othmask, Opt_ftsuffix, Opt_err};  static const match_table_t tokens = {  	{Opt_uid, "uid=%u"},  	{Opt_gid, "gid=%u"},  	{Opt_ownmask, "ownmask=%o"},  	{Opt_othmask, "othmask=%o"}, +	{Opt_ftsuffix, "ftsuffix=%u"},  	{Opt_err, NULL}  }; @@ -177,12 +175,16 @@ static int parse_options(struct super_block *sb, char *options)  		case Opt_uid:  			if (match_int(args, &option))  				return -EINVAL; -			asb->s_uid = option; +			asb->s_uid = make_kuid(current_user_ns(), option); +			if (!uid_valid(asb->s_uid)) +				return -EINVAL;  			break;  		case Opt_gid:  			if (match_int(args, &option))  				return -EINVAL; -			asb->s_gid = option; +			asb->s_gid = make_kgid(current_user_ns(), option); +			if (!gid_valid(asb->s_gid)) +				return -EINVAL;  			break;  		case Opt_ownmask:  			if (match_octal(args, &option)) @@ -194,6 +196,11 @@ static int parse_options(struct super_block *sb, char *options)  				return -EINVAL;  			asb->s_other_mask = option;  			break; +		case Opt_ftsuffix: +			if (match_int(args, &option)) +				return -EINVAL; +			asb->s_ftsuffix = option; +			break;  		default:  			printk("ADFS-fs: unrecognised mount option \"%s\" "  					"or missing value\n", p); @@ -205,6 +212,7 @@ static int parse_options(struct super_block *sb, char *options)  static int adfs_remount(struct super_block *sb, int *flags, char *data)  { +	sync_filesystem(sb);  	*flags |= MS_NODIRATIME;  	return parse_options(sb, data);  } @@ -240,11 +248,17 @@ static struct inode *adfs_alloc_inode(struct super_block *sb)  	return &ei->vfs_inode;  } -static void adfs_destroy_inode(struct inode *inode) +static void adfs_i_callback(struct rcu_head *head)  { +	struct inode *inode = container_of(head, struct inode, i_rcu);  	kmem_cache_free(adfs_inode_cachep, ADFS_I(inode));  } +static void adfs_destroy_inode(struct inode *inode) +{ +	call_rcu(&inode->i_rcu, adfs_i_callback); +} +  static void init_once(void *foo)  {  	struct adfs_inode_info *ei = (struct adfs_inode_info *) foo; @@ -252,7 +266,7 @@ static void init_once(void *foo)  	inode_init_once(&ei->vfs_inode);  } -static int init_inodecache(void) +static int __init init_inodecache(void)  {  	adfs_inode_cachep = kmem_cache_create("adfs_inode_cache",  					     sizeof(struct adfs_inode_info), @@ -266,6 +280,11 @@ static int init_inodecache(void)  static void destroy_inodecache(void)  { +	/* +	 * Make sure all delayed rcu free inodes are flushed before we +	 * destroy cache. +	 */ +	rcu_barrier();  	kmem_cache_destroy(adfs_inode_cachep);  } @@ -352,22 +371,19 @@ static int adfs_fill_super(struct super_block *sb, void *data, int silent)  	struct adfs_sb_info *asb;  	struct inode *root; -	lock_kernel(); -  	sb->s_flags |= MS_NODIRATIME;  	asb = kzalloc(sizeof(*asb), GFP_KERNEL); -	if (!asb) { -		unlock_kernel(); +	if (!asb)  		return -ENOMEM; -	}  	sb->s_fs_info = asb;  	/* set default options */ -	asb->s_uid = 0; -	asb->s_gid = 0; +	asb->s_uid = GLOBAL_ROOT_UID; +	asb->s_gid = GLOBAL_ROOT_GID;  	asb->s_owner_mask = ADFS_DEFAULT_OWNER_MASK;  	asb->s_other_mask = ADFS_DEFAULT_OTHER_MASK; +	asb->s_ftsuffix = 0;  	if (parse_options(sb, data))  		goto error; @@ -447,11 +463,13 @@ static int adfs_fill_super(struct super_block *sb, void *data, int silent)  	root_obj.parent_id = root_obj.file_id = le32_to_cpu(dr->root);  	root_obj.name_len  = 0; -	root_obj.loadaddr  = 0; -	root_obj.execaddr  = 0; +	/* Set root object date as 01 Jan 1987 00:00:00 */ +	root_obj.loadaddr  = 0xfff0003f; +	root_obj.execaddr  = 0xec22c000;  	root_obj.size	   = ADFS_NEWDIR_SIZE;  	root_obj.attr	   = ADFS_NDA_DIRECTORY   | ADFS_NDA_OWNER_READ |  			     ADFS_NDA_OWNER_WRITE | ADFS_NDA_PUBLIC_READ; +	root_obj.filetype  = -1;  	/*  	 * If this is a F+ disk with variable length directories, @@ -465,20 +483,24 @@ static int adfs_fill_super(struct super_block *sb, void *data, int silent)  		asb->s_dir     = &adfs_f_dir_ops;  		asb->s_namelen = ADFS_F_NAME_LEN;  	} +	/* +	 * ,xyz hex filetype suffix may be added by driver +	 * to files that have valid RISC OS filetype +	 */ +	if (asb->s_ftsuffix) +		asb->s_namelen += 4; +	sb->s_d_op = &adfs_dentry_operations;  	root = adfs_iget(sb, &root_obj); -	sb->s_root = d_alloc_root(root); +	sb->s_root = d_make_root(root);  	if (!sb->s_root) {  		int i; -		iput(root);  		for (i = 0; i < asb->s_map_size; i++)  			brelse(asb->s_map[i].dm_bh);  		kfree(asb->s_map);  		adfs_error(sb, "get root inode failed\n");  		goto error; -	} else -		sb->s_root->d_op = &adfs_dentry_operations; -	unlock_kernel(); +	}  	return 0;  error_free_bh: @@ -486,7 +508,6 @@ error_free_bh:  error:  	sb->s_fs_info = NULL;  	kfree(asb); -	unlock_kernel();  	return -EINVAL;  } @@ -503,6 +524,7 @@ static struct file_system_type adfs_fs_type = {  	.kill_sb	= kill_block_super,  	.fs_flags	= FS_REQUIRES_DEV,  }; +MODULE_ALIAS_FS("adfs");  static int __init init_adfs_fs(void)  {  | 
