| .. | .. | 
|---|
 | 1 | +// SPDX-License-Identifier: GPL-2.0-only  | 
|---|
| 1 | 2 |  /* | 
|---|
| 2 | 3 |   *  linux/fs/adfs/super.c | 
|---|
| 3 | 4 |   * | 
|---|
| 4 | 5 |   *  Copyright (C) 1997-1999 Russell King | 
|---|
| 5 |  | - *  | 
|---|
| 6 |  | - * This program is free software; you can redistribute it and/or modify  | 
|---|
| 7 |  | - * it under the terms of the GNU General Public License version 2 as  | 
|---|
| 8 |  | - * published by the Free Software Foundation.  | 
|---|
| 9 | 6 |   */ | 
|---|
| 10 | 7 |  #include <linux/module.h> | 
|---|
| 11 | 8 |  #include <linux/init.h> | 
|---|
| 12 |  | -#include <linux/buffer_head.h>  | 
|---|
| 13 | 9 |  #include <linux/parser.h> | 
|---|
| 14 | 10 |  #include <linux/mount.h> | 
|---|
| 15 | 11 |  #include <linux/seq_file.h> | 
|---|
| 16 | 12 |  #include <linux/slab.h> | 
|---|
| 17 | 13 |  #include <linux/statfs.h> | 
|---|
| 18 | 14 |  #include <linux/user_namespace.h> | 
|---|
 | 15 | +#include <linux/blkdev.h>  | 
|---|
| 19 | 16 |  #include "adfs.h" | 
|---|
| 20 | 17 |  #include "dir_f.h" | 
|---|
| 21 | 18 |  #include "dir_fplus.h" | 
|---|
 | 19 | +  | 
|---|
 | 20 | +#define ADFS_SB_FLAGS SB_NOATIME  | 
|---|
| 22 | 21 |   | 
|---|
| 23 | 22 |  #define ADFS_DEFAULT_OWNER_MASK S_IRWXU | 
|---|
| 24 | 23 |  #define ADFS_DEFAULT_OTHER_MASK (S_IRWXG | S_IRWXO) | 
|---|
| 25 | 24 |   | 
|---|
| 26 | 25 |  void __adfs_error(struct super_block *sb, const char *function, const char *fmt, ...) | 
|---|
| 27 | 26 |  { | 
|---|
| 28 |  | -	char error_buf[128];  | 
|---|
 | 27 | +	struct va_format vaf;  | 
|---|
| 29 | 28 |  	va_list args; | 
|---|
| 30 | 29 |   | 
|---|
| 31 | 30 |  	va_start(args, fmt); | 
|---|
| 32 |  | -	vsnprintf(error_buf, sizeof(error_buf), fmt, args);  | 
|---|
| 33 |  | -	va_end(args);  | 
|---|
 | 31 | +	vaf.fmt = fmt;  | 
|---|
 | 32 | +	vaf.va = &args;  | 
|---|
| 34 | 33 |   | 
|---|
| 35 |  | -	printk(KERN_CRIT "ADFS-fs error (device %s)%s%s: %s\n",  | 
|---|
 | 34 | +	printk(KERN_CRIT "ADFS-fs error (device %s)%s%s: %pV\n",  | 
|---|
| 36 | 35 |  		sb->s_id, function ? ": " : "", | 
|---|
| 37 |  | -		function ? function : "", error_buf);  | 
|---|
 | 36 | +		function ? function : "", &vaf);  | 
|---|
 | 37 | +  | 
|---|
 | 38 | +	va_end(args);  | 
|---|
 | 39 | +}  | 
|---|
 | 40 | +  | 
|---|
 | 41 | +void adfs_msg(struct super_block *sb, const char *pfx, const char *fmt, ...)  | 
