Restructure FAT driver to avoid hook in label reading as it hits a

GCC bug.

	* include/grub/err.h (grub_err_t): New enum value GRUB_ERR_EOF.
	* grub-core/fs/fat.c (grub_fat_iterate_context): New struct.
	(grub_fat_iterate_dir): Split into ...
	(grub_fat_iterate_init): ... this, ...
	(grub_fat_iterate_fini): ... this, ...
	(grub_fat_iterate_dir_next): ... and this. All users updated.
This commit is contained in:
Vladimir 'phcoder' Serbinenko 2012-06-20 23:29:28 +02:00
parent 9f1d654e67
commit 918a25179c
3 changed files with 183 additions and 145 deletions

View file

@ -1,3 +1,15 @@
2012-06-20 Vladimir Serbinenko <phcoder@gmail.com>
Restructure FAT driver to avoid hook in label reading as it hits a
GCC bug.
* include/grub/err.h (grub_err_t): New enum value GRUB_ERR_EOF.
* grub-core/fs/fat.c (grub_fat_iterate_context): New struct.
(grub_fat_iterate_dir): Split into ...
(grub_fat_iterate_init): ... this, ...
(grub_fat_iterate_fini): ... this, ...
(grub_fat_iterate_dir_next): ... and this. All users updated.
2012-06-20 Vladimir Serbinenko <phcoder@gmail.com>
* include/grub/ieee1275/ieee1275.h (grub_ieee1275_flag): New enum value

View file

