svn commit: r273865 - in head: sbin/fsck_msdosfs sys/boot/amd64/boot1.efi

Bruce Evans brde at optusnet.com.au
Thu Oct 30 18:09:17 UTC 2014


On Thu, 30 Oct 2014, Doug Ambrisko wrote:

> Log:
>  Make UEFI booting of 4Kn disks work:
>        -	convert boot1.efi to corrrectly calculate the lba for what the
>  	media reports and convert the size based on what FreeBSD uses.
>  	The existing code would use the 512 byte lba and convert the
>  	size using 4K byte size.
>        -	make fsck_msdosfs read the boot block as 4K so the read doesn't
>  	fail on a 4Kn drive since FreeBSD will error out parition reads
>  	of a block.  Make the bpbBytesPerSec check a multiple of 512 since
>  	it can be 512 or 4K depending on the disk.  This allows fsck to
>  	pass checking the EFI partition on a 4Kn disk.

> Modified: head/sbin/fsck_msdosfs/boot.c
> ==============================================================================
> --- head/sbin/fsck_msdosfs/boot.c	Thu Oct 30 15:41:25 2014	(r273864)
> +++ head/sbin/fsck_msdosfs/boot.c	Thu Oct 30 15:52:01 2014	(r273865)
> @@ -181,7 +181,7 @@ readboot(int dosfs, struct bootblock *bo
> 	    boot->bpbResSectors + boot->bpbFATs * boot->FATsecs -
> 	    CLUST_FIRST * boot->bpbSecPerClust;
>
> -	if (boot->bpbBytesPerSec % DOSBOOTBLOCKSIZE != 0) {
> +	if (boot->bpbBytesPerSec % DOSBOOTBLOCKSIZE_REAL != 0) {
> 		pfatal("Invalid sector size: %u", boot->bpbBytesPerSec);
> 		return FSFATAL;
> 	}
>
> Modified: head/sbin/fsck_msdosfs/dosfs.h
> ==============================================================================
> --- head/sbin/fsck_msdosfs/dosfs.h	Thu Oct 30 15:41:25 2014	(r273864)
> +++ head/sbin/fsck_msdosfs/dosfs.h	Thu Oct 30 15:52:01 2014	(r273865)
> @@ -30,7 +30,9 @@
> #ifndef DOSFS_H
> #define DOSFS_H
>
> -#define DOSBOOTBLOCKSIZE 512
> +/* support 4Kn disk reads */
> +#define DOSBOOTBLOCKSIZE_REAL 512
> +#define DOSBOOTBLOCKSIZE 4096
>
> typedef	u_int32_t	cl_t;	/* type holding a cluster number */

This affects almost everywhere DOSBOOTBLOCKSIZE is used.  You only adjusted
one place.  The others are:

% boot.c:	u_char fsinfo[2 * DOSBOOTBLOCKSIZE];

fsinfo is more incorrectly sized than before.  The change allows i/o
of fsinfo[] to work with 4K-sectors, but writes of it now clobber the
area beyond what is reserved for it with 512-sectors.  fsinfo actually
has size 2 * 512, not 2 * sector_size, so reads and writes of it need
to be padded if the sector size is larger than 1024.  Old versions of
msdosfs got fsinfo stuff wrong by laying it out naturally for
sector_sized sectors, but its layout is coded for 512-sectors, so there
are further complications.

% boot.c:	u_char backup[DOSBOOTBLOCKSIZE];

Probably OK.

% boot.c:	if (boot->bpbBytesPerSec % DOSBOOTBLOCKSIZE != 0) {

Adjusted.

% boot.c:	u_char fsinfo[2 * DOSBOOTBLOCKSIZE];

Another fsinfo[] for writing.  This is the dangerous one.  It might work
OK by read/modify write of the larger size provided the modifications
are limited to the real fsinfo.

I use the following fixes:

% Index: boot.c
% ===================================================================
% RCS file: /home/ncvs/src/sbin/fsck_msdosfs/boot.c,v
% retrieving revision 1.6
% diff -u -2 -r1.6 boot.c
% --- boot.c	31 Jan 2008 13:16:29 -0000	1.6
% +++ boot.c	3 Jul 2010 16:53:33 -0000
% @@ -48,4 +48,6 @@
%  #include "fsutil.h"
% 
% +#define	IOSIZE	65536
% +
%  int
%  readboot(dosfs, boot)
% @@ -53,18 +55,48 @@
%  	struct bootblock *boot;
%  {
% +	u_char ioblock[IOSIZE];
% +	u_char iofsinfo[IOSIZE];
% +	u_char iobackup[IOSIZE];
%  	u_char block[DOSBOOTBLOCKSIZE];
%  	u_char fsinfo[2 * DOSBOOTBLOCKSIZE];
%  	u_char backup[DOSBOOTBLOCKSIZE];
% +	u_char *infop;
% +	size_t iosize;
% +	u_int secsize;
%  	int ret = FSOK;
% 
% -	if (read(dosfs, block, sizeof block) < sizeof block) {
% +	/* Search for an i/o size that works. */
% +	for (iosize = IOSIZE; iosize >= DOSBOOTBLOCKSIZE; iosize >>= 1) {
% +		if (lseek(dosfs, (off_t)0, SEEK_SET) == 0 &&
% +		    read(dosfs, ioblock, iosize) == (ssize_t)iosize)
% +			break;
% +	}
% +	if (iosize < DOSBOOTBLOCKSIZE) {
%  		perror("could not read boot block");
%  		return FSFATAL;
%  	}
% +	memcpy(block, ioblock, sizeof block);

Probe for a working size.

This stuff is also buggy in ffs utilities and the kernel ffs and msdosfs.
Most or all of these do a probe, but assume that 8K-blocks work.  I needed
better for a DCD-RW that does 2K-reads but only 32K-writes.  Support for
4K- and 8K- blocks is only enough for reading the DVD.  I only fixed this
for fsck_msdosfs, as in this patch.  Kernel msdosfs works on the DVD since
it only assumes that 8K-blocks work for reading.  fsck_msdosfs also assumed
that 512-blocks works for writing at least fsinfo[].

% 
% -	if (block[510] != 0x55 || block[511] != 0xaa) {
% -		pfatal("Invalid signature in boot block: %02x%02x", block[511], block[510]);
% +	/*
% +	 * Preliminary decode to determine where the signature might be.
% +	 * It is supposed to be at the end of a 512-block, but we used to
% +	 * put it at the end of a sector.  Accept the latter so as to fix
% +	 * it someday.
% +	 */
% +	secsize = block[11] + (block[12] << 8);
% +	if (secsize < sizeof block || secsize > IOSIZE) {
% +		perror("Preposterous or unsupported sector size");
%  		return FSFATAL;
%  	}
% +	if (block[510] != 0x55 || block[511] != 0xaa) {
% +		if (ioblock[secsize - 2] != 0x55 ||
% +		    ioblock[secsize - 1] != 0xaa) {
% +			pfatal("Invalid signature in boot block: %02x%02x",
% +			    block[511], block[510]);
% +			return FSFATAL;
% +		}
% +		pwarn(
% +	"Invalid primary signature in boot block -- using secondary\n");
% +	}
% 
%  	memset(boot, 0, sizeof *boot);

Allow misplaced signatures.

% @@ -107,11 +139,12 @@
%  		boot->Backup = block[50] + (block[51] << 8);
% 
% +		iosize = (secsize >= sizeof fsinfo) ?  secsize : sizeof fsinfo;
%  		if (lseek(dosfs, boot->FSInfo * boot->BytesPerSec, SEEK_SET)
%  		    != boot->FSInfo * boot->BytesPerSec
% -		    || read(dosfs, fsinfo, sizeof fsinfo)
% -		    != sizeof fsinfo) {
% +		    || read(dosfs, iofsinfo, iosize) != (ssize_t)iosize) {
%  			perror("could not read fsinfo block");
%  			return FSFATAL;
%  		}
% +		memcpy(fsinfo, iofsinfo, sizeof fsinfo);
%  		if (memcmp(fsinfo, "RRaA", 4)
%  		    || memcmp(fsinfo + 0x1e4, "rrAa", 4)
% @@ -124,4 +157,22 @@
%  		    || fsinfo[0x3fe] != 0x55
%  		    || fsinfo[0x3ff] != 0xaa) {
% +			infop = &iofsinfo[secsize - DOSBOOTBLOCKSIZE];
% +			if (memcmp(fsinfo, "RRaA", 4) == 0 &&
% +			    memcmp(infop + 0x1e4, "rrAa", 4) == 0 &&
% +			    infop[0x1fc] == 0 &&
% +			    infop[0x1fd] == 0 &&
% +			    infop[0x1fe] == 0x55 &&
% +			    infop[0x1ff] == 0xaa) {
% +				pwarn(
% +		    "Invalid signature in fsinfo block -- using secondary\n");
% +				/*
% +				 * Silently fix up the actual fsinfo data
% +				 * (just 2 32-bit words) since this data
% +				 * is advisory and the indentation is
% +				 * already too painful to ask about this.
% +				 */
% +				memcpy(fsinfo + 0x1e8, infop + 0x1e8, 8);
% +				goto over;
% +			}
%  			pwarn("Invalid signature in fsinfo block\n");
%  			if (ask(0, "Fix")) {
% @@ -134,12 +185,14 @@
%  				fsinfo[0x3fe] = 0x55;
%  				fsinfo[0x3ff] = 0xaa;
% +				memcpy(iofsinfo, fsinfo, sizeof fsinfo);
%  				if (lseek(dosfs, boot->FSInfo * boot->BytesPerSec, SEEK_SET)
%  				    != boot->FSInfo * boot->BytesPerSec
% -				    || write(dosfs, fsinfo, sizeof fsinfo)
% -				    != sizeof fsinfo) {
% +				    || write(dosfs, iofsinfo, iosize)
% +				    != (ssize_t)iosize) {
%  					perror("Unable to write FSInfo");
%  					return FSFATAL;
%  				}
%  				ret = FSBOOTMOD;
% +over: ;
%  			} else
%  				boot->FSInfo = 0;

Allow misplaced signatures (and data?) in fsinfo.  newfs_msdos used to
produce them.

% @@ -107,11 +139,12 @@
%  		boot->Backup = block[50] + (block[51] << 8);
% 
% +		iosize = (secsize >= sizeof fsinfo) ?  secsize : sizeof fsinfo;
%  		if (lseek(dosfs, boot->FSInfo * boot->BytesPerSec, SEEK_SET)
%  		    != boot->FSInfo * boot->BytesPerSec
% -		    || read(dosfs, fsinfo, sizeof fsinfo)
% -		    != sizeof fsinfo) {
% +		    || read(dosfs, iofsinfo, iosize) != (ssize_t)iosize) {
%  			perror("could not read fsinfo block");
%  			return FSFATAL;
%  		}
% +		memcpy(fsinfo, iofsinfo, sizeof fsinfo);
%  		if (memcmp(fsinfo, "RRaA", 4)
%  		    || memcmp(fsinfo + 0x1e4, "rrAa", 4)
% @@ -124,4 +157,22 @@
%  		    || fsinfo[0x3fe] != 0x55
%  		    || fsinfo[0x3ff] != 0xaa) {
% +			infop = &iofsinfo[secsize - DOSBOOTBLOCKSIZE];
% +			if (memcmp(fsinfo, "RRaA", 4) == 0 &&
% +			    memcmp(infop + 0x1e4, "rrAa", 4) == 0 &&
% +			    infop[0x1fc] == 0 &&
% +			    infop[0x1fd] == 0 &&
% +			    infop[0x1fe] == 0x55 &&
% +			    infop[0x1ff] == 0xaa) {
% +				pwarn(
% +		    "Invalid signature in fsinfo block -- using secondary\n");
% +				/*
% +				 * Silently fix up the actual fsinfo data
% +				 * (just 2 32-bit words) since this data
% @@ -156,8 +209,9 @@
%  		if (lseek(dosfs, boot->Backup * boot->BytesPerSec, SEEK_SET)
%  		    != boot->Backup * boot->BytesPerSec
% -		    || read(dosfs, backup, sizeof backup) != sizeof  backup) {
% +		    || read(dosfs, iobackup, secsize) != (ssize_t)secsize) {
%  			perror("could not read backup bootblock");
%  			return FSFATAL;
%  		}
% +		memcpy(backup, iobackup, sizeof backup);
%  		backup[65] = block[65];				/* XXX */
%  		if (memcmp(block + 11, backup + 11, 79)) {

The backup boot block can be read directly, but I still do some re-blocking
for it for some reason.

% @@ -235,12 +299,18 @@
%  	struct bootblock *boot;
%  {
% +	u_char iofsinfo[IOSIZE];
%  	u_char fsinfo[2 * DOSBOOTBLOCKSIZE];

Here DOSBOOTBLOCKSIZE is an obfuscated spelling of 512 or
IBM_PC_SECTOR_SIZE_IN_1982 though fsinfo didn't exist until 10(?) years
later.  (512 was advanced for the time.  I used systems
with mainly 256-byte sectors and some 128-byte sectors.  Having both on
the same disk was most complicated.)

% +	size_t iosize;
% +	u_int secsize;
% 
% +	secsize = boot->BytesPerSec;
% +	iosize = (secsize >= sizeof fsinfo) ? secsize : sizeof fsinfo;
%  	if (lseek(dosfs, boot->FSInfo * boot->BytesPerSec, SEEK_SET)
%  	    != boot->FSInfo * boot->BytesPerSec
% -	    || read(dosfs, fsinfo, sizeof fsinfo) != sizeof fsinfo) {
% +	    || read(dosfs, iofsinfo, iosize) != (ssize_t)iosize) {
%  		perror("could not read fsinfo block");
%  		return FSFATAL;
%  	}
% +	memcpy(fsinfo, iofsinfo, sizeof fsinfo);
%  	fsinfo[0x1e8] = (u_char)boot->FSFree;
%  	fsinfo[0x1e9] = (u_char)(boot->FSFree >> 8);
% @@ -251,8 +321,8 @@
%  	fsinfo[0x1ee] = (u_char)(boot->FSNext >> 16);
%  	fsinfo[0x1ef] = (u_char)(boot->FSNext >> 24);
% +	memcpy(iofsinfo, fsinfo, sizeof fsinfo);
%  	if (lseek(dosfs, boot->FSInfo * boot->BytesPerSec, SEEK_SET)
%  	    != boot->FSInfo * boot->BytesPerSec
% -	    || write(dosfs, fsinfo, sizeof fsinfo)
% -	    != sizeof fsinfo) {
% +	    || write(dosfs, iofsinfo, iosize) != (ssize_t)iosize) {
%  		perror("Unable to write FSInfo");
%  		return FSFATAL;

I hardly tested fixing or writing back fsinfo.  It is only advisory, so it
can be replaced by garbage without much loss.  fsck_msdosfs doesn't really
understand it, and has bugs like asking whether to fix it but only writing
the fixes accidentally if it needs to write for other reasons.

Bruce


More information about the svn-src-all mailing list