|---|
 | 42 | +{  | 
|---|
 | 43 | +	struct va_format vaf;  | 
|---|
 | 44 | +	va_list args;  | 
|---|
 | 45 | +  | 
|---|
 | 46 | +	va_start(args, fmt);  | 
|---|
 | 47 | +	vaf.fmt = fmt;  | 
|---|
 | 48 | +	vaf.va = &args;  | 
|---|
 | 49 | +	printk("%sADFS-fs (%s): %pV\n", pfx, sb->s_id, &vaf);  | 
|---|
 | 50 | +	va_end(args);  | 
|---|
| 38 | 51 |  } | 
|---|
| 39 | 52 |   | 
|---|
| 40 | 53 |  static int adfs_checkdiscrecord(struct adfs_discrecord *dr) | 
|---|
| 41 | 54 |  { | 
|---|
 | 55 | +	unsigned int max_idlen;  | 
|---|
| 42 | 56 |  	int i; | 
|---|
| 43 | 57 |   | 
|---|
| 44 | 58 |  	/* sector size must be 256, 512 or 1024 bytes */ | 
|---|
| .. | .. | 
|---|
| 58 | 72 |  	if (le32_to_cpu(dr->disc_size_high) >> dr->log2secsize) | 
|---|
| 59 | 73 |  		return 1; | 
|---|
| 60 | 74 |   | 
|---|
| 61 |  | -	/* idlen must be no greater than 19 v2 [1.0] */  | 
|---|
| 62 |  | -	if (dr->idlen > 19)  | 
|---|
 | 75 | +	/*  | 
|---|
 | 76 | +	 * Maximum idlen is limited to 16 bits for new directories by  | 
|---|
 | 77 | +	 * the three-byte storage of an indirect disc address.  For  | 
|---|
 | 78 | +	 * big directories, idlen must be no greater than 19 v2 [1.0]  | 
|---|
 | 79 | +	 */  | 
|---|
 | 80 | +	max_idlen = dr->format_version ? 19 : 16;  | 
|---|
 | 81 | +	if (dr->idlen > max_idlen)  | 
|---|
| 63 | 82 |  		return 1; | 
|---|
| 64 | 83 |   | 
|---|
| 65 | 84 |  	/* reserved bytes should be zero */ | 
|---|
| .. | .. | 
|---|
| 70 | 89 |  	return 0; | 
|---|
| 71 | 90 |  } | 
|---|
| 72 | 91 |   | 
|---|
| 73 |  | -static unsigned char adfs_calczonecheck(struct super_block *sb, unsigned char *map)  | 
|---|
| 74 |  | -{  | 
|---|
| 75 |  | -	unsigned int v0, v1, v2, v3;  | 
|---|
| 76 |  | -	int i;  | 
|---|
| 77 |  | -  | 
|---|
| 78 |  | -	v0 = v1 = v2 = v3 = 0;  | 
|---|
| 79 |  | -	for (i = sb->s_blocksize - 4; i; i -= 4) {  | 
|---|
| 80 |  | -		v0 += map[i]     + (v3 >> 8);  | 
|---|
| 81 |  | -		v3 &= 0xff;  | 
|---|
| 82 |  | -		v1 += map[i + 1] + (v0 >> 8);  | 
|---|
| 83 |  | -		v0 &= 0xff;  | 
|---|
| 84 |  | -		v2 += map[i + 2] + (v1 >> 8);  | 
|---|
| 85 |  | -		v1 &= 0xff;  | 
|---|
| 86 |  | -		v3 += map[i + 3] + (v2 >> 8);  | 
|---|
| 87 |  | -		v2 &= 0xff;  | 
|---|
| 88 |  | -	}  | 
|---|
| 89 |  | -	v0 +=           v3 >> 8;  | 
|---|
| 90 |  | -	v1 += map[1] + (v0 >> 8);  | 
|---|
| 91 |  | -	v2 += map[2] + (v1 >> 8);  | 
|---|
| 92 |  | -	v3 += map[3] + (v2 >> 8);  | 
|---|
| 93 |  | -  | 
|---|
| 94 |  | -	return v0 ^ v1 ^ v2 ^ v3;  | 
|---|
| 95 |  | -}  | 
|---|
| 96 |  | -  | 
|---|
| 97 |  | -static int adfs_checkmap(struct super_block *sb, struct adfs_discmap *dm)  | 
|---|
| 98 |  | -{  | 
|---|
| 99 |  | -	unsigned char crosscheck = 0, zonecheck = 1;  | 
|---|
| 100 |  | -	int i;  | 
|---|
| 101 |  | -  | 
|---|
| 102 |  | -	for (i = 0; i < ADFS_SB(sb)->s_map_size; i++) {  | 
|---|
| 103 |  | -		unsigned char *map;  | 
|---|
| 104 |  | -  | 
|---|
| 105 |  | -		map = dm[i].dm_bh->b_data;  | 
|---|
| 106 |  | -  | 
|---|
| 107 |  | -		if (adfs_calczonecheck(sb, map) != map[0]) {  | 
|---|
| 108 |  | -			adfs_error(sb, "zone %d fails zonecheck", i);  | 
|---|
| 109 |  | -			zonecheck = 0;  | 
|---|
| 110 |  | -		}  | 
|---|
| 111 |  | -		crosscheck ^= map[3];  | 
|---|
| 112 |  | -	}  | 
|---|
| 113 |  | -	if (crosscheck != 0xff)  | 
|---|
| 114 |  | -		adfs_error(sb, "crosscheck != 0xff");  | 
|---|
| 115 |  | -	return crosscheck == 0xff && zonecheck;  | 
|---|
| 116 |  | -}  | 
|---|
| 117 |  | -  | 
|---|
| 118 | 92 |  static void adfs_put_super(struct super_block *sb) | 
|---|
| 119 | 93 |  { | 
|---|
| 120 |  | -	int i;  | 
|---|
| 121 | 94 |  	struct adfs_sb_info *asb = ADFS_SB(sb); | 
|---|
| 122 | 95 |   | 
|---|
| 123 |  | -	for (i = 0; i < asb->s_map_size; i++)  | 
|---|
| 124 |  | -		brelse(asb->s_map[i].dm_bh);  | 
|---|
| 125 |  | -	kfree(asb->s_map);  | 
|---|
 | 96 | +	adfs_free_map(sb);  | 
|---|
| 126 | 97 |  	kfree_rcu(asb, rcu); | 
|---|
| 127 | 98 |  } | 
|---|
| 128 | 99 |   | 
|---|
| .. | .. | 
|---|
| 155 | 126 |  	{Opt_err, NULL} | 
|---|
| 156 | 127 |  }; | 
|---|
| 157 | 128 |   | 
|---|
| 158 |  | -static int parse_options(struct super_block *sb, char *options)  | 
|---|
 | 129 | +static int parse_options(struct super_block *sb, struct adfs_sb_info *asb,  | 
|---|
 | 130 | +			 char *options)  | 