@ -576,26 +576,62 @@ grub_fat_read_data (grub_disk_t disk, struct grub_fat_data *data,
return ret;
}
struct grub_fat_iterate_context
{
#ifdef MODE_EXFAT
struct grub_fat_dir_node dir;
#else
struct grub_fat_dir_entry dir;
#endif
char *filename;
grub_uint16_t *unibuf;
grub_ssize_t offset;
};
static grub_err_t
grub_fat_iterate_init (struct grub_fat_iterate_context *ctxt)
{
ctxt->offset = -sizeof (struct grub_fat_dir_entry);
#ifndef MODE_EXFAT
/* Allocate space enough to hold a long name. */
ctxt->filename = grub_malloc (0x40 * 13 * GRUB_MAX_UTF8_PER_UTF16 + 1);
ctxt->unibuf = (grub_uint16_t *) grub_malloc (0x40 * 13 * 2);
#else
ctxt->unibuf = grub_malloc (15 * 256 * 2);
ctxt->filename = grub_malloc (15 * 256 * GRUB_MAX_UTF8_PER_UTF16 + 1);
#endif
if (! ctxt->filename || ! ctxt->unibuf)
{
grub_free (ctxt->filename);
grub_free (ctxt->unibuf);
return grub_errno;
}
return GRUB_ERR_NONE;
}
static void
grub_fat_iterate_fini (struct grub_fat_iterate_context *ctxt)
{
grub_free (ctxt->filename);
grub_free (ctxt->unibuf);
}
#ifdef MODE_EXFAT
static grub_err_t
grub_fat_iterate_dir (grub_disk_t disk, struct grub_fat_data *data,
int (*hook) (const char *filename,
grub_fat_dir_node_t *node))
grub_fat_iterate_dir_next (grub_disk_t disk, struct grub_fat_data *data,
struct grub_fat_iterate_context *ctxt)
{
struct grub_fat_dir_entry dir;
grub_ssize_t offset = -sizeof(dir);
grub_uint16_t *unibuf;
char *filename;
unibuf = grub_malloc (15 * 256 * 2);
filename = grub_malloc (15 * 256 * GRUB_MAX_UTF8_PER_UTF16 + 1);
grub_memset (&ctxt->dir, 0, sizeof (ctxt->dir));
while (1)
{
offset += sizeof (dir);
struct grub_fat_dir_entry dir;
if (grub_fat_read_data (disk, data, 0,
offset, sizeof (dir), (char *) &dir)
ctxt->offset += sizeof (dir);
if (grub_fat_read_data (disk, data, 0, ctxt->offset, sizeof (dir),
(char *) &dir)
!= sizeof (dir))
break;
@ -607,18 +643,17 @@ grub_fat_iterate_dir (grub_disk_t disk, struct grub_fat_data *data,
if (dir.entry_type == 0x85)
{
unsigned i, nsec, slots = 0;
grub_fat_dir_node_t node;
nsec = dir.type_specific.file.secondary_count;
node.attr = grub_cpu_to_le16 (dir.type_specific.file.attr);
node.have_stream = 0;
ctxt->dir.attr = grub_cpu_to_le16 (dir.type_specific.file.attr);
ctxt->dir.have_stream = 0;
for (i = 0; i < nsec; i++)
{
struct grub_fat_dir_entry sec;
offset += sizeof (sec);
ctxt->offset += sizeof (sec);
if (grub_fat_read_data (disk, data, 0,
offset, sizeof (sec), (char *) &sec)
ctxt->offset, sizeof (sec), (char *) &sec)
!= sizeof (sec))
break;
if (!(sec.entry_type & 0x80))
@ -628,18 +663,18 @@ grub_fat_iterate_dir (grub_disk_t disk, struct grub_fat_data *data,
switch (sec.entry_type)
{
case 0xc0:
node.first_cluster = grub_cpu_to_le32 (sec.type_specific.stream_extension.first_cluster);
node.valid_size
ctxt->dir.first_cluster = grub_cpu_to_le32 (sec.type_specific.stream_extension.first_cluster);
ctxt->dir.valid_size
= grub_cpu_to_le64 (sec.type_specific.stream_extension.valid_size);
node.file_size
ctxt->dir.file_size
= grub_cpu_to_le64 (sec.type_specific.stream_extension.file_size);
node.have_stream = 1;
ctxt->dir.have_stream = 1;
break;
case 0xc1:
{
int j;
for (j = 0; j < 15; j++)
unibuf[slots * 15 + j]
ctxt->unibuf[slots * 15 + j]
= grub_le_to_cpu16 (sec.type_specific.file_name.str[j]);
slots++;
}
@ -652,16 +687,14 @@ grub_fat_iterate_dir (grub_disk_t disk, struct grub_fat_data *data,
if (i != nsec)
{
offset -= sizeof (dir);
ctxt->offset -= sizeof (dir);
continue;
}
*grub_utf16_to_utf8 ((grub_uint8_t *) filename, unibuf,
*grub_utf16_to_utf8 ((grub_uint8_t *) ctxt->filename, ctxt->unibuf,
slots * 15) = '\0';
if (hook (filename, &node))
break;
continue;
return 0;
}
/* Allocation bitmap. */
if (dir.entry_type == 0x81)
@ -672,60 +705,41 @@ grub_fat_iterate_dir (grub_disk_t disk, struct grub_fat_data *data,
/* Volume label. */
if (dir.entry_type == 0x83)
continue;
grub_dprintf ("exfat", "unknown primary type 0x%02x\n", dir.entry_type);
grub_dprintf ("exfat", "unknown primary type 0x%02x\n",
dir.entry_type);
}
grub_free (filename);
grub_free (unibuf);
return grub_errno;
return grub_errno ? : GRUB_ERR_EOF;
}
#else
static grub_err_t
grub_fat_iterate_dir (grub_disk_t disk, struct grub_fat_data *data,
int (*hook) (const char *filename,
struct grub_fat_dir_entry *dir))
grub_fat_iterate_dir_next (grub_disk_t disk, struct grub_fat_data *data,
struct grub_fat_iterate_context *ctxt)
{
struct grub_fat_dir_entry dir;
char *filename;
char *filep = 0;
grub_uint16_t *unibuf;
int slot = -1, slots = -1;
int checksum = -1;
grub_ssize_t offset = -sizeof(dir);
if (! (data->attr & GRUB_FAT_ATTR_DIRECTORY))
return grub_error (GRUB_ERR_BAD_FILE_TYPE, N_("not a directory"));
/* Allocate space enough to hold a long name. */
filename = grub_malloc (0x40 * 13 * GRUB_MAX_UTF8_PER_UTF16 + 1);
unibuf = (grub_uint16_t *) grub_malloc (0x40 * 13 * 2);
if (! filename || ! unibuf)
{
grub_free (filename);
grub_free (unibuf);
return 0;
}
int slot = -1, slots = -1;
while (1)
{
unsigned i;
/* Adjust the offset. */
offset += sizeof (dir);
ctxt->offset += sizeof (ctxt->dir);
/* Read a directory entry. */
if (grub_fat_read_data (disk, data, 0,
offset, sizeof (dir), (char *) &dir)
!= sizeof (dir) || dir.name[0] == 0)
ctxt->offset, sizeof (ctxt->dir),
(char *) &ctxt->dir)
!= sizeof (ctxt->dir) || ctxt->dir.name[0] == 0)
break;
/* Handle long name entries. */
if (dir.attr == GRUB_FAT_ATTR_LONG_NAME)
if (ctxt->dir.attr == GRUB_FAT_ATTR_LONG_NAME)
{
struct grub_fat_long_name_entry *long_name
= (struct grub_fat_long_name_entry *) &dir;
= (struct grub_fat_long_name_entry *) &ctxt->dir;
grub_uint8_t id = long_name->id;
if (id & 0x40)
@ -742,78 +756,72 @@ grub_fat_iterate_dir (grub_disk_t disk, struct grub_fat_data *data,
}
slot--;
grub_memcpy (unibuf + slot * 13, long_name->name1, 5 * 2);
grub_memcpy (unibuf + slot * 13 + 5, long_name->name2, 6 * 2);
grub_memcpy (unibuf + slot * 13 + 11, long_name->name3, 2 * 2);
grub_memcpy (ctxt->unibuf + slot * 13, long_name->name1, 5 * 2);
grub_memcpy (ctxt->unibuf + slot * 13 + 5, long_name->name2, 6 * 2);
grub_memcpy (ctxt->unibuf + slot * 13 + 11, long_name->name3, 2 * 2);
continue;
}
/* Check if this entry is valid. */
if (dir.name[0] == 0xe5 || (dir.attr & ~GRUB_FAT_ATTR_VALID))
if (ctxt->dir.name[0] == 0xe5 || (ctxt->dir.attr & ~GRUB_FAT_ATTR_VALID))
continue;
/* This is a workaround for Japanese. */
if (dir.name[0] == 0x05)
dir.name[0] = 0xe5;
if (ctxt->dir.name[0] == 0x05)
ctxt->dir.name[0] = 0xe5;
if (checksum != -1 && slot == 0)
{
grub_uint8_t sum;
for (sum = 0, i = 0; i < sizeof (dir.name); i++)
sum = ((sum >> 1) | (sum << 7)) + dir.name[i];
for (sum = 0, i = 0; i < sizeof (ctxt->dir.name); i++)
sum = ((sum >> 1) | (sum << 7)) + ctxt->dir.name[i];
if (sum == checksum)
{
int u;
for (u = 0; u < slots * 13; u++)
unibuf[u] = grub_le_to_cpu16 (unibuf[u]);
ctxt->unibuf[u] = grub_le_to_cpu16 (ctxt->unibuf[u]);
*grub_utf16_to_utf8 ((grub_uint8_t *) filename, unibuf,
*grub_utf16_to_utf8 ((grub_uint8_t *) ctxt->filename,
ctxt->unibuf,
slots * 13) = '\0';
if (hook (filename, &dir))
break;
checksum = -1;
continue;
return GRUB_ERR_NONE;
}
checksum = -1;
}
/* Convert the 8.3 file name. */
filep = filename;
if (dir.attr & GRUB_FAT_ATTR_VOLUME_ID)
filep = ctxt->filename;
if (ctxt->dir.attr & GRUB_FAT_ATTR_VOLUME_ID)
{
for (i = 0; i < sizeof (dir.name) && dir.name[i]
&& ! grub_isspace (dir.name[i]); i++)
*filep++ = dir.name[i];
for (i = 0; i < sizeof (ctxt->dir.name) && ctxt->dir.name[i]
&& ! grub_isspace (ctxt->dir.name[i]); i++)
*filep++ = ctxt->dir.name[i];
}
else
{
for (i = 0; i < 8 && dir.name[i] && ! grub_isspace (dir.name[i]); i++)
*filep++ = grub_tolower (dir.name[i]);
for (i = 0; i < 8 && ctxt->dir.name[i] && ! grub_isspace (ctxt->dir.name[i]); i++)
*filep++ = grub_tolower (ctxt->dir.name[i]);
*filep = '.';
for (i = 8; i < 11 && dir.name[i] && ! grub_isspace (dir.name[i]); i++)
*++filep = grub_tolower (dir.name[i]);
for (i = 8; i < 11 && ctxt->dir.name[i] && ! grub_isspace (ctxt->dir.name[i]); i++)
*++filep = grub_tolower (ctxt->dir.name[i]);
if (*filep != '.')
filep++;
}
*filep = '\0';
if (hook (filename, &dir))
break;
return GRUB_ERR_NONE;
}
grub_free (filename);
grub_free (unibuf);
return grub_errno;
return grub_errno ? : GRUB_ERR_EOF;
}
#endif
/* Find the underlying directory or file in PATH and return the
@ -828,47 +836,8 @@ grub_fat_find_dir (grub_disk_t disk, struct grub_fat_data *data,
char *dirname, *dirp;
int call_hook;
int found = 0;
auto int iter_hook (const char *filename, grub_fat_dir_node_t *dir);
int iter_hook (const char *filename, grub_fat_dir_node_t *dir)
{
struct grub_dirhook_info info;
grub_memset (&info, 0, sizeof (info));
info.dir = !! (dir->attr & GRUB_FAT_ATTR_DIRECTORY);
info.case_insensitive = 1;
#ifdef MODE_EXFAT
if (!dir->have_stream)
return 0;
#else
if (dir->attr & GRUB_FAT_ATTR_VOLUME_ID)
return 0;
#endif
if (*dirname == '\0' && call_hook)
return hook (filename, &info);
if (grub_strcasecmp (dirname, filename) == 0)
{
found = 1;
data->attr = dir->attr;
#ifdef MODE_EXFAT
data->file_size = dir->file_size;
data->file_cluster = dir->first_cluster;
#else
data->file_size = grub_le_to_cpu32 (dir->file_size);
data->file_cluster = ((grub_le_to_cpu16 (dir->first_cluster_high) << 16)
| grub_le_to_cpu16 (dir->first_cluster_low));
#endif
data->cur_cluster_num = ~0U;
if (call_hook)
hook (filename, &info);
return 1;
}
return 0;
}
struct grub_fat_iterate_context ctxt;
grub_err_t err;
if (! (data->attr & GRUB_FAT_ATTR_DIRECTORY))
{
@ -898,7 +867,61 @@ grub_fat_find_dir (grub_disk_t disk, struct grub_fat_data *data,
call_hook = (! dirp && hook);
grub_fat_iterate_dir (disk, data, iter_hook);
err = grub_fat_iterate_init (&ctxt);
if (err)
{
grub_free (dirname);
return 0;
}
while (!(err = grub_fat_iterate_dir_next (disk, data, &ctxt)))
{
struct grub_dirhook_info info;
grub_memset (&info, 0, sizeof (info));
info.dir = !! (ctxt.dir.attr & GRUB_FAT_ATTR_DIRECTORY);
info.case_insensitive = 1;
#ifdef MODE_EXFAT
if (!ctxt.dir.have_stream)
continue;
#else
if (ctxt.dir.attr & GRUB_FAT_ATTR_VOLUME_ID)
continue;
#endif
if (*dirname == '\0' && call_hook)
{
if (hook (ctxt.filename, &info))
break;
else
continue;
}
if (grub_strcasecmp (dirname, ctxt.filename) == 0)
{
found = 1;
data->attr = ctxt.dir.attr;
#ifdef MODE_EXFAT
data->file_size = ctxt.dir.file_size;
data->file_cluster = ctxt.dir.first_cluster;
#else
data->file_size = grub_le_to_cpu32 (ctxt.dir.file_size);
data->file_cluster = ((grub_le_to_cpu16 (ctxt.dir.first_cluster_high) << 16)
| grub_le_to_cpu16 (ctxt.dir.first_cluster_low));
#endif
data->cur_cluster_num = ~0U;
if (call_hook)
hook (ctxt.filename, &info);
break;
}
}
grub_fat_iterate_fini (&ctxt);
if (err == GRUB_ERR_EOF)
err = 0;
if (grub_errno == GRUB_ERR_NONE && ! found && !call_hook)
grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("file `%s' not found"), origpath);
@ -1072,17 +1095,10 @@ grub_fat_label (grub_device_t device, char **label)
{
struct grub_fat_data *data;
grub_disk_t disk = device->disk;
grub_err_t err;
struct grub_fat_iterate_context ctxt;
auto int iter_hook (const char *filename, grub_fat_dir_node_t *dir);
int iter_hook (const char *filename, grub_fat_dir_node_t *dir)
{
if (dir->attr == GRUB_FAT_ATTR_VOLUME_ID)
{
*label = grub_strdup (filename);
return 1;
}
return 0;
}
*label = 0;
grub_dl_ref (my_mod);
@ -1096,9 +1112,18 @@ grub_fat_label (grub_device_t device, char **label)
return 0;
}
*label = 0;
err = grub_fat_iterate_init (&ctxt);
if (err)
goto fail;
grub_fat_iterate_dir (disk, data, iter_hook);
while (!(err = grub_fat_iterate_dir_next (disk, data, &ctxt)))
if (ctxt.dir.attr == GRUB_FAT_ATTR_VOLUME_ID)
{
*label = grub_strdup (ctxt.filename);
break;
}
grub_fat_iterate_fini (&ctxt);
fail:

View file

@ -68,7 +68,8 @@ typedef enum
GRUB_ERR_NET_INVALID_RESPONSE,
GRUB_ERR_NET_UNKNOWN_ERROR,
GRUB_ERR_NET_PACKET_TOO_BIG,
GRUB_ERR_NET_NO_DOMAIN
GRUB_ERR_NET_NO_DOMAIN,
GRUB_ERR_EOF
}
grub_err_t;