4f4128defd
Disable the -Wadress-of-packaed-member diagnostic for the grub_jfs_getent function since the result is found to be false postive. The leaf is read into memory as continous chunks in size of 32 bytes and the pointer to its base is aligned, which also guarentee its member leaf->namepart is aligned. [ 60s] ../grub-core/fs/jfs.c: In function 'grub_jfs_getent': [ 60s] ../grub-core/fs/jfs.c:557:44: error: taking address of packed member of 'struct grub_jfs_leaf_dirent' may result in an unaligned pointer value [-Werror=address-of-packed-member] [ 60s] 557 | le_to_cpu16_copy (filename + strpos, leaf->namepart, len < diro->data->namecomponentlen ? len [ 60s] | ~~~~^~~~~~~~~~ [ 60s] ../grub-core/fs/jfs.c:570:48: error: taking address of packed member of 'struct grub_jfs_leaf_next_dirent' may result in an unaligned pointer value [-Werror=address-of-packed-member] [ 60s] 570 | le_to_cpu16_copy (filename + strpos, next_leaf->namepart, len < 15 ? len : 15); [ 60s] | ~~~~~~~~~^~~~~~~~~~ [ 60s] cc1: all warnings being treated as errors Signed-off-by: Michael Chang <mchang@suse.com> Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
962 lines
23 KiB
C
962 lines
23 KiB
C
/* jfs.c - JFS. */
|
||
/*
|
||
* GRUB -- GRand Unified Bootloader
|
||
* Copyright (C) 2004,2005,2006,2007,2008,2009 Free Software Foundation, Inc.
|
||
*
|
||
* GRUB is free software: you can redistribute it and/or modify
|
||
* it under the terms of the GNU General Public License as published by
|
||
* the Free Software Foundation, either version 3 of the License, or
|
||
* (at your option) any later version.
|
||
*
|
||
* GRUB is distributed in the hope that it will be useful,
|
||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
* GNU General Public License for more details.
|
||
*
|
||
* You should have received a copy of the GNU General Public License
|
||
* along with GRUB. If not, see <http://www.gnu.org/licenses/>.
|
||
*/
|
||
|
||
#include <grub/err.h>
|
||
#include <grub/file.h>
|
||
#include <grub/mm.h>
|
||
#include <grub/misc.h>
|
||
#include <grub/disk.h>
|
||
#include <grub/dl.h>
|
||
#include <grub/types.h>
|
||
#include <grub/charset.h>
|
||
#include <grub/i18n.h>
|
||
|
||
GRUB_MOD_LICENSE ("GPLv3+");
|
||
|
||
#define GRUB_JFS_MAX_SYMLNK_CNT 8
|
||
#define GRUB_JFS_FILETYPE_MASK 0170000
|
||
#define GRUB_JFS_FILETYPE_REG 0100000
|
||
#define GRUB_JFS_FILETYPE_LNK 0120000
|
||
#define GRUB_JFS_FILETYPE_DIR 0040000
|
||
|
||
#define GRUB_JFS_SBLOCK 64
|
||
#define GRUB_JFS_AGGR_INODE 2
|
||
#define GRUB_JFS_FS1_INODE_BLK 104
|
||
|
||
#define GRUB_JFS_TREE_LEAF 2
|
||
|
||
struct grub_jfs_sblock
|
||
{
|
||
/* The magic for JFS. It should contain the string "JFS1". */
|
||
grub_uint8_t magic[4];
|
||
grub_uint32_t version;
|
||
grub_uint64_t ag_size;
|
||
|
||
/* The size of a filesystem block in bytes. XXX: currently only
|
||
4096 was tested. */
|
||
grub_uint32_t blksz;
|
||
grub_uint16_t log2_blksz;
|
||
grub_uint8_t unused[14];
|
||
grub_uint32_t flags;
|
||
grub_uint8_t unused3[61];
|
||
char volname[11];
|
||
grub_uint8_t unused2[24];
|
||
grub_uint8_t uuid[16];
|
||
char volname2[16];
|
||
};
|
||
|
||
struct grub_jfs_extent
|
||
{
|
||
/* The length of the extent in filesystem blocks. */
|
||
grub_uint16_t length;
|
||
grub_uint8_t length2;
|
||
|
||
/* The physical offset of the first block on the disk. */
|
||
grub_uint8_t blk1;
|
||
grub_uint32_t blk2;
|
||
} GRUB_PACKED;
|
||
|
||
#define GRUB_JFS_IAG_INODES_OFFSET 3072
|
||
#define GRUB_JFS_IAG_INODES_COUNT 128
|
||
|
||
struct grub_jfs_iag
|
||
{
|
||
grub_uint8_t unused[GRUB_JFS_IAG_INODES_OFFSET];
|
||
struct grub_jfs_extent inodes[GRUB_JFS_IAG_INODES_COUNT];
|
||
} GRUB_PACKED;
|
||
|
||
|
||
/* The head of the tree used to find extents. */
|
||
struct grub_jfs_treehead
|
||
{
|
||
grub_uint64_t next;
|
||
grub_uint64_t prev;
|
||
|
||
grub_uint8_t flags;
|
||
grub_uint8_t unused;
|
||
|
||
grub_uint16_t count;
|
||
grub_uint16_t max;
|
||
grub_uint8_t unused2[10];
|
||
} GRUB_PACKED;
|
||
|
||
/* A node in the extent tree. */
|
||
struct grub_jfs_tree_extent
|
||
{
|
||
grub_uint8_t flags;
|
||
grub_uint16_t unused;
|
||
|
||
/* The offset is the key used to lookup an extent. */
|
||
grub_uint8_t offset1;
|
||
grub_uint32_t offset2;
|
||
|
||
struct grub_jfs_extent extent;
|
||
} GRUB_PACKED;
|
||
|
||
/* The tree of directory entries. */
|
||
struct grub_jfs_tree_dir
|
||
{
|
||
/* Pointers to the previous and next tree headers of other nodes on
|
||
this level. */
|
||
grub_uint64_t nextb;
|
||
grub_uint64_t prevb;
|
||
|
||
grub_uint8_t flags;
|
||
|
||
/* The amount of dirents in this node. */
|
||
grub_uint8_t count;
|
||
grub_uint8_t freecnt;
|
||
grub_uint8_t freelist;
|
||
grub_uint8_t maxslot;
|
||
|
||
/* The location of the sorted array of pointers to dirents. */
|
||
grub_uint8_t sindex;
|
||
grub_uint8_t unused[10];
|
||
} GRUB_PACKED;
|
||
|
||
/* An internal node in the dirents tree. */
|
||
struct grub_jfs_internal_dirent
|
||
{
|
||
struct grub_jfs_extent ex;
|
||
grub_uint8_t next;
|
||
grub_uint8_t len;
|
||
grub_uint16_t namepart[11];
|
||
} GRUB_PACKED;
|
||
|
||
/* A leaf node in the dirents tree. */
|
||
struct grub_jfs_leaf_dirent
|
||
{
|
||
/* The inode for this dirent. */
|
||
grub_uint32_t inode;
|
||
grub_uint8_t next;
|
||
|
||
/* The size of the name. */
|
||
grub_uint8_t len;
|
||
grub_uint16_t namepart[11];
|
||
grub_uint32_t index;
|
||
} GRUB_PACKED;
|
||
|
||
/* A leaf in the dirents tree. This one is used if the previously
|
||
dirent was not big enough to store the name. */
|
||
struct grub_jfs_leaf_next_dirent
|
||
{
|
||
grub_uint8_t next;
|
||
grub_uint8_t len;
|
||
grub_uint16_t namepart[15];
|
||
} GRUB_PACKED;
|
||
|
||
struct grub_jfs_time
|
||
{
|
||
grub_int32_t sec;
|
||
grub_int32_t nanosec;
|
||
} GRUB_PACKED;
|
||
|
||
struct grub_jfs_inode
|
||
{
|
||
grub_uint32_t stamp;
|
||
grub_uint32_t fileset;
|
||
grub_uint32_t inode;
|
||
grub_uint8_t unused[12];
|
||
grub_uint64_t size;
|
||
grub_uint8_t unused2[20];
|
||
grub_uint32_t mode;
|
||
struct grub_jfs_time atime;
|
||
struct grub_jfs_time ctime;
|
||
struct grub_jfs_time mtime;
|
||
grub_uint8_t unused3[48];
|
||
grub_uint8_t unused4[96];
|
||
|
||
union
|
||
{
|
||
/* The tree describing the extents of the file. */
|
||
struct GRUB_PACKED
|
||
{
|
||
struct grub_jfs_treehead tree;
|
||
struct grub_jfs_tree_extent extents[16];
|
||
} file;
|
||
union
|
||
{
|
||
/* The tree describing the dirents. */
|
||
struct
|
||
{
|
||
grub_uint8_t unused[16];
|
||
grub_uint8_t flags;
|
||
|
||
/* Amount of dirents in this node. */
|
||
grub_uint8_t count;
|
||
grub_uint8_t freecnt;
|
||
grub_uint8_t freelist;
|
||
grub_uint32_t idotdot;
|
||
grub_uint8_t sorted[8];
|
||
} header;
|
||
struct grub_jfs_leaf_dirent dirents[8];
|
||
} GRUB_PACKED dir;
|
||
/* Fast symlink. */
|
||
struct
|
||
{
|
||
grub_uint8_t unused[32];
|
||
grub_uint8_t path[256];
|
||
} symlink;
|
||
} GRUB_PACKED;
|
||
} GRUB_PACKED;
|
||
|
||
struct grub_jfs_data
|
||
{
|
||
struct grub_jfs_sblock sblock;
|
||
grub_disk_t disk;
|
||
struct grub_jfs_inode fileset;
|
||
struct grub_jfs_inode currinode;
|
||
int caseins;
|
||
int pos;
|
||
int linknest;
|
||
int namecomponentlen;
|
||
} GRUB_PACKED;
|
||
|
||
struct grub_jfs_diropen
|
||
{
|
||
int index;
|
||
union
|
||
{
|
||
struct grub_jfs_tree_dir header;
|
||
struct grub_jfs_leaf_dirent dirent[0];
|
||
struct grub_jfs_leaf_next_dirent next_dirent[0];
|
||
grub_uint8_t sorted[0];
|
||
} GRUB_PACKED *dirpage;
|
||
struct grub_jfs_data *data;
|
||
struct grub_jfs_inode *inode;
|
||
int count;
|
||
grub_uint8_t *sorted;
|
||
struct grub_jfs_leaf_dirent *leaf;
|
||
struct grub_jfs_leaf_next_dirent *next_leaf;
|
||
|
||
/* The filename and inode of the last read dirent. */
|
||
/* On-disk name is at most 255 UTF-16 codepoints.
|
||
Every UTF-16 codepoint is at most 4 UTF-8 bytes.
|
||
*/
|
||
char name[256 * GRUB_MAX_UTF8_PER_UTF16 + 1];
|
||
grub_uint32_t ino;
|
||
} GRUB_PACKED;
|
||
|
||
|
||
static grub_dl_t my_mod;
|
||
|
||
static grub_err_t grub_jfs_lookup_symlink (struct grub_jfs_data *data, grub_uint32_t ino);
|
||
|
||
static grub_int64_t
|
||
getblk (struct grub_jfs_treehead *treehead,
|
||
struct grub_jfs_tree_extent *extents,
|
||
struct grub_jfs_data *data,
|
||
grub_uint64_t blk)
|
||
{
|
||
int found = -1;
|
||
int i;
|
||
|
||
for (i = 0; i < grub_le_to_cpu16 (treehead->count) - 2; i++)
|
||
{
|
||
if (treehead->flags & GRUB_JFS_TREE_LEAF)
|
||
{
|
||
/* Read the leafnode. */
|
||
if (grub_le_to_cpu32 (extents[i].offset2) <= blk
|
||
&& ((grub_le_to_cpu16 (extents[i].extent.length))
|
||
+ (extents[i].extent.length2 << 16)
|
||
+ grub_le_to_cpu32 (extents[i].offset2)) > blk)
|
||
return (blk - grub_le_to_cpu32 (extents[i].offset2)
|
||
+ grub_le_to_cpu32 (extents[i].extent.blk2));
|
||
}
|
||
else
|
||
if (blk >= grub_le_to_cpu32 (extents[i].offset2))
|
||
found = i;
|
||
}
|
||
|
||
if (found != -1)
|
||
{
|
||
grub_int64_t ret = -1;
|
||
struct
|
||
{
|
||
struct grub_jfs_treehead treehead;
|
||
struct grub_jfs_tree_extent extents[254];
|
||
} *tree;
|
||
|
||
tree = grub_zalloc (sizeof (*tree));
|
||
if (!tree)
|
||
return -1;
|
||
|
||
if (!grub_disk_read (data->disk,
|
||
((grub_disk_addr_t) grub_le_to_cpu32 (extents[found].extent.blk2))
|
||
<< (grub_le_to_cpu16 (data->sblock.log2_blksz)
|
||
- GRUB_DISK_SECTOR_BITS), 0,
|
||
sizeof (*tree), (char *) tree))
|
||
ret = getblk (&tree->treehead, &tree->extents[0], data, blk);
|
||
grub_free (tree);
|
||
return ret;
|
||
}
|
||
|
||
return -1;
|
||
}
|
||
|
||
/* Get the block number for the block BLK in the node INODE in the
|
||
mounted filesystem DATA. */
|
||
static grub_int64_t
|
||
grub_jfs_blkno (struct grub_jfs_data *data, struct grub_jfs_inode *inode,
|
||
grub_uint64_t blk)
|
||
{
|
||
return getblk (&inode->file.tree, &inode->file.extents[0], data, blk);
|
||
}
|
||
|
||
|
||
static grub_err_t
|
||
grub_jfs_read_inode (struct grub_jfs_data *data, grub_uint32_t ino,
|
||
struct grub_jfs_inode *inode)
|
||
{
|
||
struct grub_jfs_extent iag_inodes[GRUB_JFS_IAG_INODES_COUNT];
|
||
grub_uint32_t iagnum = ino / 4096;
|
||
unsigned inoext = (ino % 4096) / 32;
|
||
unsigned inonum = (ino % 4096) % 32;
|
||
grub_uint64_t iagblk;
|
||
grub_uint64_t inoblk;
|
||
|
||
iagblk = grub_jfs_blkno (data, &data->fileset, iagnum + 1);
|
||
if (grub_errno)
|
||
return grub_errno;
|
||
|
||
/* Read in the IAG. */
|
||
if (grub_disk_read (data->disk,
|
||
iagblk << (grub_le_to_cpu16 (data->sblock.log2_blksz)
|
||
- GRUB_DISK_SECTOR_BITS),
|
||
GRUB_JFS_IAG_INODES_OFFSET,
|
||
sizeof (iag_inodes), &iag_inodes))
|
||
return grub_errno;
|
||
|
||
inoblk = grub_le_to_cpu32 (iag_inodes[inoext].blk2);
|
||
inoblk <<= (grub_le_to_cpu16 (data->sblock.log2_blksz)
|
||
- GRUB_DISK_SECTOR_BITS);
|
||
inoblk += inonum;
|
||
|
||
if (grub_disk_read (data->disk, inoblk, 0,
|
||
sizeof (struct grub_jfs_inode), inode))
|
||
return grub_errno;
|
||
|
||
return 0;
|
||
}
|
||
|
||
|
||
static struct grub_jfs_data *
|
||
grub_jfs_mount (grub_disk_t disk)
|
||
{
|
||
struct grub_jfs_data *data = 0;
|
||
|
||
data = grub_malloc (sizeof (struct grub_jfs_data));
|
||
if (!data)
|
||
return 0;
|
||
|
||
/* Read the superblock. */
|
||
if (grub_disk_read (disk, GRUB_JFS_SBLOCK, 0,
|
||
sizeof (struct grub_jfs_sblock), &data->sblock))
|
||
goto fail;
|
||
|
||
if (grub_strncmp ((char *) (data->sblock.magic), "JFS1", 4))
|
||
{
|
||
grub_error (GRUB_ERR_BAD_FS, "not a JFS filesystem");
|
||
goto fail;
|
||
}
|
||
|
||
if (data->sblock.blksz == 0
|
||
|| grub_le_to_cpu32 (data->sblock.blksz)
|
||
!= (1U << grub_le_to_cpu16 (data->sblock.log2_blksz))
|
||
|| grub_le_to_cpu16 (data->sblock.log2_blksz) < GRUB_DISK_SECTOR_BITS)
|
||
{
|
||
grub_error (GRUB_ERR_BAD_FS, "not a JFS filesystem");
|
||
goto fail;
|
||
}
|
||
|
||
data->disk = disk;
|
||
data->pos = 0;
|
||
data->linknest = 0;
|
||
|
||
/* Read the inode of the first fileset. */
|
||
if (grub_disk_read (data->disk, GRUB_JFS_FS1_INODE_BLK, 0,
|
||
sizeof (struct grub_jfs_inode), &data->fileset))
|
||
goto fail;
|
||
|
||
if (data->sblock.flags & grub_cpu_to_le32_compile_time (0x00200000))
|
||
data->namecomponentlen = 11;
|
||
else
|
||
data->namecomponentlen = 13;
|
||
|
||
if (data->sblock.flags & grub_cpu_to_le32_compile_time (0x40000000))
|
||
data->caseins = 1;
|
||
else
|
||
data->caseins = 0;
|
||
|
||
return data;
|
||
|
||
fail:
|
||
grub_free (data);
|
||
|
||
if (grub_errno == GRUB_ERR_OUT_OF_RANGE)
|
||
grub_error (GRUB_ERR_BAD_FS, "not a JFS filesystem");
|
||
|
||
return 0;
|
||
}
|
||
|
||
|
||
static struct grub_jfs_diropen *
|
||
grub_jfs_opendir (struct grub_jfs_data *data, struct grub_jfs_inode *inode)
|
||
{
|
||
struct grub_jfs_internal_dirent *de;
|
||
struct grub_jfs_diropen *diro;
|
||
grub_disk_addr_t blk;
|
||
|
||
de = (struct grub_jfs_internal_dirent *) inode->dir.dirents;
|
||
|
||
if (!((grub_le_to_cpu32 (inode->mode)
|
||
& GRUB_JFS_FILETYPE_MASK) == GRUB_JFS_FILETYPE_DIR))
|
||
{
|
||
grub_error (GRUB_ERR_BAD_FILE_TYPE, N_("not a directory"));
|
||
return 0;
|
||
}
|
||
|
||
diro = grub_zalloc (sizeof (struct grub_jfs_diropen));
|
||
if (!diro)
|
||
return 0;
|
||
|
||
diro->data = data;
|
||
diro->inode = inode;
|
||
|
||
/* Check if the entire tree is contained within the inode. */
|
||
if (inode->file.tree.flags & GRUB_JFS_TREE_LEAF)
|
||
{
|
||
diro->leaf = inode->dir.dirents;
|
||
diro->next_leaf = (struct grub_jfs_leaf_next_dirent *) de;
|
||
diro->sorted = inode->dir.header.sorted;
|
||
diro->count = inode->dir.header.count;
|
||
|
||
return diro;
|
||
}
|
||
|
||
diro->dirpage = grub_malloc (grub_le_to_cpu32 (data->sblock.blksz));
|
||
if (!diro->dirpage)
|
||
{
|
||
grub_free (diro);
|
||
return 0;
|
||
}
|
||
|
||
blk = grub_le_to_cpu32 (de[inode->dir.header.sorted[0]].ex.blk2);
|
||
blk <<= (grub_le_to_cpu16 (data->sblock.log2_blksz) - GRUB_DISK_SECTOR_BITS);
|
||
|
||
/* Read in the nodes until we are on the leaf node level. */
|
||
do
|
||
{
|
||
int index;
|
||
if (grub_disk_read (data->disk, blk, 0,
|
||
grub_le_to_cpu32 (data->sblock.blksz),
|
||
diro->dirpage->sorted))
|
||
{
|
||
grub_free (diro->dirpage);
|
||
grub_free (diro);
|
||
return 0;
|
||
}
|
||
|
||
de = (struct grub_jfs_internal_dirent *) diro->dirpage->dirent;
|
||
index = diro->dirpage->sorted[diro->dirpage->header.sindex * 32];
|
||
blk = (grub_le_to_cpu32 (de[index].ex.blk2)
|
||
<< (grub_le_to_cpu16 (data->sblock.log2_blksz)
|
||
- GRUB_DISK_SECTOR_BITS));
|
||
} while (!(diro->dirpage->header.flags & GRUB_JFS_TREE_LEAF));
|
||
|
||
diro->leaf = diro->dirpage->dirent;
|
||
diro->next_leaf = diro->dirpage->next_dirent;
|
||
diro->sorted = &diro->dirpage->sorted[diro->dirpage->header.sindex * 32];
|
||
diro->count = diro->dirpage->header.count;
|
||
|
||
return diro;
|
||
}
|
||
|
||
|
||
static void
|
||
grub_jfs_closedir (struct grub_jfs_diropen *diro)
|
||
{
|
||
if (!diro)
|
||
return;
|
||
grub_free (diro->dirpage);
|
||
grub_free (diro);
|
||
}
|
||
|
||
static void
|
||
le_to_cpu16_copy (grub_uint16_t *out, grub_uint16_t *in, grub_size_t len)
|
||
{
|
||
while (len--)
|
||
*out++ = grub_le_to_cpu16 (*in++);
|
||
}
|
||
|
||
#if __GNUC__ >= 9
|
||
#pragma GCC diagnostic push
|
||
#pragma GCC diagnostic ignored "-Waddress-of-packed-member"
|
||
#endif
|
||
|
||
/* Read in the next dirent from the directory described by DIRO. */
|
||
static grub_err_t
|
||
grub_jfs_getent (struct grub_jfs_diropen *diro)
|
||
{
|
||
int strpos = 0;
|
||
struct grub_jfs_leaf_dirent *leaf;
|
||
struct grub_jfs_leaf_next_dirent *next_leaf;
|
||
int len;
|
||
int nextent;
|
||
grub_uint16_t filename[256];
|
||
|
||
/* The last node, read in more. */
|
||
if (diro->index == diro->count)
|
||
{
|
||
grub_disk_addr_t next;
|
||
|
||
/* If the inode contains the entry tree or if this was the last
|
||
node, there is nothing to read. */
|
||
if ((diro->inode->file.tree.flags & GRUB_JFS_TREE_LEAF)
|
||
|| !grub_le_to_cpu64 (diro->dirpage->header.nextb))
|
||
return GRUB_ERR_OUT_OF_RANGE;
|
||
|
||
next = grub_le_to_cpu64 (diro->dirpage->header.nextb);
|
||
next <<= (grub_le_to_cpu16 (diro->data->sblock.log2_blksz)
|
||
- GRUB_DISK_SECTOR_BITS);
|
||
|
||
if (grub_disk_read (diro->data->disk, next, 0,
|
||
grub_le_to_cpu32 (diro->data->sblock.blksz),
|
||
diro->dirpage->sorted))
|
||
return grub_errno;
|
||
|
||
diro->leaf = diro->dirpage->dirent;
|
||
diro->next_leaf = diro->dirpage->next_dirent;
|
||
diro->sorted = &diro->dirpage->sorted[diro->dirpage->header.sindex * 32];
|
||
diro->count = diro->dirpage->header.count;
|
||
diro->index = 0;
|
||
}
|
||
|
||
leaf = &diro->leaf[diro->sorted[diro->index]];
|
||
next_leaf = &diro->next_leaf[diro->index];
|
||
|
||
len = leaf->len;
|
||
if (!len)
|
||
{
|
||
diro->index++;
|
||
return grub_jfs_getent (diro);
|
||
}
|
||
|
||
le_to_cpu16_copy (filename + strpos, leaf->namepart, len < diro->data->namecomponentlen ? len
|
||
: diro->data->namecomponentlen);
|
||
strpos += len < diro->data->namecomponentlen ? len
|
||
: diro->data->namecomponentlen;
|
||
diro->ino = grub_le_to_cpu32 (leaf->inode);
|
||
len -= diro->data->namecomponentlen;
|
||
|
||
/* Move down to the leaf level. */
|
||
nextent = leaf->next;
|
||
if (leaf->next != 255)
|
||
do
|
||
{
|
||
next_leaf = &diro->next_leaf[nextent];
|
||
le_to_cpu16_copy (filename + strpos, next_leaf->namepart, len < 15 ? len : 15);
|
||
strpos += len < 15 ? len : 15;
|
||
|
||
len -= 15;
|
||
nextent = next_leaf->next;
|
||
} while (next_leaf->next != 255 && len > 0);
|
||
|
||
diro->index++;
|
||
|
||
/* Convert the temporary UTF16 filename to UTF8. */
|
||
*grub_utf16_to_utf8 ((grub_uint8_t *) (diro->name), filename, strpos) = '\0';
|
||
|
||
return 0;
|
||
}
|
||
|
||
#if __GNUC__ >= 9
|
||
#pragma GCC diagnostic pop
|
||
#endif
|
||
|
||
/* Read LEN bytes from the file described by DATA starting with byte
|
||
POS. Return the amount of read bytes in READ. */
|
||
static grub_ssize_t
|
||
grub_jfs_read_file (struct grub_jfs_data *data,
|
||
grub_disk_read_hook_t read_hook, void *read_hook_data,
|
||
grub_off_t pos, grub_size_t len, char *buf)
|
||
{
|
||
grub_off_t i;
|
||
grub_off_t blockcnt;
|
||
|
||
blockcnt = (len + pos + grub_le_to_cpu32 (data->sblock.blksz) - 1)
|
||
>> grub_le_to_cpu16 (data->sblock.log2_blksz);
|
||
|
||
for (i = pos >> grub_le_to_cpu16 (data->sblock.log2_blksz); i < blockcnt; i++)
|
||
{
|
||
grub_disk_addr_t blknr;
|
||
grub_uint32_t blockoff = pos & (grub_le_to_cpu32 (data->sblock.blksz) - 1);
|
||
grub_uint32_t blockend = grub_le_to_cpu32 (data->sblock.blksz);
|
||
|
||
grub_uint64_t skipfirst = 0;
|
||
|
||
blknr = grub_jfs_blkno (data, &data->currinode, i);
|
||
if (grub_errno)
|
||
return -1;
|
||
|
||
/* Last block. */
|
||
if (i == blockcnt - 1)
|
||
{
|
||
blockend = (len + pos) & (grub_le_to_cpu32 (data->sblock.blksz) - 1);
|
||
|
||
if (!blockend)
|
||
blockend = grub_le_to_cpu32 (data->sblock.blksz);
|
||
}
|
||
|
||
/* First block. */
|
||
if (i == (pos >> grub_le_to_cpu16 (data->sblock.log2_blksz)))
|
||
{
|
||
skipfirst = blockoff;
|
||
blockend -= skipfirst;
|
||
}
|
||
|
||
data->disk->read_hook = read_hook;
|
||
data->disk->read_hook_data = read_hook_data;
|
||
grub_disk_read (data->disk,
|
||
blknr << (grub_le_to_cpu16 (data->sblock.log2_blksz)
|
||
- GRUB_DISK_SECTOR_BITS),
|
||
skipfirst, blockend, buf);
|
||
|
||
data->disk->read_hook = 0;
|
||
if (grub_errno)
|
||
return -1;
|
||
|
||
buf += grub_le_to_cpu32 (data->sblock.blksz) - skipfirst;
|
||
}
|
||
|
||
return len;
|
||
}
|
||
|
||
|
||
/* Find the file with the pathname PATH on the filesystem described by
|
||
DATA. */
|
||
static grub_err_t
|
||
grub_jfs_find_file (struct grub_jfs_data *data, const char *path,
|
||
grub_uint32_t start_ino)
|
||
{
|
||
const char *name;
|
||
const char *next = path;
|
||
struct grub_jfs_diropen *diro = NULL;
|
||
|
||
if (grub_jfs_read_inode (data, start_ino, &data->currinode))
|
||
return grub_errno;
|
||
|
||
while (1)
|
||
{
|
||
name = next;
|
||
while (*name == '/')
|
||
name++;
|
||
if (name[0] == 0)
|
||
return GRUB_ERR_NONE;
|
||
for (next = name; *next && *next != '/'; next++);
|
||
|
||
if (name[0] == '.' && name + 1 == next)
|
||
continue;
|
||
|
||
if (name[0] == '.' && name[1] == '.' && name + 2 == next)
|
||
{
|
||
grub_uint32_t ino = grub_le_to_cpu32 (data->currinode.dir.header.idotdot);
|
||
|
||
if (grub_jfs_read_inode (data, ino, &data->currinode))
|
||
return grub_errno;
|
||
|
||
continue;
|
||
}
|
||
|
||
diro = grub_jfs_opendir (data, &data->currinode);
|
||
if (!diro)
|
||
return grub_errno;
|
||
|
||
for (;;)
|
||
{
|
||
if (grub_jfs_getent (diro) == GRUB_ERR_OUT_OF_RANGE)
|
||
{
|
||
grub_jfs_closedir (diro);
|
||
return grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("file `%s' not found"), path);
|
||
}
|
||
|
||
/* Check if the current direntry matches the current part of the
|
||
pathname. */
|
||
if ((data->caseins ? grub_strncasecmp (name, diro->name, next - name) == 0
|
||
: grub_strncmp (name, diro->name, next - name) == 0) && !diro->name[next - name])
|
||
{
|
||
grub_uint32_t ino = diro->ino;
|
||
grub_uint32_t dirino = grub_le_to_cpu32 (data->currinode.inode);
|
||
|
||
grub_jfs_closedir (diro);
|
||
diro = 0;
|
||
|
||
if (grub_jfs_read_inode (data, ino, &data->currinode))
|
||
break;
|
||
|
||
/* Check if this is a symlink. */
|
||
if ((grub_le_to_cpu32 (data->currinode.mode)
|
||
& GRUB_JFS_FILETYPE_MASK) == GRUB_JFS_FILETYPE_LNK)
|
||
{
|
||
grub_jfs_lookup_symlink (data, dirino);
|
||
if (grub_errno)
|
||
return grub_errno;
|
||
}
|
||
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
static grub_err_t
|
||
grub_jfs_lookup_symlink (struct grub_jfs_data *data, grub_uint32_t ino)
|
||
{
|
||
grub_size_t size = grub_le_to_cpu64 (data->currinode.size);
|
||
char *symlink;
|
||
|
||
if (++data->linknest > GRUB_JFS_MAX_SYMLNK_CNT)
|
||
return grub_error (GRUB_ERR_SYMLINK_LOOP, N_("too deep nesting of symlinks"));
|
||
|
||
symlink = grub_malloc (size + 1);
|
||
if (!symlink)
|
||
return grub_errno;
|
||
if (size <= sizeof (data->currinode.symlink.path))
|
||
grub_memcpy (symlink, (char *) (data->currinode.symlink.path), size);
|
||
else if (grub_jfs_read_file (data, 0, 0, 0, size, symlink) < 0)
|
||
{
|
||
grub_free (symlink);
|
||
return grub_errno;
|
||
}
|
||
|
||
symlink[size] = '\0';
|
||
|
||
/* The symlink is an absolute path, go back to the root inode. */
|
||
if (symlink[0] == '/')
|
||
ino = 2;
|
||
|
||
grub_jfs_find_file (data, symlink, ino);
|
||
|
||
grub_free (symlink);
|
||
|
||
return grub_errno;
|
||
}
|
||
|
||
|
||
static grub_err_t
|
||
grub_jfs_dir (grub_device_t device, const char *path,
|
||
grub_fs_dir_hook_t hook, void *hook_data)
|
||
{
|
||
struct grub_jfs_data *data = 0;
|
||
struct grub_jfs_diropen *diro = 0;
|
||
|
||
grub_dl_ref (my_mod);
|
||
|
||
data = grub_jfs_mount (device->disk);
|
||
if (!data)
|
||
goto fail;
|
||
|
||
if (grub_jfs_find_file (data, path, GRUB_JFS_AGGR_INODE))
|
||
goto fail;
|
||
|
||
diro = grub_jfs_opendir (data, &data->currinode);
|
||
if (!diro)
|
||
goto fail;
|
||
|
||
/* Iterate over the dirents in the directory that was found. */
|
||
while (grub_jfs_getent (diro) != GRUB_ERR_OUT_OF_RANGE)
|
||
{
|
||
struct grub_jfs_inode inode;
|
||
struct grub_dirhook_info info;
|
||
grub_memset (&info, 0, sizeof (info));
|
||
|
||
if (grub_jfs_read_inode (data, diro->ino, &inode))
|
||
goto fail;
|
||
|
||
info.dir = (grub_le_to_cpu32 (inode.mode)
|
||
& GRUB_JFS_FILETYPE_MASK) == GRUB_JFS_FILETYPE_DIR;
|
||
info.mtimeset = 1;
|
||
info.mtime = grub_le_to_cpu32 (inode.mtime.sec);
|
||
if (hook (diro->name, &info, hook_data))
|
||
goto fail;
|
||
}
|
||
|
||
/* XXX: GRUB_ERR_OUT_OF_RANGE is used for the last dirent. */
|
||
if (grub_errno == GRUB_ERR_OUT_OF_RANGE)
|
||
grub_errno = 0;
|
||
|
||
fail:
|
||
grub_jfs_closedir (diro);
|
||
grub_free (data);
|
||
|
||
grub_dl_unref (my_mod);
|
||
|
||
return grub_errno;
|
||
}
|
||
|
||
|
||
/* Open a file named NAME and initialize FILE. */
|
||
static grub_err_t
|
||
grub_jfs_open (struct grub_file *file, const char *name)
|
||
{
|
||
struct grub_jfs_data *data;
|
||
|
||
grub_dl_ref (my_mod);
|
||
|
||
data = grub_jfs_mount (file->device->disk);
|
||
if (!data)
|
||
goto fail;
|
||
|
||
grub_jfs_find_file (data, name, GRUB_JFS_AGGR_INODE);
|
||
if (grub_errno)
|
||
goto fail;
|
||
|
||
/* It is only possible for open regular files. */
|
||
if (! ((grub_le_to_cpu32 (data->currinode.mode)
|
||
& GRUB_JFS_FILETYPE_MASK) == GRUB_JFS_FILETYPE_REG))
|
||
{
|
||
grub_error (GRUB_ERR_BAD_FILE_TYPE, N_("not a regular file"));
|
||
goto fail;
|
||
}
|
||
|
||
file->data = data;
|
||
file->size = grub_le_to_cpu64 (data->currinode.size);
|
||
|
||
return 0;
|
||
|
||
fail:
|
||
|
||
grub_dl_unref (my_mod);
|
||
|
||
grub_free (data);
|
||
|
||
return grub_errno;
|
||
}
|
||
|
||
|
||
static grub_ssize_t
|
||
grub_jfs_read (grub_file_t file, char *buf, grub_size_t len)
|
||
{
|
||
struct grub_jfs_data *data =
|
||
(struct grub_jfs_data *) file->data;
|
||
|
||
return grub_jfs_read_file (data, file->read_hook, file->read_hook_data,
|
||
file->offset, len, buf);
|
||
}
|
||
|
||
|
||
static grub_err_t
|
||
grub_jfs_close (grub_file_t file)
|
||
{
|
||
grub_free (file->data);
|
||
|
||
grub_dl_unref (my_mod);
|
||
|
||
return GRUB_ERR_NONE;
|
||
}
|
||
|
||
static grub_err_t
|
||
grub_jfs_uuid (grub_device_t device, char **uuid)
|
||
{
|
||
struct grub_jfs_data *data;
|
||
grub_disk_t disk = device->disk;
|
||
|
||
grub_dl_ref (my_mod);
|
||
|
||
data = grub_jfs_mount (disk);
|
||
if (data)
|
||
{
|
||
*uuid = grub_xasprintf ("%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-"
|
||
"%02x%02x%02x%02x%02x%02x",
|
||
data->sblock.uuid[0], data->sblock.uuid[1],
|
||
data->sblock.uuid[2], data->sblock.uuid[3],
|
||
data->sblock.uuid[4], data->sblock.uuid[5],
|
||
data->sblock.uuid[6], data->sblock.uuid[7],
|
||
data->sblock.uuid[8], data->sblock.uuid[9],
|
||
data->sblock.uuid[10], data->sblock.uuid[11],
|
||
data->sblock.uuid[12], data->sblock.uuid[13],
|
||
data->sblock.uuid[14], data->sblock.uuid[15]);
|
||
}
|
||
else
|
||
*uuid = NULL;
|
||
|
||
grub_dl_unref (my_mod);
|
||
|
||
grub_free (data);
|
||
|
||
return grub_errno;
|
||
}
|
||
|
||
static grub_err_t
|
||
grub_jfs_label (grub_device_t device, char **label)
|
||
{
|
||
struct grub_jfs_data *data;
|
||
data = grub_jfs_mount (device->disk);
|
||
|
||
if (data)
|
||
{
|
||
if (data->sblock.volname2[0] < ' ')
|
||
{
|
||
char *ptr;
|
||
ptr = data->sblock.volname + sizeof (data->sblock.volname) - 1;
|
||
while (ptr >= data->sblock.volname && *ptr == ' ')
|
||
ptr--;
|
||
*label = grub_strndup (data->sblock.volname,
|
||
ptr - data->sblock.volname + 1);
|
||
}
|
||
else
|
||
*label = grub_strndup (data->sblock.volname2,
|
||
sizeof (data->sblock.volname2));
|
||
}
|
||
else
|
||
*label = 0;
|
||
|
||
grub_free (data);
|
||
|
||
return grub_errno;
|
||
}
|
||
|
||
|
||
static struct grub_fs grub_jfs_fs =
|
||
{
|
||
.name = "jfs",
|
||
.fs_dir = grub_jfs_dir,
|
||
.fs_open = grub_jfs_open,
|
||
.fs_read = grub_jfs_read,
|
||
.fs_close = grub_jfs_close,
|
||
.fs_label = grub_jfs_label,
|
||
.fs_uuid = grub_jfs_uuid,
|
||
#ifdef GRUB_UTIL
|
||
.reserved_first_sector = 1,
|
||
.blocklist_install = 1,
|
||
#endif
|
||
.next = 0
|
||
};
|
||
|
||
GRUB_MOD_INIT(jfs)
|
||
{
|
||
grub_fs_register (&grub_jfs_fs);
|
||
my_mod = mod;
|
||
}
|
||
|
||
GRUB_MOD_FINI(jfs)
|
||
{
|
||
grub_fs_unregister (&grub_jfs_fs);
|
||
}
|