|---|
| 159 | 131 |  { | 
|---|
| 160 | 132 |  	char *p; | 
|---|
| 161 |  | -	struct adfs_sb_info *asb = ADFS_SB(sb);  | 
|---|
| 162 | 133 |  	int option; | 
|---|
| 163 | 134 |   | 
|---|
| 164 | 135 |  	if (!options) | 
|---|
| .. | .. | 
|---|
| 202 | 173 |  			asb->s_ftsuffix = option; | 
|---|
| 203 | 174 |  			break; | 
|---|
| 204 | 175 |  		default: | 
|---|
| 205 |  | -			printk("ADFS-fs: unrecognised mount option \"%s\" "  | 
|---|
| 206 |  | -					"or missing value\n", p);  | 
|---|
 | 176 | +			adfs_msg(sb, KERN_ERR,  | 
|---|
 | 177 | +				 "unrecognised mount option \"%s\" or missing value",  | 
|---|
 | 178 | +				 p);  | 
|---|
| 207 | 179 |  			return -EINVAL; | 
|---|
| 208 | 180 |  		} | 
|---|
| 209 | 181 |  	} | 
|---|
| .. | .. | 
|---|
| 212 | 184 |   | 
|---|
| 213 | 185 |  static int adfs_remount(struct super_block *sb, int *flags, char *data) | 
|---|
| 214 | 186 |  { | 
|---|
 | 187 | +	struct adfs_sb_info temp_asb;  | 
|---|
 | 188 | +	int ret;  | 
|---|
 | 189 | +  | 
|---|
| 215 | 190 |  	sync_filesystem(sb); | 
|---|
| 216 |  | -	*flags |= SB_NODIRATIME;  | 
|---|
| 217 |  | -	return parse_options(sb, data);  | 
|---|
 | 191 | +	*flags |= ADFS_SB_FLAGS;  | 
|---|
 | 192 | +  | 
|---|
 | 193 | +	temp_asb = *ADFS_SB(sb);  | 
|---|
 | 194 | +	ret = parse_options(sb, &temp_asb, data);  | 
|---|
 | 195 | +	if (ret == 0)  | 
|---|
 | 196 | +		*ADFS_SB(sb) = temp_asb;  | 
|---|
 | 197 | +  | 
|---|
 | 198 | +	return ret;  | 
|---|
| 218 | 199 |  } | 
|---|
| 219 | 200 |   | 
|---|
| 220 | 201 |  static int adfs_statfs(struct dentry *dentry, struct kstatfs *buf) | 
|---|
| .. | .. | 
|---|
| 223 | 204 |  	struct adfs_sb_info *sbi = ADFS_SB(sb); | 
|---|
| 224 | 205 |  	u64 id = huge_encode_dev(sb->s_bdev->bd_dev); | 
|---|
| 225 | 206 |   | 
|---|
 | 207 | +	adfs_map_statfs(sb, buf);  | 
|---|
 | 208 | +  | 
|---|
| 226 | 209 |  	buf->f_type    = ADFS_SUPER_MAGIC; | 
|---|
| 227 | 210 |  	buf->f_namelen = sbi->s_namelen; | 
|---|
| 228 | 211 |  	buf->f_bsize   = sb->s_blocksize; | 
|---|
| 229 |  | -	buf->f_blocks  = sbi->s_size;  | 
|---|
| 230 |  | -	buf->f_files   = sbi->s_ids_per_zone * sbi->s_map_size;  | 
|---|
| 231 |  | -	buf->f_bavail  =  | 
|---|
| 232 |  | -	buf->f_bfree   = adfs_map_free(sb);  | 
|---|
| 233 | 212 |  	buf->f_ffree   = (long)(buf->f_bfree * buf->f_files) / (long)buf->f_blocks; | 
|---|
| 234 |  | -	buf->f_fsid.val[0] = (u32)id;  | 
|---|
| 235 |  | -	buf->f_fsid.val[1] = (u32)(id >> 32);  | 
|---|
 | 213 | +	buf->f_fsid    = u64_to_fsid(id);  | 
|---|
| 236 | 214 |   | 
|---|
| 237 | 215 |  	return 0; | 
|---|
| 238 | 216 |  } | 
|---|
| .. | .. | 
|---|
| 248 | 226 |  	return &ei->vfs_inode; | 
|---|
| 249 | 227 |  } | 
|---|
| 250 | 228 |   | 
|---|
| 251 |  | -static void adfs_i_callback(struct rcu_head *head)  | 
|---|
 | 229 | +static void adfs_free_inode(struct inode *inode)  | 
