Rewrite blocklist functions in order to get progress when

reading large extents and decrease amount of blocklist hook calls.
This commit is contained in:
Vladimir Serbinenko 2013-11-01 23:28:03 +01:00
parent 896f913571
commit cb72aa1809
20 changed files with 220 additions and 181 deletions

View file

@ -1,3 +1,8 @@
2013-11-01 Vladimir Serbinenko <phcoder@gmail.com>
Rewrite blocklist functions in order to get progress when
reading large extents and decrease amount of blocklist hook calls.
2013-11-01 Vladimir Serbinenko <phcoder@gmail.com> 2013-11-01 Vladimir Serbinenko <phcoder@gmail.com>
* grub-core/term/serial.c (options), (grub_cmd_serial): Fix handling * grub-core/term/serial.c (options), (grub_cmd_serial): Fix handling

View file

@ -62,23 +62,47 @@ read_blocklist (grub_disk_addr_t sector, unsigned offset, unsigned length,
if (ctx->num_sectors > 0) if (ctx->num_sectors > 0)
{ {
if (ctx->start_sector + ctx->num_sectors == sector if (ctx->start_sector + ctx->num_sectors == sector
&& offset == 0 && length == GRUB_DISK_SECTOR_SIZE) && offset == 0 && length >= GRUB_DISK_SECTOR_SIZE)
{ {
ctx->num_sectors++; ctx->num_sectors += length >> GRUB_DISK_SECTOR_BITS;
return; sector += length >> GRUB_DISK_SECTOR_BITS;
length &= (GRUB_DISK_SECTOR_SIZE - 1);
} }
if (!length)
return;
print_blocklist (ctx->start_sector, ctx->num_sectors, 0, 0, ctx); print_blocklist (ctx->start_sector, ctx->num_sectors, 0, 0, ctx);
ctx->num_sectors = 0; ctx->num_sectors = 0;
} }
if (offset == 0 && length == GRUB_DISK_SECTOR_SIZE) if (offset)
{ {
ctx->start_sector = sector; unsigned l = length + offset;
ctx->num_sectors++; l &= (GRUB_DISK_SECTOR_SIZE - 1);
l -= offset;
print_blocklist (sector, 0, offset, l, ctx);
length -= l;
sector++;
offset = 0;
}
if (!length)
return;
if (length & (GRUB_DISK_SECTOR_SIZE - 1))
{
if (length >> GRUB_DISK_SECTOR_BITS)
{
print_blocklist (sector, length >> GRUB_DISK_SECTOR_BITS, 0, 0, ctx);
sector += length >> GRUB_DISK_SECTOR_BITS;
}
print_blocklist (sector, 0, 0, length & (GRUB_DISK_SECTOR_SIZE - 1), ctx);
} }
else else
print_blocklist (sector, 0, offset, length, ctx); {
ctx->start_sector = sector;
ctx->num_sectors = length >> GRUB_DISK_SECTOR_BITS;
}
} }
static grub_err_t static grub_err_t

View file

