patch-2.4.5 linux/fs/fat/inode.c

Next file: linux/fs/fat/misc.c
Previous file: linux/fs/fat/file.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.4.4/linux/fs/fat/inode.c linux/fs/fat/inode.c
@@ -201,7 +201,7 @@
 }
 
 
-static int parse_options(char *options,int *fat, int *blksize, int *debug,
+static int parse_options(char *options,int *fat, int *debug,
 			 struct fat_mount_options *opts,
 			 char *cvf_format, char *cvf_options)
 {
@@ -310,13 +310,8 @@
 			else opts->quiet = 1;
 		}
 		else if (!strcmp(this_char,"blocksize")) {
-			if (!value || !*value) ret = 0;
-			else {
-				*blksize = simple_strtoul(value,&value,0);
-				if (*value || (*blksize != 512 &&
-					*blksize != 1024 && *blksize != 2048))
-					ret = 0;
-			}
+			printk("FAT: blocksize option is obsolete, "
+			       "not supported now\n");
 		}
 		else if (!strcmp(this_char,"sys_immutable")) {
 			if (value) ret = 0;
@@ -383,8 +378,8 @@
 		MSDOS_I(inode)->i_start = sbi->root_cluster;
 		if ((nr = MSDOS_I(inode)->i_start) != 0) {
 			while (nr != -1) {
-				inode->i_size += SECTOR_SIZE*sbi->cluster_size;
-				if (!(nr = fat_access(sb,nr,-1))) {
+				inode->i_size += 1 << sbi->cluster_bits;
+				if (!(nr = fat_access(sb, nr, -1))) {
 					printk("Directory %ld: bad FAT\n",
 					       inode->i_ino);
 					break;
@@ -393,13 +388,11 @@
 		}
 	} else {
 		MSDOS_I(inode)->i_start = 0;
-		inode->i_size = sbi->dir_entries*
-			sizeof(struct msdos_dir_entry);
+		inode->i_size = sbi->dir_entries * sizeof(struct msdos_dir_entry);
 	}
-	inode->i_blksize = sbi->cluster_size* SECTOR_SIZE;
-	inode->i_blocks =
-		((inode->i_size+inode->i_blksize-1)>>sbi->cluster_bits) *
-		    sbi->cluster_size;
+	inode->i_blksize = 1 << sbi->cluster_bits;
+	inode->i_blocks = ((inode->i_size + inode->i_blksize - 1)
+			   & ~(inode->i_blksize - 1)) / 512;
 	MSDOS_I(inode)->i_logstart = 0;
 	MSDOS_I(inode)->mmu_private = inode->i_size;
 
@@ -432,10 +425,9 @@
 	struct fat_boot_sector *b;
 	struct msdos_sb_info *sbi = MSDOS_SB(sb);
 	char *p;
-	int data_sectors,logical_sector_size,sector_mult,fat_clusters=0;
-	int debug,error,fat,cp;
-	int blksize;
-	int fat32;
+	int logical_sector_size, hard_blksize, fat_clusters = 0;
+	unsigned int total_sectors, rootdir_sectors;
+	int fat32, debug, error, fat, cp;
 	struct fat_mount_options opts;
 	char buf[50];
 	int i;
@@ -451,35 +443,26 @@
 
 	sb->s_maxbytes = MAX_NON_LFS;
 	sb->s_op = &fat_sops;
-	blksize = get_hardsect_size(sb->s_dev);
-	if (!blksize)
-		blksize = 512;
-	if (blksize != 512)
-		printk ("MSDOS: Hardware sector size is %d\n",blksize);
+
+	hard_blksize = get_hardsect_size(sb->s_dev);
+	if (!hard_blksize)
+		hard_blksize = 512;
+	if (hard_blksize != 512)
+		printk("MSDOS: Hardware sector size is %d\n", hard_blksize);
 
 	opts.isvfat = sbi->options.isvfat;
-	if (!parse_options((char *) data, &fat, &blksize, &debug, &opts, 
-			   cvf_format, cvf_options)
-	    || (blksize != 512 && blksize != 1024 && blksize != 2048))
+	if (!parse_options((char *) data, &fat, &debug, &opts,
+			   cvf_format, cvf_options))
 		goto out_fail;
 	/* N.B. we should parse directly into the sb structure */
 	memcpy(&(sbi->options), &opts, sizeof(struct fat_mount_options));
 
 	fat_cache_init();
-	if( blksize > 1024 )
-	  {
-	    /* Force the superblock to a larger size here. */
-	    sb->s_blocksize = blksize;
-	    set_blocksize(sb->s_dev, blksize);
-	  }
-	else
-	  {
-	    /* The first read is always 1024 bytes */
-	    sb->s_blocksize = 1024;
-	    set_blocksize(sb->s_dev, 1024);
-	  }
+
+	sb->s_blocksize = hard_blksize;
+	set_blocksize(sb->s_dev, hard_blksize);
 	bh = bread(sb->s_dev, 0, sb->s_blocksize);
-	if (bh == NULL || !buffer_uptodate(bh)) {
+	if (bh == NULL) {
 		brelse (bh);
 		goto out_no_bread;
 	}
@@ -499,110 +482,122 @@
  * (by Drew Eckhardt)
  */
 
-#define ROUND_TO_MULTIPLE(n,m) ((n) && (m) ? (n)+(m)-1-((n)-1)%(m) : 0)
-    /* don't divide by zero */
 
 	b = (struct fat_boot_sector *) bh->b_data;
 	logical_sector_size =
 		CF_LE_W(get_unaligned((unsigned short *) &b->sector_size));
-	sector_mult = logical_sector_size >> SECTOR_BITS;
-	sbi->cluster_size = b->cluster_size*sector_mult;
-	if (!sbi->cluster_size || (sbi->cluster_size & (sbi->cluster_size-1))) {
-		printk("fatfs: bogus cluster size\n");
+	if (!logical_sector_size
+	    || (logical_sector_size & (logical_sector_size - 1))) {
+		printk("fatfs: bogus logical sector size %d\n",
+		       logical_sector_size);
+		brelse(bh);
+		goto out_invalid;
+	}
+
+	sbi->cluster_size = b->cluster_size;
+	if (!sbi->cluster_size || (sbi->cluster_size & (sbi->cluster_size - 1))) {
+		printk("fatfs: bogus cluster size %d\n", sbi->cluster_size);
 		brelse(bh);
 		goto out_invalid;
 	}
-	for (sbi->cluster_bits=0;
-	     1<<sbi->cluster_bits<sbi->cluster_size;
-	     sbi->cluster_bits++)
-		;
-	sbi->cluster_bits += SECTOR_BITS;
+
+	sbi->cluster_bits = ffs(logical_sector_size * sbi->cluster_size) - 1;
 	sbi->fats = b->fats;
-	sbi->fat_start = CF_LE_W(b->reserved)*sector_mult;
+	sbi->fat_start = CF_LE_W(b->reserved);
 	if (!b->fat_length && b->fat32_length) {
 		struct fat_boot_fsinfo *fsinfo;
+		struct buffer_head *fsinfo_bh;
+		int fsinfo_block, fsinfo_offset;
 
 		/* Must be FAT32 */
 		fat32 = 1;
-		sbi->fat_length= CF_LE_L(b->fat32_length)*sector_mult;
+		sbi->fat_length = CF_LE_L(b->fat32_length);
 		sbi->root_cluster = CF_LE_L(b->root_cluster);
 
+		sbi->fsinfo_sector = CF_LE_W(b->info_sector);
 		/* MC - if info_sector is 0, don't multiply by 0 */
-		if(CF_LE_W(b->info_sector) == 0) {
-			sbi->fsinfo_offset =
-	 			logical_sector_size + 0x1e0;
-		} else {
-			sbi->fsinfo_offset =
-				(CF_LE_W(b->info_sector) * logical_sector_size)
-				+ 0x1e0;
-		}
-		if (sbi->fsinfo_offset + sizeof(struct fat_boot_fsinfo) > sb->s_blocksize) {
-			printk("fat_read_super: Bad fsinfo_offset\n");
-			brelse(bh);
-			goto out_invalid;
-		}
-		fsinfo = (struct fat_boot_fsinfo *)
-			&bh->b_data[sbi->fsinfo_offset];
-		if (CF_LE_L(fsinfo->signature) != 0x61417272) {
-			printk("fat_read_super: Did not find valid FSINFO "
-				"signature. Found 0x%x\n",
-				CF_LE_L(fsinfo->signature));
+		if (sbi->fsinfo_sector == 0)
+			sbi->fsinfo_sector = 1;
+
+		fsinfo_block =
+			(sbi->fsinfo_sector * logical_sector_size) / hard_blksize;
+		fsinfo_offset =
+			(sbi->fsinfo_sector * logical_sector_size) % hard_blksize;
+		fsinfo_bh = bh;
+		if (bh->b_blocknr != fsinfo_block) {
+			fsinfo_bh = bread(sb->s_dev, fsinfo_block, hard_blksize);
+			if (fsinfo_bh == NULL) {
+				printk("FAT: bread failed, fsinfo block %d\n",
+				       fsinfo_block);
+				brelse(bh);
+				goto out_invalid;
+			}
+		}
+		fsinfo = (struct fat_boot_fsinfo *)&fsinfo_bh->b_data[fsinfo_offset];
+		if (!IS_FSINFO(fsinfo)) {
+			printk("FAT: Did not find valid FSINFO signature.\n"
+			       "Found signature1 0x%x signature2 0x%x sector=%ld.\n",
+			       CF_LE_L(fsinfo->signature1),
+			       CF_LE_L(fsinfo->signature2),
+			       sbi->fsinfo_sector);
 		} else {
 			sbi->free_clusters = CF_LE_L(fsinfo->free_clusters);
 		}
+
+		if (bh->b_blocknr != fsinfo_block)
+			brelse(fsinfo_bh);
 	} else {
 		fat32 = 0;
-		sbi->fat_length = CF_LE_W(b->fat_length)*sector_mult;
+		sbi->fat_length = CF_LE_W(b->fat_length);
 		sbi->root_cluster = 0;
 		sbi->free_clusters = -1; /* Don't know yet */
 	}
-	sbi->dir_start= CF_LE_W(b->reserved)*sector_mult+
-	    b->fats*sbi->fat_length;
+
+	sbi->dir_per_block = logical_sector_size / sizeof(struct msdos_dir_entry);
+	sbi->dir_per_block_bits = ffs(sbi->dir_per_block) - 1;
+
+	sbi->dir_start = sbi->fat_start + sbi->fats * sbi->fat_length;
 	sbi->dir_entries =
-		CF_LE_W(get_unaligned((unsigned short *) &b->dir_entries));
-	sbi->data_start = sbi->dir_start+ROUND_TO_MULTIPLE((
-	    sbi->dir_entries << MSDOS_DIR_BITS) >> SECTOR_BITS,
-	    sector_mult);
-	data_sectors = CF_LE_W(get_unaligned((unsigned short *) &b->sectors));
-	if (!data_sectors) {
-		data_sectors = CF_LE_L(b->total_sect);
-	}
-	data_sectors = data_sectors * sector_mult - sbi->data_start;
-	error = !b->cluster_size || !sector_mult;
+		CF_LE_W(get_unaligned((unsigned short *)&b->dir_entries));
+	rootdir_sectors = sbi->dir_entries
+		* sizeof(struct msdos_dir_entry) / logical_sector_size;
+	sbi->data_start = sbi->dir_start + rootdir_sectors;
+	total_sectors = CF_LE_W(get_unaligned((unsigned short *)&b->sectors));
+	if (total_sectors == 0)
+		total_sectors = CF_LE_L(b->total_sect);
+	sbi->clusters = (total_sectors - sbi->data_start) / sbi->cluster_size;
+
+	error = 0;
 	if (!error) {
-		sbi->clusters = b->cluster_size ? data_sectors/
-		    b->cluster_size/sector_mult : 0;
 		sbi->fat_bits = fat32 ? 32 :
 			(fat ? fat :
 			 (sbi->clusters > MSDOS_FAT12 ? 16 : 12));
-		fat_clusters = sbi->fat_length*SECTOR_SIZE*8/
-		    sbi->fat_bits;
-		error = !sbi->fats || (sbi->dir_entries &
-		    (MSDOS_DPS-1)) || sbi->clusters+2 > fat_clusters+
-		    MSDOS_MAX_EXTRA || (logical_sector_size & (SECTOR_SIZE-1))
-		    || !b->secs_track || !b->heads;
+		fat_clusters =
+			sbi->fat_length * logical_sector_size * 8 / sbi->fat_bits;
+		error = !sbi->fats || (sbi->dir_entries & (sbi->dir_per_block - 1))
+			|| sbi->clusters + 2 > fat_clusters + MSDOS_MAX_EXTRA
+			|| logical_sector_size < 512
+			|| PAGE_CACHE_SIZE < logical_sector_size
+			|| !b->secs_track || !b->heads;
 	}
 	brelse(bh);
-	set_blocksize(sb->s_dev, blksize);
-	/*
-		This must be done after the brelse because the bh is a dummy
-		allocated by fat_bread (see buffer.c)
-	*/
-	sb->s_blocksize = blksize;    /* Using this small block size solves */
-				/* the misfit with buffer cache and cluster */
-				/* because clusters (DOS) are often aligned */
-				/* on odd sectors. */
-	sb->s_blocksize_bits = blksize == 512 ? 9 : (blksize == 1024 ? 10 : 11);
+
+	sb->s_blocksize = logical_sector_size;
+	sb->s_blocksize_bits = ffs(logical_sector_size) - 1;
+	if (sb->s_blocksize >= hard_blksize) {
+		set_blocksize(sb->s_dev, sb->s_blocksize);
+		sbi->cvf_format = &default_cvf;
+	} else {
+		set_blocksize(sb->s_dev, hard_blksize);
+		sbi->cvf_format = &bigblock_cvf;
+	}
+
 	if (!strcmp(cvf_format,"none"))
 		i = -1;
 	else
 		i = detect_cvf(sb,cvf_format);
 	if (i >= 0)
 		error = cvf_formats[i]->mount_cvf(sb,cvf_options);
-	else if (sb->s_blocksize == 512)
-		sbi->cvf_format = &default_cvf;
-	else
-		sbi->cvf_format = &bigblock_cvf;
 	if (error || debug) {
 		/* The MSDOS_CAN_BMAP is obsolete, but left just to remember */
 		printk("[MS-DOS FS Rel. 12,FAT %d,check=%c,conv=%c,"
@@ -612,18 +607,19 @@
 		       MSDOS_CAN_BMAP(sbi) ? ",bmap" : "");
 		printk("[me=0x%x,cs=%d,#f=%d,fs=%d,fl=%ld,ds=%ld,de=%d,data=%ld,"
 		       "se=%d,ts=%ld,ls=%d,rc=%ld,fc=%u]\n",
-			b->media,sbi->cluster_size,
-			sbi->fats,sbi->fat_start,
-			sbi->fat_length,
+		       b->media,sbi->cluster_size,
+		       sbi->fats,sbi->fat_start,
+		       sbi->fat_length,
 		       sbi->dir_start,sbi->dir_entries,
 		       sbi->data_start,
 		       CF_LE_W(*(unsigned short *) &b->sectors),
 		       (unsigned long)b->total_sect,logical_sector_size,
 		       sbi->root_cluster,sbi->free_clusters);
-		printk ("Transaction block size = %d\n",blksize);
+		printk ("Transaction block size = %d\n", hard_blksize);
 	}
-	if (i<0) if (sbi->clusters+2 > fat_clusters)
-		sbi->clusters = fat_clusters-2;
+	if (i < 0)
+		if (sbi->clusters + 2 > fat_clusters)
+			sbi->clusters = fat_clusters - 2;
 	if (error)
 		goto out_invalid;
 
@@ -692,7 +688,7 @@
 	}
 	if(sbi->private_data)
 		kfree(sbi->private_data);
-	sbi->private_data=NULL;
+	sbi->private_data = NULL;
  
 	return NULL;
 }
@@ -717,7 +713,7 @@
 	}
 	unlock_fat(sb);
 	buf->f_type = sb->s_magic;
-	buf->f_bsize = MSDOS_SB(sb)->cluster_size*SECTOR_SIZE;
+	buf->f_bsize = 1 << MSDOS_SB(sb)->cluster_bits;
 	buf->f_blocks = MSDOS_SB(sb)->clusters;
 	buf->f_bfree = free;
 	buf->f_bavail = free;
@@ -796,8 +792,8 @@
 #endif
 		if ((nr = MSDOS_I(inode)->i_start) != 0)
 			while (nr != -1) {
-				inode->i_size += SECTOR_SIZE*sbi->cluster_size;
-				if (!(nr = fat_access(sb,nr,-1))) {
+				inode->i_size += 1 << sbi->cluster_bits;
+				if (!(nr = fat_access(sb, nr, -1))) {
 					printk("Directory %ld: bad FAT\n",
 					    inode->i_ino);
 					break;
@@ -828,12 +824,11 @@
 			inode->i_flags |= S_IMMUTABLE;
 	MSDOS_I(inode)->i_attrs = de->attr & ATTR_UNUSED;
 	/* this is as close to the truth as we can get ... */
-	inode->i_blksize = sbi->cluster_size*SECTOR_SIZE;
-	inode->i_blocks =
-		((inode->i_size+inode->i_blksize-1)>>sbi->cluster_bits) *
-		sbi->cluster_size;
+	inode->i_blksize = 1 << sbi->cluster_bits;
+	inode->i_blocks = ((inode->i_size + inode->i_blksize - 1)
+			   & ~(inode->i_blksize - 1)) / 512;
 	inode->i_mtime = inode->i_atime =
-	    date_dos2unix(CF_LE_W(de->time),CF_LE_W(de->date));
+		date_dos2unix(CF_LE_W(de->time),CF_LE_W(de->date));
 	inode->i_ctime =
 		MSDOS_SB(sb)->options.isvfat
 		? date_dos2unix(CF_LE_W(de->ctime),CF_LE_W(de->cdate))
@@ -854,7 +849,7 @@
 		return;
 	}
 	lock_kernel();
-	if (!(bh = fat_bread(sb, i_pos >> MSDOS_DPB_BITS))) {
+	if (!(bh = fat_bread(sb, i_pos >> MSDOS_SB(sb)->dir_per_block_bits))) {
 		printk("dev = %s, ino = %d\n", kdevname(inode->i_dev), i_pos);
 		fat_fs_panic(sb, "msdos_write_inode: unable to read i-node block");
 		unlock_kernel();
@@ -869,7 +864,7 @@
 	}
 
 	raw_entry = &((struct msdos_dir_entry *) (bh->b_data))
-	    [i_pos & (MSDOS_DPB-1)];
+	    [i_pos & (MSDOS_SB(sb)->dir_per_block - 1)];
 	if (S_ISDIR(inode->i_mode)) {
 		raw_entry->attr = ATTR_DIR;
 		raw_entry->size = 0;

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)