diff mbox

[04/32] vvfat: Do not clobber the user's geometry

Message ID 1340984094-5451-5-git-send-email-armbru@redhat.com
State New
Headers show

Commit Message

Markus Armbruster June 29, 2012, 3:34 p.m. UTC
vvfat creates a virtual VFAT filesystem with a certain logical
geometry that depends on its options.  It sets the "geometry hint" to
this geometry.  It is the only block driver to do this.

The geometry hint is about about *physical* geometry, and used only by
certain hard disk device models.

vvfat's hint is normally invisible for device models, because
bdrv_open() puts a raw format on top of vvfat's fat protocol.  That
raw format is where drive_init() puts the user's geometry (if any),
and where the device model gets it from.

Nobody complained, because the default physical geometry is the same
as vvfat's logical geometry:

    opts        LCHS        def. PCHS
                1024,16,63  same
    :32:        1024,16,63  same
    :16:        1024,16,63  same
    :12:          64,16,63  same

Except when you specify :floppy:

    opts        LCHS        def. PCHS
       :floppy:   80, 2,36  5,16,63
    :32:floppy:   80, 2,36  5,16,63
    :16:floppy:   80, 2,36  5,16,63
    :12:floppy:   80, 2,18  2,16,63

Silly thing to do for use with a hard disk.

However, the "raw" format can be suppressed by adding an
redundant-looking "format=vvfat" to "file=fat:FOO".  Then, vvfat's
hint clobbers the user's geometry, i.e. -drive options cyls, heads,
secs get silently ignored.  Don't do that.

No change without format=vvfat.  With it, the user's hard disk
geometry (-drive options cyls, heads, secs) is now obeyed, and the
default hard disk geometry with :floppy: now matches the one without
format=vvfat.

Signed-off-by: Markus Armbruster <armbru@redhat.com>
---
 block/vvfat.c |   52 ++++++++++++++++++++++++++++------------------------
 1 files changed, 28 insertions(+), 24 deletions(-)

Comments

Kevin Wolf July 4, 2012, 3:23 p.m. UTC | #1
Am 29.06.2012 17:34, schrieb Markus Armbruster:
> vvfat creates a virtual VFAT filesystem with a certain logical
> geometry that depends on its options.  It sets the "geometry hint" to
> this geometry.  It is the only block driver to do this.
> 
> The geometry hint is about about *physical* geometry, and used only by
> certain hard disk device models.
> 
> vvfat's hint is normally invisible for device models, because
> bdrv_open() puts a raw format on top of vvfat's fat protocol.  That
> raw format is where drive_init() puts the user's geometry (if any),
> and where the device model gets it from.
> 
> Nobody complained, because the default physical geometry is the same
> as vvfat's logical geometry:
> 
>     opts        LCHS        def. PCHS
>                 1024,16,63  same
>     :32:        1024,16,63  same
>     :16:        1024,16,63  same
>     :12:          64,16,63  same
> 
> Except when you specify :floppy:
> 
>     opts        LCHS        def. PCHS
>        :floppy:   80, 2,36  5,16,63
>     :32:floppy:   80, 2,36  5,16,63
>     :16:floppy:   80, 2,36  5,16,63
>     :12:floppy:   80, 2,18  2,16,63
> 
> Silly thing to do for use with a hard disk.
> 
> However, the "raw" format can be suppressed by adding an
> redundant-looking "format=vvfat" to "file=fat:FOO".  Then, vvfat's
> hint clobbers the user's geometry, i.e. -drive options cyls, heads,
> secs get silently ignored.  Don't do that.
> 
> No change without format=vvfat.  With it, the user's hard disk
> geometry (-drive options cyls, heads, secs) is now obeyed, and the
> default hard disk geometry with :floppy: now matches the one without
> format=vvfat.
> 
> Signed-off-by: Markus Armbruster <armbru@redhat.com>

For some values of "obeyed". If I understand correctly, the user defined
geometry will indeed by visible for the device emulation now, but this
still doesn't mean that vvfat also provides an image that matches this
geometry. Not sure if it is a good idea to allow such mismatches.