@ -259,10 +259,28 @@ check_blocklists (grub_envblk_t envblk, struct blocklist *blocklists,
for (p = blocklists; p; p = p->next) for (p = blocklists; p; p = p->next)
{ {
struct blocklist *q; struct blocklist *q;
/* Check if any pair of blocks overlap. */
for (q = p->next; q; q = q->next) for (q = p->next; q; q = q->next)
{ {
/* Check if any pair of blocks overlap. */ grub_disk_addr_t s1, s2;
if (p->sector == q->sector) grub_disk_addr_t e1, e2, t;
s1 = p->sector;
e1 = s1 + ((p->length + GRUB_DISK_SECTOR_SIZE - 1) >> GRUB_DISK_SECTOR_BITS);
s2 = q->sector;
e2 = s2 + ((q->length + GRUB_DISK_SECTOR_SIZE - 1) >> GRUB_DISK_SECTOR_BITS);
if (s2 > s1)
{
t = s2;
s2 = s1;
s1 = t;
t = e2;
e2 = e1;
e1 = t;
}
if (e1 > s2)
{ {
/* This might be actually valid, but it is unbelievable that /* This might be actually valid, but it is unbelievable that
any filesystem makes such a silly allocation. */ any filesystem makes such a silly allocation. */
@ -286,9 +304,18 @@ check_blocklists (grub_envblk_t envblk, struct blocklist *blocklists,
part_start = grub_partition_get_start (disk->partition); part_start = grub_partition_get_start (disk->partition);
buf = grub_envblk_buffer (envblk); buf = grub_envblk_buffer (envblk);
char *blockbuf = NULL;
grub_size_t blockbuf_len = 0;
for (p = blocklists, index = 0; p; index += p->length, p = p->next) for (p = blocklists, index = 0; p; index += p->length, p = p->next)
{ {
char blockbuf[GRUB_DISK_SECTOR_SIZE]; if (p->length > blockbuf_len)
{
grub_free (blockbuf);
blockbuf_len = 2 * p->length;
blockbuf = grub_malloc (blockbuf_len);
if (!blockbuf)
return grub_errno;
}
if (grub_disk_read (disk, p->sector - part_start, if (grub_disk_read (disk, p->sector - part_start,
p->offset, p->length, blockbuf)) p->offset, p->length, blockbuf))
@ -340,10 +367,6 @@ save_env_read_hook (grub_disk_addr_t sector, unsigned offset, unsigned length,
struct grub_cmd_save_env_ctx *ctx = data; struct grub_cmd_save_env_ctx *ctx = data;
struct blocklist *block; struct blocklist *block;
if (offset + length > GRUB_DISK_SECTOR_SIZE)
/* Seemingly a bug. */
return;
block = grub_malloc (sizeof (*block)); block = grub_malloc (sizeof (*block));
if (! block) if (! block)
return; return;

View file

@ -35,10 +35,13 @@ GRUB_MOD_LICENSE ("GPLv3+");
static void static void
read_progress (grub_disk_addr_t sector __attribute__ ((unused)), read_progress (grub_disk_addr_t sector __attribute__ ((unused)),
unsigned offset __attribute__ ((unused)), unsigned offset __attribute__ ((unused)),
unsigned len __attribute__ ((unused)), unsigned len,
void *data __attribute__ ((unused))) void *data __attribute__ ((unused)))
{ {
grub_xputs ("."); for (; len >= GRUB_DISK_SECTOR_SIZE; len -= GRUB_DISK_SECTOR_SIZE)
grub_xputs (".");
if (len)
grub_xputs (".");
grub_refresh (); grub_refresh ();
} }

View file

@ -285,7 +285,6 @@ grub_ata_readwrite (grub_disk_t disk, grub_disk_addr_t sector,
if (addressing == GRUB_ATA_LBA48 && ((sector + size) >> 28) != 0) if (addressing == GRUB_ATA_LBA48 && ((sector + size) >> 28) != 0)
{ {
batch = 65536;
if (ata->dma) if (ata->dma)
{ {
cmd = GRUB_ATA_CMD_READ_SECTORS_DMA_EXT; cmd = GRUB_ATA_CMD_READ_SECTORS_DMA_EXT;
@ -301,10 +300,6 @@ grub_ata_readwrite (grub_disk_t disk, grub_disk_addr_t sector,
{ {
if (addressing == GRUB_ATA_LBA48) if (addressing == GRUB_ATA_LBA48)
addressing = GRUB_ATA_LBA; addressing = GRUB_ATA_LBA;
if (addressing != GRUB_ATA_CHS)
batch = 256;
else
batch = 1;
if (ata->dma) if (ata->dma)
{ {
cmd = GRUB_ATA_CMD_READ_SECTORS_DMA; cmd = GRUB_ATA_CMD_READ_SECTORS_DMA;
@ -317,8 +312,10 @@ grub_ata_readwrite (grub_disk_t disk, grub_disk_addr_t sector,
} }
} }
if (batch > (ata->maxbuffer >> ata->log_sector_size)) if (addressing != GRUB_ATA_CHS)
batch = (ata->maxbuffer >> ata->log_sector_size); batch = 256;
else
batch = 1;
while (nsectors < size) while (nsectors < size)
{ {
@ -464,6 +461,10 @@ grub_ata_open (const char *name, grub_disk_t disk)
return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "not an ATA harddisk"); return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "not an ATA harddisk");
disk->total_sectors = ata->size; disk->total_sectors = ata->size;
disk->max_agglomerate = (ata->maxbuffer >> (GRUB_DISK_CACHE_BITS + GRUB_DISK_SECTOR_BITS));
if (disk->max_agglomerate > (256U >> (GRUB_DISK_CACHE_BITS + GRUB_DISK_SECTOR_BITS - ata->log_sector_size)))
disk->max_agglomerate = (256U >> (GRUB_DISK_CACHE_BITS + GRUB_DISK_SECTOR_BITS - ata->log_sector_size));
disk->log_sector_size = ata->log_sector_size; disk->log_sector_size = ata->log_sector_size;
disk->id = grub_make_scsi_id (id, bus, 0); disk->id = grub_make_scsi_id (id, bus, 0);

View file

@ -517,6 +517,7 @@ grub_cryptodisk_open (const char *name, grub_disk_t disk)
disk->data = dev; disk->data = dev;
disk->total_sectors = dev->total_length; disk->total_sectors = dev->total_length;
disk->max_agglomerate = GRUB_DISK_MAX_MAX_AGGLOMERATE;
disk->id = dev->id; disk->id = dev->id;
dev->ref++; dev->ref++;
return GRUB_ERR_NONE; return GRUB_ERR_NONE;

View file

@ -445,6 +445,7 @@ grub_diskfilter_open (const char *name, grub_disk_t disk)
disk->data = lv; disk->data = lv;
disk->total_sectors = lv->size; disk->total_sectors = lv->size;
disk->max_agglomerate = GRUB_DISK_MAX_MAX_AGGLOMERATE;
return 0; return 0;
} }

View file

@ -518,6 +518,8 @@ grub_efidisk_open (const char *name, struct grub_disk *disk)
grub_dprintf ("efidisk", "m = %p, last block = %llx, block size = %x\n", grub_dprintf ("efidisk", "m = %p, last block = %llx, block size = %x\n",
m, (unsigned long long) m->last_block, m->block_size); m, (unsigned long long) m->last_block, m->block_size);
disk->total_sectors = m->last_block + 1; disk->total_sectors = m->last_block + 1;
/* Don't increase this value due to bug in some EFI. */
disk->max_agglomerate = 0xa0000 >> (GRUB_DISK_CACHE_BITS + GRUB_DISK_SECTOR_BITS);
if (m->block_size & (m->block_size - 1) || !m->block_size) if (m->block_size & (m->block_size - 1) || !m->block_size)
return grub_error (GRUB_ERR_IO, "invalid sector size %d", return grub_error (GRUB_ERR_IO, "invalid sector size %d",
m->block_size); m->block_size);
@ -550,24 +552,11 @@ grub_efidisk_readwrite (struct grub_disk *disk, grub_disk_addr_t sector,
d = disk->data; d = disk->data;
bio = d->block_io; bio = d->block_io;
while (size > 0) return efi_call_5 ((wr ? bio->write_blocks : bio->read_blocks), bio,
{ bio->media->media_id,
grub_size_t len; (grub_efi_uint64_t) sector,
len = 0x500; (grub_efi_uintn_t) size << disk->log_sector_size,
if (len > size) buf);
len = size;
status = efi_call_5 ((wr ? bio->write_blocks : bio->read_blocks), bio,
bio->media->media_id,
(grub_efi_uint64_t) sector,
(grub_efi_uintn_t) len << disk->log_sector_size,
buf);
size -= len;
buf += len << disk->log_sector_size;
sector += len;
if (status != GRUB_EFI_SUCCESS)
return status;
}
return GRUB_EFI_SUCCESS;
} }
static grub_err_t static grub_err_t

View file

@ -424,6 +424,9 @@ grub_biosdisk_open (const char *name, grub_disk_t disk)
} }
disk->total_sectors = total_sectors; disk->total_sectors = total_sectors;
/* Limit the max to 0x7f because of Phoenix EDD. */
disk->max_agglomerate = 0x7f >> GRUB_DISK_CACHE_BITS;
disk->data = data; disk->data = data;
return GRUB_ERR_NONE; return GRUB_ERR_NONE;
@ -548,10 +551,6 @@ get_safe_sectors (grub_disk_t disk, grub_disk_addr_t sector)
size = sectors - offset; size = sectors - offset;
/* Limit the max to 0x7f because of Phoenix EDD. */
if (size > ((0x7fU << GRUB_DISK_SECTOR_BITS) >> disk->log_sector_size))
size = ((0x7fU << GRUB_DISK_SECTOR_BITS) >> disk->log_sector_size);
return size; return size;
} }

View file

@ -167,6 +167,10 @@ grub_loopback_open (const char *name, grub_disk_t disk)
/ GRUB_DISK_SECTOR_SIZE); / GRUB_DISK_SECTOR_SIZE);
else else
disk->total_sectors = GRUB_DISK_SIZE_UNKNOWN; disk->total_sectors = GRUB_DISK_SIZE_UNKNOWN;
/* Avoid reading more than 512M. */
disk->max_agglomerate = 1 << (29 - GRUB_DISK_SECTOR_BITS
- GRUB_DISK_CACHE_BITS);
disk->id = (unsigned long) dev; disk->id = (unsigned long) dev;
disk->data = dev; disk->data = dev;

View file

@ -46,6 +46,7 @@ grub_memdisk_open (const char *name, grub_disk_t disk)
return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "not a memdisk"); return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "not a memdisk");
disk->total_sectors = memdisk_size / GRUB_DISK_SECTOR_SIZE; disk->total_sectors = memdisk_size / GRUB_DISK_SECTOR_SIZE;
disk->max_agglomerate = GRUB_DISK_MAX_MAX_AGGLOMERATE;
disk->id = (unsigned long) "mdsk"; disk->id = (unsigned long) "mdsk";
return GRUB_ERR_NONE; return GRUB_ERR_NONE;

View file

@ -606,6 +606,13 @@ grub_scsi_open (const char *name, grub_disk_t disk)
} }
disk->total_sectors = scsi->last_block + 1; disk->total_sectors = scsi->last_block + 1;
/* PATA doesn't support more than 32K reads.
Not sure about AHCI and USB. If it's confirmed that either of
them can do bigger reads reliably this value can be moved to 'scsi'
structure. */
disk->max_agglomerate = 32768 >> (GRUB_DISK_SECTOR_BITS
+ GRUB_DISK_CACHE_BITS);
if (scsi->blocksize & (scsi->blocksize - 1) || !scsi->blocksize) if (scsi->blocksize & (scsi->blocksize - 1) || !scsi->blocksize)
{ {
grub_free (scsi); grub_free (scsi);
@ -647,40 +654,27 @@ grub_scsi_read (grub_disk_t disk, grub_disk_addr_t sector,
scsi = disk->data; scsi = disk->data;
while (size) grub_err_t err;
/* Depending on the type, select a read function. */
switch (scsi->devtype)
{ {
/* PATA doesn't support more than 32K reads. case grub_scsi_devtype_direct:
Not sure about AHCI and USB. If it's confirmed that either of if (sector >> 32)
them can do bigger reads reliably this value can be moved to 'scsi' err = grub_scsi_read16 (disk, sector, size, buf);
structure. */ else
grub_size_t len = 32768 >> disk->log_sector_size; err = grub_scsi_read10 (disk, sector, size, buf);
grub_err_t err; if (err)
if (len > size) return err;
len = size; break;
/* Depending on the type, select a read function. */
switch (scsi->devtype)
{
case grub_scsi_devtype_direct:
if (sector >> 32)
err = grub_scsi_read16 (disk, sector, len, buf);
else
err = grub_scsi_read10 (disk, sector, len, buf);
if (err)
return err;
break;
case grub_scsi_devtype_cdrom: case grub_scsi_devtype_cdrom:
if (sector >> 32) if (sector >> 32)
err = grub_scsi_read16 (disk, sector, len, buf); err = grub_scsi_read16 (disk, sector, size, buf);
else else
err = grub_scsi_read12 (disk, sector, len, buf); err = grub_scsi_read12 (disk, sector, size, buf);
if (err) if (err)
return err; return err;
break; break;
}
size -= len;
sector += len;
buf += len << disk->log_sector_size;
} }
return GRUB_ERR_NONE; return GRUB_ERR_NONE;
@ -718,10 +712,10 @@ grub_scsi_read (grub_disk_t disk, grub_disk_addr_t sector,
} }
static grub_err_t static grub_err_t
grub_scsi_write (grub_disk_t disk __attribute((unused)), grub_scsi_write (grub_disk_t disk,
grub_disk_addr_t sector __attribute((unused)), grub_disk_addr_t sector,
grub_size_t size __attribute((unused)), grub_size_t size,
const char *buf __attribute((unused))) const char *buf)
{ {
grub_scsi_t scsi; grub_scsi_t scsi;
@ -730,31 +724,18 @@ grub_scsi_write (grub_disk_t disk __attribute((unused)),
if (scsi->devtype == grub_scsi_devtype_cdrom) if (scsi->devtype == grub_scsi_devtype_cdrom)
return grub_error (GRUB_ERR_IO, N_("cannot write to CD-ROM")); return grub_error (GRUB_ERR_IO, N_("cannot write to CD-ROM"));
while (size) grub_err_t err;
/* Depending on the type, select a read function. */
switch (scsi->devtype)
{ {
/* PATA doesn't support more than 32K reads. case grub_scsi_devtype_direct:
Not sure about AHCI and USB. If it's confirmed that either of if (sector >> 32)
them can do bigger reads reliably this value can be moved to 'scsi' err = grub_scsi_write16 (disk, sector, size, buf);
structure. */ else
grub_size_t len = 32768 >> disk->log_sector_size; err = grub_scsi_write10 (disk, sector, size, buf);
grub_err_t err; if (err)
if (len > size) return err;
len = size; break;
/* Depending on the type, select a read function. */
switch (scsi->devtype)
{
case grub_scsi_devtype_direct:
if (sector >> 32)
err = grub_scsi_write16 (disk, sector, len, buf);
else
err = grub_scsi_write10 (disk, sector, len, buf);
if (err)
return err;
break;
}
size -= len;
sector += len;
buf += len << disk->log_sector_size;
} }
return GRUB_ERR_NONE; return GRUB_ERR_NONE;

View file

@ -289,6 +289,7 @@ grub_cbfsdisk_open (const char *name, grub_disk_t disk)
return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "not a cbfsdisk"); return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "not a cbfsdisk");
disk->total_sectors = cbfsdisk_size / GRUB_DISK_SECTOR_SIZE; disk->total_sectors = cbfsdisk_size / GRUB_DISK_SECTOR_SIZE;
disk->max_agglomerate = GRUB_DISK_MAX_MAX_AGGLOMERATE;
disk->id = (unsigned long) "cbfs"; disk->id = (unsigned long) "cbfs";
return GRUB_ERR_NONE; return GRUB_ERR_NONE;

View file

@ -199,6 +199,9 @@ grub_disk_open (const char *name)
if (! disk) if (! disk)
return 0; return 0;
disk->log_sector_size = GRUB_DISK_SECTOR_BITS; disk->log_sector_size = GRUB_DISK_SECTOR_BITS;
/* Default 1MiB of maximum agglomerate. */
disk->max_agglomerate = 1048576 >> (GRUB_DISK_SECTOR_BITS
+ GRUB_DISK_CACHE_BITS);
p = find_part_sep (name); p = find_part_sep (name);
if (p) if (p)
@ -311,8 +314,8 @@ grub_disk_close (grub_disk_t disk)
sector is already adjusted and is divisible by cache unit size. sector is already adjusted and is divisible by cache unit size.
*/ */
static grub_err_t static grub_err_t
grub_disk_read_small (grub_disk_t disk, grub_disk_addr_t sector, grub_disk_read_small_real (grub_disk_t disk, grub_disk_addr_t sector,
grub_off_t offset, grub_size_t size, void *buf) grub_off_t offset, grub_size_t size, void *buf)
{ {
char *data; char *data;
char *tmp_buf; char *tmp_buf;
@ -389,15 +392,27 @@ grub_disk_read_small (grub_disk_t disk, grub_disk_addr_t sector,
} }
} }
static grub_err_t
grub_disk_read_small (grub_disk_t disk, grub_disk_addr_t sector,
grub_off_t offset, grub_size_t size, void *buf)
{
grub_err_t err;
err = grub_disk_read_small_real (disk, sector, offset, size, buf);
if (err)
return err;
if (disk->read_hook)
(disk->read_hook) (sector + (offset >> GRUB_DISK_SECTOR_BITS),
offset & (GRUB_DISK_SECTOR_SIZE - 1),
size, disk->read_hook_data);
return GRUB_ERR_NONE;
}
/* Read data from the disk. */ /* Read data from the disk. */
grub_err_t grub_err_t
grub_disk_read (grub_disk_t disk, grub_disk_addr_t sector, grub_disk_read (grub_disk_t disk, grub_disk_addr_t sector,
grub_off_t offset, grub_size_t size, void *buf) grub_off_t offset, grub_size_t size, void *buf)
{ {
grub_off_t real_offset;
grub_disk_addr_t real_sector;
grub_size_t real_size;
/* First of all, check if the region is within the disk. */ /* First of all, check if the region is within the disk. */
if (grub_disk_adjust_range (disk, &sector, &offset, size) != GRUB_ERR_NONE) if (grub_disk_adjust_range (disk, &sector, &offset, size) != GRUB_ERR_NONE)
{ {
@ -408,10 +423,6 @@ grub_disk_read (grub_disk_t disk, grub_disk_addr_t sector,
return grub_errno; return grub_errno;
} }
real_sector = sector;
real_offset = offset;
real_size = size;
/* First read until first cache boundary. */ /* First read until first cache boundary. */
if (offset || (sector & (GRUB_DISK_CACHE_SIZE - 1))) if (offset || (sector & (GRUB_DISK_CACHE_SIZE - 1)))
{ {
@ -446,7 +457,8 @@ grub_disk_read (grub_disk_t disk, grub_disk_addr_t sector,
/* agglomerate read until we find a first cached entry. */ /* agglomerate read until we find a first cached entry. */
for (agglomerate = 0; agglomerate for (agglomerate = 0; agglomerate
< (size >> (GRUB_DISK_SECTOR_BITS + GRUB_DISK_CACHE_BITS)); < (size >> (GRUB_DISK_SECTOR_BITS + GRUB_DISK_CACHE_BITS))
&& agglomerate < disk->max_agglomerate;
agglomerate++) agglomerate++)
{ {
data = grub_disk_cache_fetch (disk->dev->id, disk->id, data = grub_disk_cache_fetch (disk->dev->id, disk->id,
@ -486,6 +498,11 @@ grub_disk_read (grub_disk_t disk, grub_disk_addr_t sector,
+ (i << (GRUB_DISK_CACHE_BITS + (i << (GRUB_DISK_CACHE_BITS
+ GRUB_DISK_SECTOR_BITS))); + GRUB_DISK_SECTOR_BITS)));
if (disk->read_hook)
(disk->read_hook) (sector, 0, agglomerate << (GRUB_DISK_CACHE_BITS + GRUB_DISK_SECTOR_BITS),
disk->read_hook_data);
sector += agglomerate << GRUB_DISK_CACHE_BITS; sector += agglomerate << GRUB_DISK_CACHE_BITS;
size -= agglomerate << (GRUB_DISK_CACHE_BITS + GRUB_DISK_SECTOR_BITS); size -= agglomerate << (GRUB_DISK_CACHE_BITS + GRUB_DISK_SECTOR_BITS);
buf = (char *) buf buf = (char *) buf
@ -494,6 +511,9 @@ grub_disk_read (grub_disk_t disk, grub_disk_addr_t sector,
if (data) if (data)
{ {
if (disk->read_hook)
(disk->read_hook) (sector, 0, (GRUB_DISK_CACHE_SIZE << GRUB_DISK_SECTOR_BITS),
disk->read_hook_data);
sector += GRUB_DISK_CACHE_SIZE; sector += GRUB_DISK_CACHE_SIZE;
buf = (char *) buf + (GRUB_DISK_CACHE_SIZE << GRUB_DISK_SECTOR_BITS); buf = (char *) buf + (GRUB_DISK_CACHE_SIZE << GRUB_DISK_SECTOR_BITS);
size -= (GRUB_DISK_CACHE_SIZE << GRUB_DISK_SECTOR_BITS); size -= (GRUB_DISK_CACHE_SIZE << GRUB_DISK_SECTOR_BITS);
@ -509,26 +529,6 @@ grub_disk_read (grub_disk_t disk, grub_disk_addr_t sector,
return err; return err;
} }
/* Call the read hook, if any. */
if (disk->read_hook)
{
grub_disk_addr_t s = real_sector;
grub_size_t l = real_size;
grub_off_t o = real_offset;
while (l)
{
grub_size_t cl;
cl = GRUB_DISK_SECTOR_SIZE - o;
if (cl > l)
cl = l;
(disk->read_hook) (s, o, cl, disk->read_hook_data);
s++;
l -= cl;
o = 0;
}
}
return grub_errno; return grub_errno;
} }

View file

@ -155,6 +155,7 @@ grub_util_biosdisk_open (const char *name, grub_disk_t disk)
disk->total_sectors = grub_util_get_fd_size (fd, map[drive].device, disk->total_sectors = grub_util_get_fd_size (fd, map[drive].device,
&disk->log_sector_size); &disk->log_sector_size);
disk->total_sectors >>= disk->log_sector_size; disk->total_sectors >>= disk->log_sector_size;
disk->max_agglomerate = GRUB_DISK_MAX_MAX_AGGLOMERATE;
#if GRUB_UTIL_FD_STAT_IS_FUNCTIONAL #if GRUB_UTIL_FD_STAT_IS_FUNCTIONAL
{ {

View file

@ -123,6 +123,13 @@ grub_disk_write (grub_disk_t disk, grub_disk_addr_t sector,
len = size & ~((1 << disk->log_sector_size) - 1); len = size & ~((1 << disk->log_sector_size) - 1);
n = size >> disk->log_sector_size; n = size >> disk->log_sector_size;
if (n > (disk->max_agglomerate
<< (GRUB_DISK_CACHE_BITS + GRUB_DISK_SECTOR_BITS
- disk->log_sector_size)))
n = (disk->max_agglomerate
<< (GRUB_DISK_CACHE_BITS + GRUB_DISK_SECTOR_BITS
- disk->log_sector_size));
if ((disk->dev->write) (disk, transform_sector (disk, sector), if ((disk->dev->write) (disk, transform_sector (disk, sector),
n, buf) != GRUB_ERR_NONE) n, buf) != GRUB_ERR_NONE)
goto finish; goto finish;

View file

@ -70,7 +70,7 @@ grub_install_get_blocklist (grub_device_t root_dev,
if (ioctl (fd, FS_IOC_FIEMAP, &fie1) < 0) if (ioctl (fd, FS_IOC_FIEMAP, &fie1) < 0)
{ {
int nblocks, i, j; int nblocks, i;
int bsize; int bsize;
int mul; int mul;
@ -88,27 +88,25 @@ grub_install_get_blocklist (grub_device_t root_dev,
for (i = 0; i < nblocks; i++) for (i = 0; i < nblocks; i++)
{ {
unsigned blk = i; unsigned blk = i;
int rest;
if (ioctl (fd, FIBMAP, &blk) < 0) if (ioctl (fd, FIBMAP, &blk) < 0)
grub_util_error (_("can't retrieve blocklists: %s"), grub_util_error (_("can't retrieve blocklists: %s"),
strerror (errno)); strerror (errno));
for (j = 0; j < mul; j++) rest = core_size - ((i * mul) << GRUB_DISK_SECTOR_BITS);
{ if (rest <= 0)
int rest = core_size - ((i * mul + j) << GRUB_DISK_SECTOR_BITS); break;
if (rest <= 0) if (rest > GRUB_DISK_SECTOR_SIZE * mul)
break; rest = GRUB_DISK_SECTOR_SIZE * mul;
if (rest > GRUB_DISK_SECTOR_SIZE) callback (((grub_uint64_t) blk) * mul
rest = GRUB_DISK_SECTOR_SIZE; + container_start,
callback (((grub_uint64_t) blk) * mul + j 0, rest, hook_data);
+ container_start,
0, rest, hook_data);
}
} }
} }
else else
{ {
struct fiemap *fie2; struct fiemap *fie2;
int i, j; int i;
fie2 = xmalloc (sizeof (*fie2) fie2 = xmalloc (sizeof (*fie2)
+ fie1.fm_mapped_extents + fie1.fm_mapped_extents
* sizeof (fie1.fm_extents[1])); * sizeof (fie1.fm_extents[1]));
@ -122,22 +120,12 @@ grub_install_get_blocklist (grub_device_t root_dev,
strerror (errno)); strerror (errno));
for (i = 0; i < fie2->fm_mapped_extents; i++) for (i = 0; i < fie2->fm_mapped_extents; i++)
{ {
for (j = 0; callback ((fie2->fm_extents[i].fe_physical
j < ((fie2->fm_extents[i].fe_length >> GRUB_DISK_SECTOR_BITS)
+ GRUB_DISK_SECTOR_SIZE - 1) + container_start,
>> GRUB_DISK_SECTOR_BITS); fie2->fm_extents[i].fe_physical
j++) & (GRUB_DISK_SECTOR_SIZE - 1),
{ fie2->fm_extents[i].fe_length, hook_data);
size_t len = (fie2->fm_extents[i].fe_length
- j * GRUB_DISK_SECTOR_SIZE);
if (len > GRUB_DISK_SECTOR_SIZE)
len = GRUB_DISK_SECTOR_SIZE;
callback ((fie2->fm_extents[i].fe_physical
>> GRUB_DISK_SECTOR_BITS)
+ j + container_start,
fie2->fm_extents[i].fe_physical
& (GRUB_DISK_SECTOR_SIZE - 1), len, hook_data);
}
} }
} }
close (fd); close (fd);

View file

@ -49,7 +49,7 @@ grub_install_get_blocklist (grub_device_t root_dev,
DWORD rets; DWORD rets;
RETRIEVAL_POINTERS_BUFFER *extbuf; RETRIEVAL_POINTERS_BUFFER *extbuf;
size_t extbuf_size; size_t extbuf_size;
DWORD i, j, k; DWORD i;
grub_uint64_t sec_per_lcn; grub_uint64_t sec_per_lcn;
grub_uint64_t curvcn = 0; grub_uint64_t curvcn = 0;
STARTING_VCN_INPUT_BUFFER start_vcn; STARTING_VCN_INPUT_BUFFER start_vcn;
@ -108,12 +108,8 @@ grub_install_get_blocklist (grub_device_t root_dev,
CloseHandle (filehd); CloseHandle (filehd);
for (i = 0; i < extbuf->ExtentCount; i++) for (i = 0; i < extbuf->ExtentCount; i++)
{ callback (extbuf->Extents[i].Lcn.QuadPart
for (j = 0; j < extbuf->Extents[i].NextVcn.QuadPart - curvcn; j++) * sec_per_lcn + first_lcn,
for (k = 0; k < sec_per_lcn; k++) 0, 512 * sec_per_lcn * (extbuf->Extents[i].NextVcn.QuadPart - curvcn), hook_data);
callback ((extbuf->Extents[i].Lcn.QuadPart + j)
* sec_per_lcn + first_lcn
+ k, 0, 512, hook_data);
}
free (extbuf); free (extbuf);
} }

View file

@ -125,6 +125,9 @@ struct grub_disk
/* Logarithm of sector size. */ /* Logarithm of sector size. */
unsigned int log_sector_size; unsigned int log_sector_size;
/* Maximum number of sectors read divided by GRUB_DISK_CACHE_SIZE. */
unsigned int max_agglomerate;
/* The id used by the disk cache manager. */ /* The id used by the disk cache manager. */
unsigned long id; unsigned long id;
@ -164,6 +167,8 @@ typedef struct grub_disk_memberlist *grub_disk_memberlist_t;
#define GRUB_DISK_CACHE_BITS 6 #define GRUB_DISK_CACHE_BITS 6
#define GRUB_DISK_CACHE_SIZE (1 << GRUB_DISK_CACHE_BITS) #define GRUB_DISK_CACHE_SIZE (1 << GRUB_DISK_CACHE_BITS)
#define GRUB_DISK_MAX_MAX_AGGLOMERATE ((1 << (30 - GRUB_DISK_CACHE_BITS - GRUB_DISK_SECTOR_BITS)) - 1)
/* Return value of grub_disk_get_size() in case disk size is unknown. */ /* Return value of grub_disk_get_size() in case disk size is unknown. */
#define GRUB_DISK_SIZE_UNKNOWN 0xffffffffffffffffULL #define GRUB_DISK_SIZE_UNKNOWN 0xffffffffffffffffULL

View file

@ -149,33 +149,40 @@ save_blocklists (grub_disk_addr_t sector, unsigned offset, unsigned length,
{ {
struct blocklists *bl = data; struct blocklists *bl = data;
struct grub_boot_blocklist *prev = bl->block + 1; struct grub_boot_blocklist *prev = bl->block + 1;
grub_uint64_t seclen;
grub_util_info ("saving <%" PRIuGRUB_UINT64_T ",%u,%u>", grub_util_info ("saving <%" PRIuGRUB_UINT64_T ",%u,%u>",
sector, offset, length); sector, offset, length);
if (bl->first_sector == (grub_disk_addr_t) -1) if (bl->first_sector == (grub_disk_addr_t) -1)
{ {
if (offset != 0 || length != GRUB_DISK_SECTOR_SIZE) if (offset != 0 || length < GRUB_DISK_SECTOR_SIZE)
grub_util_error ("%s", _("the first sector of the core file is not sector-aligned")); grub_util_error ("%s", _("the first sector of the core file is not sector-aligned"));
bl->first_sector = sector; bl->first_sector = sector;
return; sector++;
length -= GRUB_DISK_SECTOR_SIZE;
if (!length)
return;
} }
if (offset != 0 || bl->last_length != GRUB_DISK_SECTOR_SIZE) if (offset != 0 || bl->last_length != 0)
grub_util_error ("%s", _("non-sector-aligned data is found in the core file")); grub_util_error ("%s", _("non-sector-aligned data is found in the core file"));
seclen = (length + GRUB_DISK_SECTOR_SIZE - 1) >> GRUB_DISK_SECTOR_BITS;
if (bl->block != bl->first_block if (bl->block != bl->first_block
&& (grub_target_to_host64 (prev->start) && (grub_target_to_host64 (prev->start)
+ grub_target_to_host16 (prev->len)) == sector) + grub_target_to_host16 (prev->len)) == sector)
{ {
grub_uint16_t t = grub_target_to_host16 (prev->len) + 1; grub_uint16_t t = grub_target_to_host16 (prev->len);
t += seclen;
prev->len = grub_host_to_target16 (t); prev->len = grub_host_to_target16 (t);
} }
else else
{ {
bl->block->start = grub_host_to_target64 (sector); bl->block->start = grub_host_to_target64 (sector);
bl->block->len = grub_host_to_target16 (1); bl->block->len = grub_host_to_target16 (seclen);
#ifdef GRUB_SETUP_BIOS #ifdef GRUB_SETUP_BIOS
bl->block->segment = grub_host_to_target16 (bl->current_segment); bl->block->segment = grub_host_to_target16 (bl->current_segment);
#endif #endif
@ -185,9 +192,9 @@ save_blocklists (grub_disk_addr_t sector, unsigned offset, unsigned length,
grub_util_error ("%s", _("the sectors of the core file are too fragmented")); grub_util_error ("%s", _("the sectors of the core file are too fragmented"));
} }
bl->last_length = length; bl->last_length = length & (GRUB_DISK_SECTOR_SIZE - 1);
#ifdef GRUB_SETUP_BIOS #ifdef GRUB_SETUP_BIOS
bl->current_segment += GRUB_DISK_SECTOR_SIZE >> 4; bl->current_segment += seclen << (GRUB_DISK_SECTOR_BITS - 4);
#endif #endif
} }
@ -260,7 +267,7 @@ SETUP (const char *dir,
bl.current_segment = bl.current_segment =
GRUB_BOOT_I386_PC_KERNEL_SEG + (GRUB_DISK_SECTOR_SIZE >> 4); GRUB_BOOT_I386_PC_KERNEL_SEG + (GRUB_DISK_SECTOR_SIZE >> 4);
#endif #endif
bl.last_length = GRUB_DISK_SECTOR_SIZE; bl.last_length = 0;
/* Read the boot image by the OS service. */ /* Read the boot image by the OS service. */
boot_path = grub_util_get_path (dir, boot_file); boot_path = grub_util_get_path (dir, boot_file);
@ -731,6 +738,8 @@ unable_to_embed:
if ((char *) bl.block <= core_img) if ((char *) bl.block <= core_img)
grub_util_error ("%s", _("no terminator in the core image")); grub_util_error ("%s", _("no terminator in the core image"));
} }
if (len)
grub_util_error ("%s", _("blocklists are incomplete"));
core_dev->disk->partition = container; core_dev->disk->partition = container;
free (buf); free (buf);
} }