|---|
| 252 | 230 |  { | 
|---|
| 253 |  | -	struct inode *inode = container_of(head, struct inode, i_rcu);  | 
|---|
| 254 | 231 |  	kmem_cache_free(adfs_inode_cachep, ADFS_I(inode)); | 
|---|
| 255 | 232 |  } | 
|---|
| 256 | 233 |   | 
|---|
| 257 |  | -static void adfs_destroy_inode(struct inode *inode)  | 
|---|
 | 234 | +static int adfs_drop_inode(struct inode *inode)  | 
|---|
| 258 | 235 |  { | 
|---|
| 259 |  | -	call_rcu(&inode->i_rcu, adfs_i_callback);  | 
|---|
 | 236 | +	/* always drop inodes if we are read-only */  | 
|---|
 | 237 | +	return !IS_ENABLED(CONFIG_ADFS_FS_RW) || IS_RDONLY(inode);  | 
|---|
| 260 | 238 |  } | 
|---|
| 261 | 239 |   | 
|---|
| 262 | 240 |  static void init_once(void *foo) | 
|---|
| .. | .. | 
|---|
| 290 | 268 |   | 
|---|
| 291 | 269 |  static const struct super_operations adfs_sops = { | 
|---|
| 292 | 270 |  	.alloc_inode	= adfs_alloc_inode, | 
|---|
| 293 |  | -	.destroy_inode	= adfs_destroy_inode,  | 
|---|
| 294 |  | -	.drop_inode	= generic_delete_inode,  | 
|---|
 | 271 | +	.free_inode	= adfs_free_inode,  | 
|---|
 | 272 | +	.drop_inode	= adfs_drop_inode,  | 
|---|
| 295 | 273 |  	.write_inode	= adfs_write_inode, | 
|---|
| 296 | 274 |  	.put_super	= adfs_put_super, | 
|---|
| 297 | 275 |  	.statfs		= adfs_statfs, | 
|---|
| .. | .. | 
|---|
| 299 | 277 |  	.show_options	= adfs_show_options, | 
|---|
| 300 | 278 |  }; | 
|---|
| 301 | 279 |   | 
|---|
| 302 |  | -static struct adfs_discmap *adfs_read_map(struct super_block *sb, struct adfs_discrecord *dr)  | 
|---|
 | 280 | +static int adfs_probe(struct super_block *sb, unsigned int offset, int silent,  | 
|---|
 | 281 | +		      int (*validate)(struct super_block *sb,  | 
|---|
 | 282 | +				      struct buffer_head *bh,  | 
|---|
 | 283 | +				      struct adfs_discrecord **bhp))  | 
