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> 2012-06-20 Vladimir Serbinenko <phcoder@gmail.com>
* include/grub/ieee1275/ieee1275.h (grub_ieee1275_flag): New enum value * 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; 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 #ifdef MODE_EXFAT
static grub_err_t static grub_err_t
grub_fat_iterate_dir (grub_disk_t disk, struct grub_fat_data *data, grub_fat_iterate_dir_next (grub_disk_t disk, struct grub_fat_data *data,
int (*hook) (const char *filename, struct grub_fat_iterate_context *ctxt)
grub_fat_dir_node_t *node))
{ {
struct grub_fat_dir_entry dir; grub_memset (&ctxt->dir, 0, sizeof (ctxt->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);
while (1) while (1)
{ {
offset += sizeof (dir); struct grub_fat_dir_entry dir;
if (grub_fat_read_data (disk, data, 0, ctxt->offset += sizeof (dir);
offset, sizeof (dir), (char *) &dir)
if (grub_fat_read_data (disk, data, 0, ctxt->offset, sizeof (dir),
(char *) &dir)
!= sizeof (dir)) != sizeof (dir))
break; break;
@ -607,18 +643,17 @@ grub_fat_iterate_dir (grub_disk_t disk, struct grub_fat_data *data,
if (dir.entry_type == 0x85) if (dir.entry_type == 0x85)
{ {
unsigned i, nsec, slots = 0; unsigned i, nsec, slots = 0;
grub_fat_dir_node_t node;
nsec = dir.type_specific.file.secondary_count; nsec = dir.type_specific.file.secondary_count;
node.attr = grub_cpu_to_le16 (dir.type_specific.file.attr); ctxt->dir.attr = grub_cpu_to_le16 (dir.type_specific.file.attr);
node.have_stream = 0; ctxt->dir.have_stream = 0;
for (i = 0; i < nsec; i++) for (i = 0; i < nsec; i++)
{ {
struct grub_fat_dir_entry sec; struct grub_fat_dir_entry sec;
offset += sizeof (sec); ctxt->offset += sizeof (sec);
if (grub_fat_read_data (disk, data, 0, if (grub_fat_read_data (disk, data, 0,
offset, sizeof (sec), (char *) &sec) ctxt->offset, sizeof (sec), (char *) &sec)
!= sizeof (sec)) != sizeof (sec))
break; break;
if (!(sec.entry_type & 0x80)) 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) switch (sec.entry_type)
{ {
case 0xc0: case 0xc0:
node.first_cluster = grub_cpu_to_le32 (sec.type_specific.stream_extension.first_cluster); ctxt->dir.first_cluster = grub_cpu_to_le32 (sec.type_specific.stream_extension.first_cluster);
node.valid_size ctxt->dir.valid_size
= grub_cpu_to_le64 (sec.type_specific.stream_extension.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); = grub_cpu_to_le64 (sec.type_specific.stream_extension.file_size);
node.have_stream = 1; ctxt->dir.have_stream = 1;
break; break;
case 0xc1: case 0xc1:
{ {
int j; int j;
for (j = 0; j < 15; 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]); = grub_le_to_cpu16 (sec.type_specific.file_name.str[j]);
slots++; slots++;
} }
@ -652,16 +687,14 @@ grub_fat_iterate_dir (grub_disk_t disk, struct grub_fat_data *data,
if (i != nsec) if (i != nsec)
{ {
offset -= sizeof (dir); ctxt->offset -= sizeof (dir);
continue; continue;
} }
*grub_utf16_to_utf8 ((grub_uint8_t *) filename, unibuf, *grub_utf16_to_utf8 ((grub_uint8_t *) ctxt->filename, ctxt->unibuf,
slots * 15) = '\0'; slots * 15) = '\0';
if (hook (filename, &node)) return 0;
break;
continue;
} }
/* Allocation bitmap. */ /* Allocation bitmap. */
if (dir.entry_type == 0x81) if (dir.entry_type == 0x81)
@ -672,60 +705,41 @@ grub_fat_iterate_dir (grub_disk_t disk, struct grub_fat_data *data,
/* Volume label. */ /* Volume label. */
if (dir.entry_type == 0x83) if (dir.entry_type == 0x83)
continue; 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); return grub_errno ? : GRUB_ERR_EOF;
grub_free (unibuf);
return grub_errno;
} }
#else #else
static grub_err_t static grub_err_t
grub_fat_iterate_dir (grub_disk_t disk, struct grub_fat_data *data, grub_fat_iterate_dir_next (grub_disk_t disk, struct grub_fat_data *data,
int (*hook) (const char *filename, struct grub_fat_iterate_context *ctxt)
struct grub_fat_dir_entry *dir))
{ {
struct grub_fat_dir_entry dir;
char *filename;
char *filep = 0; char *filep = 0;
grub_uint16_t *unibuf;
int slot = -1, slots = -1;
int checksum = -1; int checksum = -1;
grub_ssize_t offset = -sizeof(dir); int slot = -1, slots = -1;
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;
}
while (1) while (1)
{ {
unsigned i; unsigned i;
/* Adjust the offset. */ /* Adjust the offset. */
offset += sizeof (dir); ctxt->offset += sizeof (ctxt->dir);
/* Read a directory entry. */ /* Read a directory entry. */
if (grub_fat_read_data (disk, data, 0, if (grub_fat_read_data (disk, data, 0,
offset, sizeof (dir), (char *) &dir) ctxt->offset, sizeof (ctxt->dir),
!= sizeof (dir) || dir.name[0] == 0) (char *) &ctxt->dir)
!= sizeof (ctxt->dir) || ctxt->dir.name[0] == 0)
break; break;
/* Handle long name entries. */ /* 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 *long_name
= (struct grub_fat_long_name_entry *) &dir; = (struct grub_fat_long_name_entry *) &ctxt->dir;
grub_uint8_t id = long_name->id; grub_uint8_t id = long_name->id;
if (id & 0x40) if (id & 0x40)
@ -742,78 +756,72 @@ grub_fat_iterate_dir (grub_disk_t disk, struct grub_fat_data *data,
} }
slot--; slot--;
grub_memcpy (unibuf + slot * 13, long_name->name1, 5 * 2); grub_memcpy (ctxt->unibuf + slot * 13, long_name->name1, 5 * 2);
grub_memcpy (unibuf + slot * 13 + 5, long_name->name2, 6 * 2); grub_memcpy (ctxt->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 + 11, long_name->name3, 2 * 2);
continue; continue;
} }
/* Check if this entry is valid. */ /* 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; continue;
/* This is a workaround for Japanese. */ /* This is a workaround for Japanese. */
if (dir.name[0] == 0x05) if (ctxt->dir.name[0] == 0x05)
dir.name[0] = 0xe5; ctxt->dir.name[0] = 0xe5;
if (checksum != -1 && slot == 0) if (checksum != -1 && slot == 0)
{ {
grub_uint8_t sum; grub_uint8_t sum;
for (sum = 0, i = 0; i < sizeof (dir.name); i++) for (sum = 0, i = 0; i < sizeof (ctxt->dir.name); i++)
sum = ((sum >> 1) | (sum << 7)) + dir.name[i]; sum = ((sum >> 1) | (sum << 7)) + ctxt->dir.name[i];
if (sum == checksum) if (sum == checksum)
{ {
int u; int u;
for (u = 0; u < slots * 13; 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'; slots * 13) = '\0';
if (hook (filename, &dir)) return GRUB_ERR_NONE;
break;
checksum = -1;
continue;
} }
checksum = -1; checksum = -1;
} }
/* Convert the 8.3 file name. */ /* Convert the 8.3 file name. */
filep = filename; filep = ctxt->filename;
if (dir.attr & GRUB_FAT_ATTR_VOLUME_ID) if (ctxt->dir.attr & GRUB_FAT_ATTR_VOLUME_ID)
{ {
for (i = 0; i < sizeof (dir.name) && dir.name[i] for (i = 0; i < sizeof (ctxt->dir.name) && ctxt->dir.name[i]
&& ! grub_isspace (dir.name[i]); i++) && ! grub_isspace (ctxt->dir.name[i]); i++)
*filep++ = dir.name[i]; *filep++ = ctxt->dir.name[i];
} }
else else
{ {
for (i = 0; i < 8 && dir.name[i] && ! grub_isspace (dir.name[i]); i++) for (i = 0; i < 8 && ctxt->dir.name[i] && ! grub_isspace (ctxt->dir.name[i]); i++)
*filep++ = grub_tolower (dir.name[i]); *filep++ = grub_tolower (ctxt->dir.name[i]);
*filep = '.'; *filep = '.';
for (i = 8; i < 11 && dir.name[i] && ! grub_isspace (dir.name[i]); i++) for (i = 8; i < 11 && ctxt->dir.name[i] && ! grub_isspace (ctxt->dir.name[i]); i++)
*++filep = grub_tolower (dir.name[i]); *++filep = grub_tolower (ctxt->dir.name[i]);
if (*filep != '.') if (*filep != '.')
filep++; filep++;
} }
*filep = '\0'; *filep = '\0';
if (hook (filename, &dir)) return GRUB_ERR_NONE;
break;
} }
grub_free (filename); return grub_errno ? : GRUB_ERR_EOF;
grub_free (unibuf);
return grub_errno;
} }
#endif #endif
/* Find the underlying directory or file in PATH and return the /* 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; char *dirname, *dirp;
int call_hook; int call_hook;
int found = 0; int found = 0;
struct grub_fat_iterate_context ctxt;
auto int iter_hook (const char *filename, grub_fat_dir_node_t *dir); grub_err_t err;
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;
}
if (! (data->attr & GRUB_FAT_ATTR_DIRECTORY)) 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); 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) if (grub_errno == GRUB_ERR_NONE && ! found && !call_hook)
grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("file `%s' not found"), origpath); 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; struct grub_fat_data *data;
grub_disk_t disk = device->disk; 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); *label = 0;
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;
}
grub_dl_ref (my_mod); grub_dl_ref (my_mod);
@ -1096,9 +1112,18 @@ grub_fat_label (grub_device_t device, char **label)
return 0; 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: fail:

View file

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