BFS: Convert to fshelp.

BFS doesn't handle ".." correctly, so convert it to fshelp to reuse the logic.
This commit is contained in:
Vladimir Serbinenko 2015-07-27 12:48:38 +02:00
parent fa93b0e4f5
commit d1d3a60b71

View file

@ -29,6 +29,7 @@
#include <grub/dl.h>
#include <grub/types.h>
#include <grub/i18n.h>
#include <grub/fshelp.h>
GRUB_MOD_LICENSE ("GPLv3+");
@ -561,10 +562,10 @@ iterate_in_b_tree (grub_disk_t disk,
}
static int
bfs_strcmp (const char *a, const char *b, grub_size_t alen, grub_size_t blen)
bfs_strcmp (const char *a, const char *b, grub_size_t alen)
{
char ac, bc;
while (blen && alen)
while (*b && alen)
{
if (*a != *b)
break;
@ -572,11 +573,10 @@ bfs_strcmp (const char *a, const char *b, grub_size_t alen, grub_size_t blen)
a++;
b++;
alen--;
blen--;
}
ac = alen ? *a : 0;
bc = blen ? *b : 0;
bc = *b;
#ifdef MODE_AFS
return (int) (grub_int8_t) ac - (int) (grub_int8_t) bc;
@ -589,7 +589,6 @@ static grub_err_t
find_in_b_tree (grub_disk_t disk,
const struct grub_bfs_superblock *sb,
const struct grub_bfs_inode *ino, const char *name,
grub_size_t name_len,
grub_uint64_t * res)
{
struct grub_bfs_btree_header head;
@ -637,7 +636,7 @@ find_in_b_tree (grub_disk_t disk,
end = grub_bfs_to_cpu16 (keylen_idx[(i | (1 << j))]);
if (grub_bfs_to_cpu_treehead (node->total_key_len) <= end)
end = grub_bfs_to_cpu_treehead (node->total_key_len);
cmp = bfs_strcmp (key_data + start, name, end - start, name_len);
cmp = bfs_strcmp (key_data + start, name, end - start);
if (cmp == 0 && level == 0)
{
*res = grub_bfs_to_cpu64 (key_values[i | (1 << j)].val);
@ -658,7 +657,7 @@ find_in_b_tree (grub_disk_t disk,
end = grub_bfs_to_cpu16 (keylen_idx[0]);
if (grub_bfs_to_cpu_treehead (node->total_key_len) <= end)
end = grub_bfs_to_cpu_treehead (node->total_key_len);
cmp = bfs_strcmp (key_data, name, end, name_len);
cmp = bfs_strcmp (key_data, name, end);
if (cmp == 0 && level == 0)
{
*res = grub_bfs_to_cpu64 (key_values[0].val);
@ -707,127 +706,119 @@ find_in_b_tree (grub_disk_t disk,
}
}
struct grub_fshelp_node
{
grub_disk_t disk;
const struct grub_bfs_superblock *sb;
struct grub_bfs_inode ino;
};
static grub_err_t
hop_level (grub_disk_t disk,
const struct grub_bfs_superblock *sb,
struct grub_bfs_inode *ino, const char *name,
const char *name_end)
lookup_file (grub_fshelp_node_t dir,
const char *name,
grub_fshelp_node_t *foundnode,
enum grub_fshelp_filetype *foundtype)
{
grub_err_t err;
struct grub_bfs_inode *new_ino;
grub_uint64_t res = 0;
if (((grub_bfs_to_cpu32 (ino->mode) & ATTR_TYPE) != ATTR_DIR))
return grub_error (GRUB_ERR_BAD_FILE_TYPE, N_("not a directory"));
err = find_in_b_tree (disk, sb, ino, name, name_end - name, &res);
err = find_in_b_tree (dir->disk, dir->sb, &dir->ino, name, &res);
if (err)
return err;
return grub_disk_read (disk, res
<< (grub_bfs_to_cpu32 (sb->log2_bsize)
- GRUB_DISK_SECTOR_BITS), 0,
sizeof (*ino), (char *) ino);
*foundnode = grub_malloc (sizeof (struct grub_fshelp_node));
if (!*foundnode)
return grub_errno;
(*foundnode)->disk = dir->disk;
(*foundnode)->sb = dir->sb;
new_ino = &(*foundnode)->ino;
if (grub_disk_read (dir->disk, res
<< (grub_bfs_to_cpu32 (dir->sb->log2_bsize)
- GRUB_DISK_SECTOR_BITS), 0,
sizeof (*new_ino), (char *) new_ino))
{
grub_free (*foundnode);
return grub_errno;
}
switch (grub_bfs_to_cpu32 (new_ino->mode) & ATTR_TYPE)
{
default:
case ATTR_REG:
*foundtype = GRUB_FSHELP_REG;
break;
case ATTR_DIR:
*foundtype = GRUB_FSHELP_DIR;
break;
case ATTR_LNK:
*foundtype = GRUB_FSHELP_SYMLINK;
break;
}
return GRUB_ERR_NONE;
}
static char *
read_symlink (grub_fshelp_node_t node)
{
char *alloc = NULL;
grub_err_t err;
#ifndef MODE_AFS
if (!(grub_bfs_to_cpu32 (node->ino.flags) & LONG_SYMLINK))
{
alloc = grub_malloc (sizeof (node->ino.inplace_link) + 1);
if (!alloc)
{
return NULL;
}
grub_memcpy (alloc, node->ino.inplace_link,
sizeof (node->ino.inplace_link));
alloc[sizeof (node->ino.inplace_link)] = 0;
}
else
#endif
{
grub_size_t symsize = grub_bfs_to_cpu64 (node->ino.size);
alloc = grub_malloc (symsize + 1);
if (!alloc)
return NULL;
err = read_bfs_file (node->disk, node->sb, &node->ino, 0, alloc, symsize, 0, 0);
if (err)
{
grub_free (alloc);
return NULL;
}
alloc[symsize] = 0;
}
return alloc;
}
static grub_err_t
find_file (const char *path, grub_disk_t disk,
const struct grub_bfs_superblock *sb, struct grub_bfs_inode *ino)
const struct grub_bfs_superblock *sb, struct grub_bfs_inode *ino,
enum grub_fshelp_filetype exptype)
{
const char *ptr, *next = path;
char *alloc = NULL;
char *wptr;
grub_err_t err;
struct grub_bfs_inode old_ino;
unsigned symlinks_max = 32;
struct grub_fshelp_node root = {
.disk = disk,
.sb = sb,
};
struct grub_fshelp_node *found;
err = read_extent (disk, sb, &sb->root_dir, 0, 0, ino,
sizeof (*ino));
err = read_extent (disk, sb, &sb->root_dir, 0, 0, &root.ino,
sizeof (root.ino));
if (err)
return err;
err = grub_fshelp_find_file_lookup (path, &root, &found, lookup_file, read_symlink, exptype);
if (!err)
grub_memcpy (ino, &found->ino, sizeof (*ino));
while (1)
{
ptr = next;
while (*ptr == '/')
ptr++;
if (*ptr == 0)
{
grub_free (alloc);
return GRUB_ERR_NONE;
}
for (next = ptr; *next && *next != '/'; next++);
grub_memcpy (&old_ino, ino, sizeof (old_ino));
err = hop_level (disk, sb, ino, ptr, next);
if (err)
return err;
if (((grub_bfs_to_cpu32 (ino->mode) & ATTR_TYPE) == ATTR_LNK))
{
char *old_alloc = alloc;
if (--symlinks_max == 0)
{
grub_free (alloc);
return grub_error (GRUB_ERR_SYMLINK_LOOP,
N_("too deep nesting of symlinks"));
}
#ifndef MODE_AFS
if (grub_bfs_to_cpu32 (ino->flags) & LONG_SYMLINK)
#endif
{
grub_size_t symsize = grub_bfs_to_cpu64 (ino->size);
alloc = grub_malloc (grub_strlen (next)
+ symsize + 1);
if (!alloc)
{
grub_free (alloc);
return grub_errno;
}
grub_free (old_alloc);
err = read_bfs_file (disk, sb, ino, 0, alloc, symsize, 0, 0);
if (err)
{
grub_free (alloc);
return err;
}
alloc[symsize] = 0;
}
#ifndef MODE_AFS
else
{
alloc = grub_malloc (grub_strlen (next)
+ sizeof (ino->inplace_link) + 1);
if (!alloc)
{
grub_free (alloc);
return grub_errno;
}
grub_free (old_alloc);
grub_memcpy (alloc, ino->inplace_link,
sizeof (ino->inplace_link));
alloc[sizeof (ino->inplace_link)] = 0;
}
#endif
if (alloc[0] == '/')
{
err = read_extent (disk, sb, &sb->root_dir, 0, 0, ino,
sizeof (*ino));
if (err)
{
grub_free (alloc);
return err;
}
}
else
grub_memcpy (ino, &old_ino, sizeof (old_ino));
wptr = alloc + grub_strlen (alloc);
if (next)
wptr = grub_stpcpy (wptr, next);
*wptr = 0;
next = alloc;
continue;
}
}
if (&root != found)
grub_free (found);
return err;
}
static grub_err_t
@ -909,11 +900,9 @@ grub_bfs_dir (grub_device_t device, const char *path,
{
struct grub_bfs_inode ino;
err = find_file (path, device->disk, &ctx.sb, &ino);
err = find_file (path, device->disk, &ctx.sb, &ino, GRUB_FSHELP_DIR);
if (err)
return err;
if (((grub_bfs_to_cpu32 (ino.mode) & ATTR_TYPE) != ATTR_DIR))
return grub_error (GRUB_ERR_BAD_FILE_TYPE, N_("not a directory"));
iterate_in_b_tree (device->disk, &ctx.sb, &ino, grub_bfs_dir_iter,
&ctx);
}
@ -934,11 +923,9 @@ grub_bfs_open (struct grub_file *file, const char *name)
{
struct grub_bfs_inode ino;
struct grub_bfs_data *data;
err = find_file (name, file->device->disk, &sb, &ino);
err = find_file (name, file->device->disk, &sb, &ino, GRUB_FSHELP_REG);
if (err)
return err;
if (((grub_bfs_to_cpu32 (ino.mode) & ATTR_TYPE) != ATTR_REG))
return grub_error (GRUB_ERR_BAD_FILE_TYPE, N_("not a regular file"));
data = grub_zalloc (sizeof (struct grub_bfs_data));
if (!data)
@ -1034,7 +1021,7 @@ read_bfs_attr (grub_disk_t disk,
if (err)
return -1;
err = find_in_b_tree (disk, sb, ino, name, grub_strlen (name), &res);
err = find_in_b_tree (disk, sb, ino, name, &res);
if (err)
return -1;
grub_disk_read (disk, res