|---|
| 303 | 284 |  { | 
|---|
| 304 |  | -	struct adfs_discmap *dm;  | 
|---|
| 305 |  | -	unsigned int map_addr, zone_size, nzones;  | 
|---|
| 306 |  | -	int i, zone;  | 
|---|
| 307 | 285 |  	struct adfs_sb_info *asb = ADFS_SB(sb); | 
|---|
 | 286 | +	struct adfs_discrecord *dr;  | 
|---|
 | 287 | +	struct buffer_head *bh;  | 
|---|
 | 288 | +	unsigned int blocksize = BLOCK_SIZE;  | 
|---|
 | 289 | +	int ret, try;  | 
|---|
| 308 | 290 |   | 
|---|
| 309 |  | -	nzones    = asb->s_map_size;  | 
|---|
| 310 |  | -	zone_size = (8 << dr->log2secsize) - le16_to_cpu(dr->zone_spare);  | 
|---|
| 311 |  | -	map_addr  = (nzones >> 1) * zone_size -  | 
|---|
| 312 |  | -		     ((nzones > 1) ? ADFS_DR_SIZE_BITS : 0);  | 
|---|
| 313 |  | -	map_addr  = signed_asl(map_addr, asb->s_map2blk);  | 
|---|
| 314 |  | -  | 
|---|
| 315 |  | -	asb->s_ids_per_zone = zone_size / (asb->s_idlen + 1);  | 
|---|
| 316 |  | -  | 
|---|
| 317 |  | -	dm = kmalloc_array(nzones, sizeof(*dm), GFP_KERNEL);  | 
|---|
| 318 |  | -	if (dm == NULL) {  | 
|---|
| 319 |  | -		adfs_error(sb, "not enough memory");  | 
|---|
| 320 |  | -		return ERR_PTR(-ENOMEM);  | 
|---|
| 321 |  | -	}  | 
|---|
| 322 |  | -  | 
|---|
| 323 |  | -	for (zone = 0; zone < nzones; zone++, map_addr++) {  | 
|---|
| 324 |  | -		dm[zone].dm_startbit = 0;  | 
|---|
| 325 |  | -		dm[zone].dm_endbit   = zone_size;  | 
|---|
| 326 |  | -		dm[zone].dm_startblk = zone * zone_size - ADFS_DR_SIZE_BITS;  | 
|---|
| 327 |  | -		dm[zone].dm_bh       = sb_bread(sb, map_addr);  | 
|---|
| 328 |  | -  | 
|---|
| 329 |  | -		if (!dm[zone].dm_bh) {  | 
|---|
| 330 |  | -			adfs_error(sb, "unable to read map");  | 
|---|
| 331 |  | -			goto error_free;  | 
|---|
 | 291 | +	for (try = 0; try < 2; try++) {  | 
|---|
 | 292 | +		/* try to set the requested block size */  | 
|---|
 | 293 | +		if (sb->s_blocksize != blocksize &&  | 
|---|
 | 294 | +		    !sb_set_blocksize(sb, blocksize)) {  | 
|---|
 | 295 | +			if (!silent)  | 
|---|
 | 296 | +				adfs_msg(sb, KERN_ERR,  | 
|---|
 | 297 | +					 "error: unsupported blocksize");  | 
|---|
 | 298 | +			return -EINVAL;  | 
|---|
| 332 | 299 |  		} | 
|---|
 | 300 | +  | 
|---|
 | 301 | +		/* read the buffer */  | 
|---|
 | 302 | +		bh = sb_bread(sb, offset >> sb->s_blocksize_bits);  | 
|---|
 | 303 | +		if (!bh) {  | 
|---|
 | 304 | +			adfs_msg(sb, KERN_ERR,  | 
|---|
 | 305 | +				 "error: unable to read block %u, try %d",  | 
|---|
 | 306 | +				 offset >> sb->s_blocksize_bits, try);  | 
|---|
 | 307 | +			return -EIO;  | 
|---|
 | 308 | +		}  | 
|---|
 | 309 | +  | 
|---|
 | 310 | +		/* validate it */  | 
|---|
 | 311 | +		ret = validate(sb, bh, &dr);  | 
|---|
 | 312 | +		if (ret) {  | 
|---|
 | 313 | +			brelse(bh);  | 
|---|
 | 314 | +			return ret;  | 
|---|
 | 315 | +		}  | 
|---|
 | 316 | +  | 
|---|
 | 317 | +		/* does the block size match the filesystem block size? */  | 
|---|
 | 318 | +		blocksize = 1 << dr->log2secsize;  | 
|---|
 | 319 | +		if (sb->s_blocksize == blocksize) {  | 
|---|
 | 320 | +			asb->s_map = adfs_read_map(sb, dr);  | 
|---|
 | 321 | +			brelse(bh);  | 
|---|
 | 322 | +			return PTR_ERR_OR_ZERO(asb->s_map);  | 
|---|
 | 323 | +		}  | 
|---|
 | 324 | +  | 
|---|
 | 325 | +		brelse(bh);  | 
|---|
| 333 | 326 |  	} | 
|---|
| 334 | 327 |   | 
|---|
| 335 |  | -	/* adjust the limits for the first and last map zones */  | 
|---|
| 336 |  | -	i = zone - 1;  | 
|---|
| 337 |  | -	dm[0].dm_startblk = 0;  | 
|---|
| 338 |  | -	dm[0].dm_startbit = ADFS_DR_SIZE_BITS;  | 
|---|
| 339 |  | -	dm[i].dm_endbit   = (le32_to_cpu(dr->disc_size_high) << (32 - dr->log2bpmb)) +  | 
|---|
| 340 |  | -			    (le32_to_cpu(dr->disc_size) >> dr->log2bpmb) +  | 
|---|
| 341 |  | -			    (ADFS_DR_SIZE_BITS - i * zone_size);  | 
|---|
| 342 |  | -  | 
|---|
| 343 |  | -	if (adfs_checkmap(sb, dm))  | 
|---|
| 344 |  | -		return dm;  | 
|---|
| 345 |  | -  | 
|---|
| 346 |  | -	adfs_error(sb, "map corrupted");  | 
|---|
| 347 |  | -  | 
|---|
| 348 |  | -error_free:  | 
|---|
| 349 |  | -	while (--zone >= 0)  | 
|---|
| 350 |  | -		brelse(dm[zone].dm_bh);  | 
|---|
| 351 |  | -  | 
|---|
| 352 |  | -	kfree(dm);  | 
|---|
| 353 |  | -	return ERR_PTR(-EIO);  | 
|---|
 | 328 | +	return -EIO;  | 
|---|
| 354 | 329 |  } | 
|---|
| 355 | 330 |   | 
|---|
| 356 |  | -static inline unsigned long adfs_discsize(struct adfs_discrecord *dr, int block_bits)  | 
|---|
 | 331 | +static int adfs_validate_bblk(struct super_block *sb, struct buffer_head *bh,  | 
|---|
 | 332 | +			      struct adfs_discrecord **drp)  | 
|---|
| 357 | 333 |  { | 
|---|
| 358 |  | -	unsigned long discsize;  | 
|---|
 | 334 | +	struct adfs_discrecord *dr;  | 
|---|
 | 335 | +	unsigned char *b_data;  | 
|---|
| 359 | 336 |   | 
|---|
| 360 |  | -	discsize  = le32_to_cpu(dr->disc_size_high) << (32 - block_bits);  | 
|---|
| 361 |  | -	discsize |= le32_to_cpu(dr->disc_size) >> block_bits;  | 
|---|
 | 337 | +	b_data = bh->b_data + (ADFS_DISCRECORD % sb->s_blocksize);  | 
|---|
 | 338 | +	if (adfs_checkbblk(b_data))  | 
|---|
 | 339 | +		return -EILSEQ;  | 
|---|
| 362 | 340 |   | 
|---|
| 363 |  | -	return discsize;  | 
|---|
 | 341 | +	/* Do some sanity checks on the ADFS disc record */  | 
|---|
 | 342 | +	dr = (struct adfs_discrecord *)(b_data + ADFS_DR_OFFSET);  | 
|---|
 | 343 | +	if (adfs_checkdiscrecord(dr))  | 
|---|
 | 344 | +		return -EILSEQ;  | 
|---|
 | 345 | +  | 
|---|
 | 346 | +	*drp = dr;  | 
|---|
 | 347 | +	return 0;  | 
|---|
 | 348 | +}  | 
|---|
 | 349 | +  | 
|---|
 | 350 | +static int adfs_validate_dr0(struct super_block *sb, struct buffer_head *bh,  | 
|---|
 | 351 | +			      struct adfs_discrecord **drp)  | 
|---|
 | 352 | +{  | 
|---|
 | 353 | +	struct adfs_discrecord *dr;  | 
|---|
 | 354 | +  | 
|---|
 | 355 | +	/* Do some sanity checks on the ADFS disc record */  | 
|---|
 | 356 | +	dr = (struct adfs_discrecord *)(bh->b_data + 4);  | 
|---|
 | 357 | +	if (adfs_checkdiscrecord(dr) || dr->nzones_high || dr->nzones != 1)  | 
|---|
 | 358 | +		return -EILSEQ;  | 
|---|
 | 359 | +  | 
|---|
 | 360 | +	*drp = dr;  | 
|---|
 | 361 | +	return 0;  | 
|---|
| 364 | 362 |  } | 
|---|
| 365 | 363 |   | 
|---|
| 366 | 364 |  static int adfs_fill_super(struct super_block *sb, void *data, int silent) | 
|---|
| 367 | 365 |  { | 
|---|
| 368 | 366 |  	struct adfs_discrecord *dr; | 
|---|
| 369 |  | -	struct buffer_head *bh;  | 
|---|
| 370 | 367 |  	struct object_info root_obj; | 
|---|
| 371 |  | -	unsigned char *b_data;  | 
|---|
| 372 |  | -	unsigned int blocksize;  | 
|---|
| 373 | 368 |  	struct adfs_sb_info *asb; | 
|---|
| 374 | 369 |  	struct inode *root; | 
|---|
| 375 | 370 |  	int ret = -EINVAL; | 
|---|
| 376 | 371 |   | 
|---|
| 377 |  | -	sb->s_flags |= SB_NODIRATIME;  | 
|---|
 | 372 | +	sb->s_flags |= ADFS_SB_FLAGS;  | 
|---|
| 378 | 373 |   | 
|---|
| 379 | 374 |  	asb = kzalloc(sizeof(*asb), GFP_KERNEL); | 
|---|
| 380 | 375 |  	if (!asb) | 
|---|
| 381 | 376 |  		return -ENOMEM; | 
|---|
 | 377 | +  | 
|---|
| 382 | 378 |  	sb->s_fs_info = asb; | 
|---|
 | 379 | +	sb->s_magic = ADFS_SUPER_MAGIC;  | 
|---|
 | 380 | +	sb->s_time_gran = 10000000;  | 
|---|
| 383 | 381 |   | 
|---|
| 384 | 382 |  	/* set default options */ | 
|---|
| 385 | 383 |  	asb->s_uid = GLOBAL_ROOT_UID; | 
|---|
| .. | .. | 
|---|
| 388 | 386 |  	asb->s_other_mask = ADFS_DEFAULT_OTHER_MASK; | 
|---|
| 389 | 387 |  	asb->s_ftsuffix = 0; | 
|---|
| 390 | 388 |   | 
|---|
| 391 |  | -	if (parse_options(sb, data))  | 
|---|
 | 389 | +	if (parse_options(sb, asb, data))  | 
|---|
| 392 | 390 |  		goto error; | 
|---|
| 393 | 391 |   | 
|---|
| 394 |  | -	sb_set_blocksize(sb, BLOCK_SIZE);  | 
|---|
| 395 |  | -	if (!(bh = sb_bread(sb, ADFS_DISCRECORD / BLOCK_SIZE))) {  | 
|---|
| 396 |  | -		adfs_error(sb, "unable to read superblock");  | 
|---|
| 397 |  | -		ret = -EIO;  | 
|---|
 | 392 | +	/* Try to probe the filesystem boot block */  | 
|---|
 | 393 | +	ret = adfs_probe(sb, ADFS_DISCRECORD, 1, adfs_validate_bblk);  | 
|---|
 | 394 | +	if (ret == -EILSEQ)  | 
|---|
 | 395 | +		ret = adfs_probe(sb, 0, silent, adfs_validate_dr0);  | 
|---|
 | 396 | +	if (ret == -EILSEQ) {  | 
|---|
 | 397 | +		if (!silent)  | 
|---|
 | 398 | +			adfs_msg(sb, KERN_ERR,  | 
|---|
 | 399 | +				 "error: can't find an ADFS filesystem on dev %s.",  | 
|---|
 | 400 | +				 sb->s_id);  | 
|---|
 | 401 | +		ret = -EINVAL;  | 
|---|
 | 402 | +	}  | 
|---|
 | 403 | +	if (ret)  | 
|---|
| 398 | 404 |  		goto error; | 
|---|
| 399 |  | -	}  | 
|---|
| 400 | 405 |   | 
|---|
| 401 |  | -	b_data = bh->b_data + (ADFS_DISCRECORD % BLOCK_SIZE);  | 
|---|
| 402 |  | -  | 
|---|
| 403 |  | -	if (adfs_checkbblk(b_data)) {  | 
|---|
| 404 |  | -		if (!silent)  | 
|---|
| 405 |  | -			printk("VFS: Can't find an adfs filesystem on dev "  | 
|---|
| 406 |  | -				"%s.\n", sb->s_id);  | 
|---|
| 407 |  | -		ret = -EINVAL;  | 
|---|
| 408 |  | -		goto error_free_bh;  | 
|---|
| 409 |  | -	}  | 
|---|
| 410 |  | -  | 
|---|
| 411 |  | -	dr = (struct adfs_discrecord *)(b_data + ADFS_DR_OFFSET);  | 
|---|
| 412 |  | -  | 
|---|
| 413 |  | -	/*  | 
|---|
| 414 |  | -	 * Do some sanity checks on the ADFS disc record  | 
|---|
| 415 |  | -	 */  | 
|---|
| 416 |  | -	if (adfs_checkdiscrecord(dr)) {  | 
|---|
| 417 |  | -		if (!silent)  | 
|---|
| 418 |  | -			printk("VPS: Can't find an adfs filesystem on dev "  | 
|---|
| 419 |  | -				"%s.\n", sb->s_id);  | 
|---|
| 420 |  | -		ret = -EINVAL;  | 
|---|
| 421 |  | -		goto error_free_bh;  | 
|---|
| 422 |  | -	}  | 
|---|
| 423 |  | -  | 
|---|
| 424 |  | -	blocksize = 1 << dr->log2secsize;  | 
|---|
| 425 |  | -	brelse(bh);  | 
|---|
| 426 |  | -  | 
|---|
| 427 |  | -	if (sb_set_blocksize(sb, blocksize)) {  | 
|---|
| 428 |  | -		bh = sb_bread(sb, ADFS_DISCRECORD / sb->s_blocksize);  | 
|---|
| 429 |  | -		if (!bh) {  | 
|---|
| 430 |  | -			adfs_error(sb, "couldn't read superblock on "  | 
|---|
| 431 |  | -				"2nd try.");  | 
|---|
| 432 |  | -			ret = -EIO;  | 
|---|
| 433 |  | -			goto error;  | 
|---|
| 434 |  | -		}  | 
|---|
| 435 |  | -		b_data = bh->b_data + (ADFS_DISCRECORD % sb->s_blocksize);  | 
|---|
| 436 |  | -		if (adfs_checkbblk(b_data)) {  | 
|---|
| 437 |  | -			adfs_error(sb, "disc record mismatch, very weird!");  | 
|---|
| 438 |  | -			ret = -EINVAL;  | 
|---|
| 439 |  | -			goto error_free_bh;  | 
|---|
| 440 |  | -		}  | 
|---|
| 441 |  | -		dr = (struct adfs_discrecord *)(b_data + ADFS_DR_OFFSET);  | 
|---|
| 442 |  | -	} else {  | 
|---|
| 443 |  | -		if (!silent)  | 
|---|
| 444 |  | -			printk(KERN_ERR "VFS: Unsupported blocksize on dev "  | 
|---|
| 445 |  | -				"%s.\n", sb->s_id);  | 
|---|
| 446 |  | -		ret = -EINVAL;  | 
|---|
| 447 |  | -		goto error;  | 
|---|
| 448 |  | -	}  | 
|---|
| 449 |  | -  | 
|---|
| 450 |  | -	/*  | 
|---|
| 451 |  | -	 * blocksize on this device should now be set to the ADFS log2secsize  | 
|---|
| 452 |  | -	 */  | 
|---|
| 453 |  | -  | 
|---|
| 454 |  | -	sb->s_magic		= ADFS_SUPER_MAGIC;  | 
|---|
| 455 |  | -	asb->s_idlen		= dr->idlen;  | 
|---|
| 456 |  | -	asb->s_map_size		= dr->nzones | (dr->nzones_high << 8);  | 
|---|
| 457 |  | -	asb->s_map2blk		= dr->log2bpmb - dr->log2secsize;  | 
|---|
| 458 |  | -	asb->s_size    		= adfs_discsize(dr, sb->s_blocksize_bits);  | 
|---|
| 459 |  | -	asb->s_version 		= dr->format_version;  | 
|---|
| 460 |  | -	asb->s_log2sharesize	= dr->log2sharesize;  | 
|---|
| 461 |  | -  | 
|---|
| 462 |  | -	asb->s_map = adfs_read_map(sb, dr);  | 
|---|
| 463 |  | -	if (IS_ERR(asb->s_map)) {  | 
|---|
| 464 |  | -		ret =  PTR_ERR(asb->s_map);  | 
|---|
| 465 |  | -		goto error_free_bh;  | 
|---|
| 466 |  | -	}  | 
|---|
| 467 |  | -  | 
|---|
| 468 |  | -	brelse(bh);  | 
|---|
| 469 |  | -  | 
|---|
| 470 |  | -	/*  | 
|---|
| 471 |  | -	 * set up enough so that we can read an inode  | 
|---|
| 472 |  | -	 */  | 
|---|
 | 406 | +	/* set up enough so that we can read an inode */  | 
|---|
| 473 | 407 |  	sb->s_op = &adfs_sops; | 
|---|
| 474 | 408 |   | 
|---|
| 475 |  | -	dr = (struct adfs_discrecord *)(asb->s_map[0].dm_bh->b_data + 4);  | 
|---|
 | 409 | +	dr = adfs_map_discrecord(asb->s_map);  | 
|---|
| 476 | 410 |   | 
|---|
| 477 |  | -	root_obj.parent_id = root_obj.file_id = le32_to_cpu(dr->root);  | 
|---|
 | 411 | +	root_obj.parent_id = root_obj.indaddr = le32_to_cpu(dr->root);  | 
|---|
| 478 | 412 |  	root_obj.name_len  = 0; | 
|---|
| 479 | 413 |  	/* Set root object date as 01 Jan 1987 00:00:00 */ | 
|---|
| 480 | 414 |  	root_obj.loadaddr  = 0xfff0003f; | 
|---|
| .. | .. | 
|---|
| 482 | 416 |  	root_obj.size	   = ADFS_NEWDIR_SIZE; | 
|---|
| 483 | 417 |  	root_obj.attr	   = ADFS_NDA_DIRECTORY   | ADFS_NDA_OWNER_READ | | 
|---|
| 484 | 418 |  			     ADFS_NDA_OWNER_WRITE | ADFS_NDA_PUBLIC_READ; | 
|---|
| 485 |  | -	root_obj.filetype  = -1;  | 
|---|
| 486 | 419 |   | 
|---|
| 487 | 420 |  	/* | 
|---|
| 488 | 421 |  	 * If this is a F+ disk with variable length directories, | 
|---|
| 489 | 422 |  	 * get the root_size from the disc record. | 
|---|
| 490 | 423 |  	 */ | 
|---|
| 491 |  | -	if (asb->s_version) {  | 
|---|
 | 424 | +	if (dr->format_version) {  | 
|---|
| 492 | 425 |  		root_obj.size = le32_to_cpu(dr->root_size); | 
|---|
| 493 | 426 |  		asb->s_dir     = &adfs_fplus_dir_ops; | 
|---|
| 494 | 427 |  		asb->s_namelen = ADFS_FPLUS_NAME_LEN; | 
|---|
| .. | .. | 
|---|
| 507 | 440 |  	root = adfs_iget(sb, &root_obj); | 
|---|
| 508 | 441 |  	sb->s_root = d_make_root(root); | 
|---|
| 509 | 442 |  	if (!sb->s_root) { | 
|---|
| 510 |  | -		int i;  | 
|---|
| 511 |  | -		for (i = 0; i < asb->s_map_size; i++)  | 
|---|
| 512 |  | -			brelse(asb->s_map[i].dm_bh);  | 
|---|
| 513 |  | -		kfree(asb->s_map);  | 
|---|
 | 443 | +		adfs_free_map(sb);  | 
|---|
| 514 | 444 |  		adfs_error(sb, "get root inode failed\n"); | 
|---|
| 515 | 445 |  		ret = -EIO; | 
|---|
| 516 | 446 |  		goto error; | 
|---|
| 517 | 447 |  	} | 
|---|
| 518 | 448 |  	return 0; | 
|---|
| 519 | 449 |   | 
|---|
| 520 |  | -error_free_bh:  | 
|---|
| 521 |  | -	brelse(bh);  | 
|---|
| 522 | 450 |  error: | 
|---|
| 523 | 451 |  	sb->s_fs_info = NULL; | 
|---|
| 524 | 452 |  	kfree(asb); | 
|---|
| .. | .. | 
|---|
| 564 | 492 |  module_init(init_adfs_fs) | 
|---|
| 565 | 493 |  module_exit(exit_adfs_fs) | 
|---|
| 566 | 494 |  MODULE_LICENSE("GPL"); | 
|---|
 | 495 | +MODULE_IMPORT_NS(ANDROID_GKI_VFS_EXPORT_ONLY);  | 
|---|