> @@ -1067,19 +1074,16 @@ DLOG(if (stderr == NULL) {
>      else
>  	dirname += i+1;
>  
> -    bs->total_sectors=bs->cyls*bs->heads*bs->secs;
> +    bs->total_sectors = cyls * heads * secs;
>  
> -    if(init_directories(s, dirname))
> +    if (init_directories(s, dirname, heads, secs)) {
>  	return -1;
> +    }
>  
>      s->sector_count = s->faked_sectors + s->sectors_per_cluster*s->cluster_count;
>  
>      if(s->first_sectors_number==0x40)
> -	init_mbr(s);
> -    else {
> -        /* MS-DOS does not like to know about CHS (?). */
> -	bs->heads = bs->cyls = bs->secs = 0;
> -    }
> +        init_mbr(s, cyls, heads, secs);

You can add braces here while touching the code.

Kevin
Paolo Bonzini July 4, 2012, 4:25 p.m. UTC | #2
Il 04/07/2012 17:23, Kevin Wolf ha scritto:
>> >     opts        LCHS        def. PCHS
>> >        :floppy:   80, 2,36  5,16,63
>> >     :32:floppy:   80, 2,36  5,16,63
>> >     :16:floppy:   80, 2,36  5,16,63
>> >     :12:floppy:   80, 2,18  2,16,63
>> > 
>> > Silly thing to do for use with a hard disk.
>> > 
>> > However, the "raw" format can be suppressed by adding an
>> > redundant-looking "format=vvfat" to "file=fat:FOO".  Then, vvfat's
>> > hint clobbers the user's geometry, i.e. -drive options cyls, heads,
>> > secs get silently ignored.  Don't do that.
>> > 
>> > No change without format=vvfat.  With it, the user's hard disk
>> > geometry (-drive options cyls, heads, secs) is now obeyed, and the
>> > default hard disk geometry with :floppy: now matches the one without
>> > format=vvfat.
>> > 
>> > Signed-off-by: Markus Armbruster <armbru@redhat.com>
> For some values of "obeyed". If I understand correctly, the user defined
> geometry will indeed by visible for the device emulation now, but this
> still doesn't mean that vvfat also provides an image that matches this
> geometry. Not sure if it is a good idea to allow such mismatches.
> 

Does this only matter if you declare an image with :floppy: and pass it
as a hard disk?  Then we can honestly say we don't care...

Paolo
Kevin Wolf July 5, 2012, 7:06 a.m. UTC | #3
Am 04.07.2012 18:25, schrieb Paolo Bonzini:
> Il 04/07/2012 17:23, Kevin Wolf ha scritto:
>>>>     opts        LCHS        def. PCHS
>>>>        :floppy:   80, 2,36  5,16,63
>>>>     :32:floppy:   80, 2,36  5,16,63
>>>>     :16:floppy:   80, 2,36  5,16,63
>>>>     :12:floppy:   80, 2,18  2,16,63
>>>>
>>>> Silly thing to do for use with a hard disk.
>>>>
>>>> However, the "raw" format can be suppressed by adding an
>>>> redundant-looking "format=vvfat" to "file=fat:FOO".  Then, vvfat's
>>>> hint clobbers the user's geometry, i.e. -drive options cyls, heads,
>>>> secs get silently ignored.  Don't do that.
>>>>
>>>> No change without format=vvfat.  With it, the user's hard disk
>>>> geometry (-drive options cyls, heads, secs) is now obeyed, and the
>>>> default hard disk geometry with :floppy: now matches the one without
>>>> format=vvfat.
>>>>
>>>> Signed-off-by: Markus Armbruster <armbru@redhat.com>
>> For some values of "obeyed". If I understand correctly, the user defined
>> geometry will indeed by visible for the device emulation now, but this
>> still doesn't mean that vvfat also provides an image that matches this
>> geometry. Not sure if it is a good idea to allow such mismatches.
>>
> 
> Does this only matter if you declare an image with :floppy: and pass it
> as a hard disk?  Then we can honestly say we don't care...

As I understand it, the patch has two effect:

1. the user's hard disk geometry (-drive options cyls, heads, secs) is
now obeyed
2. the default hard disk geometry with :floppy: now matches the one
without format=vvfat

I don't care about 2. indeed, but doing 1. right would be nice.

Kevin
Markus Armbruster July 5, 2012, 9:16 a.m. UTC | #4
Kevin Wolf <kwolf@redhat.com> writes:

> Am 04.07.2012 18:25, schrieb Paolo Bonzini:
>> Il 04/07/2012 17:23, Kevin Wolf ha scritto:
>>>>>     opts        LCHS        def. PCHS
>>>>>        :floppy:   80, 2,36  5,16,63
>>>>>     :32:floppy:   80, 2,36  5,16,63
>>>>>     :16:floppy:   80, 2,36  5,16,63
>>>>>     :12:floppy:   80, 2,18  2,16,63
>>>>>
>>>>> Silly thing to do for use with a hard disk.
>>>>>
>>>>> However, the "raw" format can be suppressed by adding an
>>>>> redundant-looking "format=vvfat" to "file=fat:FOO".  Then, vvfat's
>>>>> hint clobbers the user's geometry, i.e. -drive options cyls, heads,
>>>>> secs get silently ignored.  Don't do that.
>>>>>
>>>>> No change without format=vvfat.  With it, the user's hard disk
>>>>> geometry (-drive options cyls, heads, secs) is now obeyed, and the
>>>>> default hard disk geometry with :floppy: now matches the one without
>>>>> format=vvfat.
>>>>>
>>>>> Signed-off-by: Markus Armbruster <armbru@redhat.com>
>>> For some values of "obeyed". If I understand correctly, the user defined
>>> geometry will indeed by visible for the device emulation now, but this
>>> still doesn't mean that vvfat also provides an image that matches this
>>> geometry. Not sure if it is a good idea to allow such mismatches.

This patch doesn't let the user do anything he couldn't do before.  It
only makes the special case "vvfat without raw on top" consistent with
all other block backend configurations.  With "raw over vvfat" in
particular.

So, if anything needs additional user parameter validation, it's the
common case.  Note that any other image type could contain the very same
FAT filesystem as a vvfat one, with the very same impact on a guest
vulnerable to mismatches between geometry and block device contents.

To avoid confusion, let me recap geometry.

Physical geometry is a property of the disk.  Or rather, a property of
the disk's legacy CHS command interface; it ceased to have any useful
relation to platters and heads long ago.  Non-legacy commands use LBA to
address blocks.

Logical geometry is a property of the PC BIOS legacy CHS interface (int
13h).  It was invented when disks outgrew the BIOS's CHS limits.  It
need not match physical geometry (*not* matching is its purpose).  The
BIOS translates logical geometry to something the disk understands.
Long ago, this meant translating to physical CHS.  Nowadays, it means
translating to LBA.  As far as I can tell, SeaBIOS always translates to
LBA.  SeaBIOS picks a logical geometry based on what it finds in CMOS.

Logical geometry matters only for software that uses legacy int 13h.
DOS, basically.

Physical geometry matters only for software that talks to the disk
directly, using legacy CHS commands.  Sufficently old versions of Unix,
perhaps?

The choice of geometry isn't really important, as long as:

1. The mapping to LBA is sane: lba = ((c * #h) + h * #s) + (s-1)

   where c, h, s is the address, and #c, #h, #s is the geometry.
   Increasing first s, then h, then c runs through the lbas in order,
   regardless of what you picked for geometry.

   For physical geometry, the device model maps.  As far as I can tell,
   they all map sanely.

   For logical geometry, SeaBIOS maps.  As far as I can tell, it maps
   sanely.

   Actually, even an insane mapping would work for block drivers that
   only schlep blocks, as long as it doesn't change.  Same as with
   physical disks.

2. The OS is happy with the partition table.

   Some OSs want each partition to start and end on a cylinder boundary.
   For them, whatever geometry they use should be consistent with the
   partition table.

   Note that before PATCH 03/32, vvfat's partition end was screwed up,
   and nobody noticed.

>> Does this only matter if you declare an image with :floppy: and pass it
>> as a hard disk?  Then we can honestly say we don't care...
>
> As I understand it, the patch has two effect:
>
> 1. the user's hard disk geometry (-drive options cyls, heads, secs) is
> now obeyed

... even when the user suppresses raw by specifying format=vvfat with
file=fat:...  Consistent with how all other block drivers work.

> 2. the default hard disk geometry with :floppy: now matches the one
> without format=vvfat

Yes.

> I don't care about 2. indeed, but doing 1. right would be nice.
Markus Armbruster July 5, 2012, 11:13 a.m. UTC | #5
Kevin Wolf <kwolf@redhat.com> writes:

> Am 29.06.2012 17:34, schrieb Markus Armbruster:
[...]
>> @@ -1067,19 +1074,16 @@ DLOG(if (stderr == NULL) {
>>      else
>>  	dirname += i+1;
>>  
>> -    bs->total_sectors=bs->cyls*bs->heads*bs->secs;
>> +    bs->total_sectors = cyls * heads * secs;
>>  
>> -    if(init_directories(s, dirname))
>> +    if (init_directories(s, dirname, heads, secs)) {
>>  	return -1;
>> +    }
>>  
>>      s->sector_count = s->faked_sectors + s->sectors_per_cluster*s->cluster_count;
>>  
>>      if(s->first_sectors_number==0x40)
>> -	init_mbr(s);
>> -    else {
>> -        /* MS-DOS does not like to know about CHS (?). */
>> -	bs->heads = bs->cyls = bs->secs = 0;
>> -    }
>> +        init_mbr(s, cyls, heads, secs);
>
> You can add braces here while touching the code.

I'm not touching the if...  I'll do it if you insist.
diff mbox

Patch

diff --git a/block/vvfat.c b/block/vvfat.c
index 62745b5..ef89f71 100644
--- a/block/vvfat.c
+++ b/block/vvfat.c
@@ -359,11 +359,12 @@  typedef struct BDRVVVFATState {
  * if the position is outside the specified geometry, fill maximum value for CHS
  * and return 1 to signal overflow.
  */
-static int sector2CHS(BlockDriverState* bs, mbr_chs_t * chs, int spos){
+static int sector2CHS(mbr_chs_t *chs, int spos, int cyls, int heads, int secs)
+{
     int head,sector;
-    sector   = spos % (bs->secs);  spos/= bs->secs;
-    head     = spos % (bs->heads); spos/= bs->heads;
-    if(spos >= bs->cyls){
+    sector   = spos % secs;  spos /= secs;
+    head     = spos % heads; spos /= heads;
+    if (spos >= cyls) {
         /* Overflow,
         it happens if 32bit sector positions are used, while CHS is only 24bit.
         Windows/Dos is said to take 1023/255/63 as nonrepresentable CHS */
@@ -378,7 +379,7 @@  static int sector2CHS(BlockDriverState* bs, mbr_chs_t * chs, int spos){
     return 0;
 }
 
-static void init_mbr(BDRVVVFATState* s)
+static void init_mbr(BDRVVVFATState *s, int cyls, int heads, int secs)
 {
     /* TODO: if the files mbr.img and bootsect.img exist, use them */
     mbr_t* real_mbr=(mbr_t*)s->first_sectors;
@@ -393,8 +394,10 @@  static void init_mbr(BDRVVVFATState* s)
     partition->attributes=0x80; /* bootable */
 
     /* LBA is used when partition is outside the CHS geometry */
-    lba = sector2CHS(s->bs, &partition->start_CHS, s->first_sectors_number-1);
-    lba |= sector2CHS(s->bs, &partition->end_CHS, s->bs->total_sectors - 1);
+    lba  = sector2CHS(&partition->start_CHS, s->first_sectors_number - 1,
+                     cyls, heads, secs);
+    lba |= sector2CHS(&partition->end_CHS,   s->bs->total_sectors - 1,
+                     cyls, heads, secs);
 
     /*LBA partitions are identified only by start/length_sector_long not by CHS*/
     partition->start_sector_long  = cpu_to_le32(s->first_sectors_number-1);
@@ -831,7 +834,7 @@  static inline off_t cluster2sector(BDRVVVFATState* s, uint32_t cluster_num)
 }
 
 static int init_directories(BDRVVVFATState* s,
-	const char* dirname)
+                            const char *dirname, int heads, int secs)
 {
     bootsector_t* bootsector;
     mapping_t* mapping;
@@ -958,8 +961,8 @@  static int init_directories(BDRVVVFATState* s,
     bootsector->media_type=(s->first_sectors_number>1?0xf8:0xf0); /* media descriptor (f8=hd, f0=3.5 fd)*/
     s->fat.pointer[0] = bootsector->media_type;
     bootsector->sectors_per_fat=cpu_to_le16(s->sectors_per_fat);
-    bootsector->sectors_per_track=cpu_to_le16(s->bs->secs);
-    bootsector->number_of_heads=cpu_to_le16(s->bs->heads);
+    bootsector->sectors_per_track = cpu_to_le16(secs);
+    bootsector->number_of_heads = cpu_to_le16(heads);
     bootsector->hidden_sectors=cpu_to_le32(s->first_sectors_number==1?0:0x3f);
     bootsector->total_sectors=cpu_to_le32(s->sector_count>0xffff?s->sector_count:0);
 
@@ -992,7 +995,7 @@  static void vvfat_rebind(BlockDriverState *bs)
 static int vvfat_open(BlockDriverState *bs, const char* dirname, int flags)
 {
     BDRVVVFATState *s = bs->opaque;
-    int i;
+    int i, cyls, heads, secs;
 
 #ifdef DEBUG
     vvv = s;
@@ -1034,24 +1037,28 @@  DLOG(if (stderr == NULL) {
 	/* 1.44MB or 2.88MB floppy.  2.88MB can be FAT12 (default) or FAT16. */
 	if (!s->fat_type) {
 	    s->fat_type = 12;
-	    bs->secs = 36;
+            secs = 36;
 	    s->sectors_per_cluster=2;
 	} else {
-	    bs->secs=(s->fat_type == 12 ? 18 : 36);
+            secs = s->fat_type == 12 ? 18 : 36;
 	    s->sectors_per_cluster=1;
 	}
 	s->first_sectors_number = 1;
-	bs->cyls=80; bs->heads=2;
+        cyls = 80;
+        heads = 2;
     } else {
 	/* 32MB or 504MB disk*/
 	if (!s->fat_type) {
 	    s->fat_type = 16;
 	}
-	bs->cyls=(s->fat_type == 12 ? 64 : 1024);
-	bs->heads=16; bs->secs=63;
+        cyls = s->fat_type == 12 ? 64 : 1024;
+        heads = 16;
+        secs = 63;
     }
+    fprintf(stderr, "vvfat %s chs %d,%d,%d\n",
+            dirname, cyls, heads, secs);
 
-    s->sector_count=bs->cyls*bs->heads*bs->secs-(s->first_sectors_number-1);
+    s->sector_count = cyls * heads * secs - (s->first_sectors_number - 1);
 
     if (strstr(dirname, ":rw:")) {
 	if (enable_write_target(s))
@@ -1067,19 +1074,16 @@  DLOG(if (stderr == NULL) {
     else
 	dirname += i+1;
 
-    bs->total_sectors=bs->cyls*bs->heads*bs->secs;
+    bs->total_sectors = cyls * heads * secs;
 
-    if(init_directories(s, dirname))
+    if (init_directories(s, dirname, heads, secs)) {
 	return -1;
+    }
 
     s->sector_count = s->faked_sectors + s->sectors_per_cluster*s->cluster_count;
 
     if(s->first_sectors_number==0x40)
-	init_mbr(s);
-    else {
-        /* MS-DOS does not like to know about CHS (?). */
-	bs->heads = bs->cyls = bs->secs = 0;
-    }
+        init_mbr(s, cyls, heads, secs);
 
     //    assert(is_consistent(s));
     qemu_co_mutex_init(&s->lock);