/* jfs.c - JFS. */ /* * VAS_EBOOT -- GRand Unified Bootloader * Copyright (C) 2004,2005,2006,2007,2008,2009 Free Software Foundation, Inc. * * VAS_EBOOT 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. * * VAS_EBOOT 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 VAS_EBOOT. If not, see . */ #include #include #include #include #include #include #include #include #include #include VAS_EBOOT_MOD_LICENSE ("GPLv3+"); #define VAS_EBOOT_JFS_MAX_SYMLNK_CNT 8 #define VAS_EBOOT_JFS_FILETYPE_MASK 0170000 #define VAS_EBOOT_JFS_FILETYPE_REG 0100000 #define VAS_EBOOT_JFS_FILETYPE_LNK 0120000 #define VAS_EBOOT_JFS_FILETYPE_DIR 0040000 #define VAS_EBOOT_JFS_SBLOCK 64 #define VAS_EBOOT_JFS_AGGR_INODE 2 #define VAS_EBOOT_JFS_FS1_INODE_BLK 104 #define VAS_EBOOT_JFS_TREE_LEAF 2 /* * Define max entries stored in-line in an inode. * https://jfs.sourceforge.net/project/pub/jfslayout.pdf */ #define VAS_EBOOT_JFS_INODE_INLINE_ENTRIES 8 #define VAS_EBOOT_JFS_DIR_MAX_SLOTS 128 struct VasEBoot_jfs_sblock { /* The magic for JFS. It should contain the string "JFS1". */ VasEBoot_uint8_t magic[4]; VasEBoot_uint32_t version; VasEBoot_uint64_t ag_size; /* The size of a filesystem block in bytes. XXX: currently only 4096 was tested. */ VasEBoot_uint32_t blksz; VasEBoot_uint16_t log2_blksz; VasEBoot_uint8_t unused[14]; VasEBoot_uint32_t flags; VasEBoot_uint8_t unused3[61]; char volname[11]; VasEBoot_uint8_t unused2[24]; VasEBoot_uint8_t uuid[16]; char volname2[16]; }; struct VasEBoot_jfs_extent { /* The length of the extent in filesystem blocks. */ VasEBoot_uint16_t length; VasEBoot_uint8_t length2; /* The physical offset of the first block on the disk. */ VasEBoot_uint8_t blk1; VasEBoot_uint32_t blk2; } VAS_EBOOT_PACKED; #define VAS_EBOOT_JFS_IAG_INODES_OFFSET 3072 #define VAS_EBOOT_JFS_IAG_INODES_COUNT 128 struct VasEBoot_jfs_iag { VasEBoot_uint8_t unused[VAS_EBOOT_JFS_IAG_INODES_OFFSET]; struct VasEBoot_jfs_extent inodes[VAS_EBOOT_JFS_IAG_INODES_COUNT]; } VAS_EBOOT_PACKED; /* The head of the tree used to find extents. */ struct VasEBoot_jfs_treehead { VasEBoot_uint64_t next; VasEBoot_uint64_t prev; VasEBoot_uint8_t flags; VasEBoot_uint8_t unused; VasEBoot_uint16_t count; VasEBoot_uint16_t max; VasEBoot_uint8_t unused2[10]; } VAS_EBOOT_PACKED; /* A node in the extent tree. */ struct VasEBoot_jfs_tree_extent { VasEBoot_uint8_t flags; VasEBoot_uint16_t unused; /* The offset is the key used to lookup an extent. */ VasEBoot_uint8_t offset1; VasEBoot_uint32_t offset2; struct VasEBoot_jfs_extent extent; } VAS_EBOOT_PACKED; /* The tree of directory entries. */ struct VasEBoot_jfs_tree_dir { /* Pointers to the previous and next tree headers of other nodes on this level. */ VasEBoot_uint64_t nextb; VasEBoot_uint64_t prevb; VasEBoot_uint8_t flags; /* The amount of dirents in this node. */ VasEBoot_uint8_t count; VasEBoot_uint8_t freecnt; VasEBoot_uint8_t freelist; VasEBoot_uint8_t maxslot; /* The location of the sorted array of pointers to dirents. */ VasEBoot_uint8_t sindex; VasEBoot_uint8_t unused[10]; } VAS_EBOOT_PACKED; /* An internal node in the dirents tree. */ struct VasEBoot_jfs_internal_dirent { struct VasEBoot_jfs_extent ex; VasEBoot_uint8_t next; VasEBoot_uint8_t len; VasEBoot_uint16_t namepart[11]; } VAS_EBOOT_PACKED; /* A leaf node in the dirents tree. */ struct VasEBoot_jfs_leaf_dirent { /* The inode for this dirent. */ VasEBoot_uint32_t inode; VasEBoot_uint8_t next; /* The size of the name. */ VasEBoot_uint8_t len; VasEBoot_uint16_t namepart[11]; VasEBoot_uint32_t index; } VAS_EBOOT_PACKED; /* A leaf in the dirents tree. This one is used if the previously dirent was not big enough to store the name. */ struct VasEBoot_jfs_leaf_next_dirent { VasEBoot_uint8_t next; VasEBoot_uint8_t len; VasEBoot_uint16_t namepart[15]; } VAS_EBOOT_PACKED; struct VasEBoot_jfs_time { VasEBoot_int32_t sec; VasEBoot_int32_t nanosec; } VAS_EBOOT_PACKED; struct VasEBoot_jfs_inode { VasEBoot_uint32_t stamp; VasEBoot_uint32_t fileset; VasEBoot_uint32_t inode; VasEBoot_uint8_t unused[12]; VasEBoot_uint64_t size; VasEBoot_uint8_t unused2[20]; VasEBoot_uint32_t mode; struct VasEBoot_jfs_time atime; struct VasEBoot_jfs_time ctime; struct VasEBoot_jfs_time mtime; VasEBoot_uint8_t unused3[48]; VasEBoot_uint8_t unused4[96]; union { /* The tree describing the extents of the file. */ struct VAS_EBOOT_PACKED { struct VasEBoot_jfs_treehead tree; struct VasEBoot_jfs_tree_extent extents[16]; } file; union { /* The tree describing the dirents. */ struct { VasEBoot_uint8_t unused[16]; VasEBoot_uint8_t flags; /* Amount of dirents in this node. */ VasEBoot_uint8_t count; VasEBoot_uint8_t freecnt; VasEBoot_uint8_t freelist; VasEBoot_uint32_t idotdot; VasEBoot_uint8_t sorted[VAS_EBOOT_JFS_INODE_INLINE_ENTRIES]; } header; struct VasEBoot_jfs_leaf_dirent dirents[VAS_EBOOT_JFS_INODE_INLINE_ENTRIES]; } VAS_EBOOT_PACKED dir; /* Fast symlink. */ struct { VasEBoot_uint8_t unused[32]; VasEBoot_uint8_t path[256]; } symlink; } VAS_EBOOT_PACKED; } VAS_EBOOT_PACKED; struct VasEBoot_jfs_data { struct VasEBoot_jfs_sblock sblock; VasEBoot_disk_t disk; struct VasEBoot_jfs_inode fileset; struct VasEBoot_jfs_inode currinode; int caseins; int pos; int linknest; int namecomponentlen; } VAS_EBOOT_PACKED; struct VasEBoot_jfs_diropen { int index; union { struct VasEBoot_jfs_tree_dir header; struct VasEBoot_jfs_leaf_dirent dirent[0]; struct VasEBoot_jfs_leaf_next_dirent next_dirent[0]; VasEBoot_uint8_t sorted[0]; } VAS_EBOOT_PACKED *dirpage; struct VasEBoot_jfs_data *data; struct VasEBoot_jfs_inode *inode; int count; VasEBoot_uint8_t *sorted; struct VasEBoot_jfs_leaf_dirent *leaf; struct VasEBoot_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 * VAS_EBOOT_MAX_UTF8_PER_UTF16 + 1]; VasEBoot_uint32_t ino; } VAS_EBOOT_PACKED; static VasEBoot_dl_t my_mod; static VasEBoot_err_t VasEBoot_jfs_lookup_symlink (struct VasEBoot_jfs_data *data, VasEBoot_uint32_t ino); /* * An extent's offset, physical and logical, is represented as a 40-bit value. * This 40-bit value is split into two parts: * - offset1: the most signficant 8 bits of the offset, * - offset2: the least significant 32 bits of the offset. * * This function calculates and returns the 64-bit offset of an extent. */ static VasEBoot_uint64_t get_ext_offset (VasEBoot_uint8_t offset1, VasEBoot_uint32_t offset2) { return (((VasEBoot_uint64_t) offset1 << 32) | VasEBoot_le_to_cpu32 (offset2)); } static VasEBoot_uint64_t getblk (struct VasEBoot_jfs_treehead *treehead, struct VasEBoot_jfs_tree_extent *extents, int max_extents, struct VasEBoot_jfs_data *data, VasEBoot_uint64_t blk) { int found = -1; int i; VasEBoot_uint64_t ext_offset, ext_blk; VasEBoot_errno = VAS_EBOOT_ERR_NONE; for (i = 0; i < VasEBoot_le_to_cpu16 (treehead->count) - 2 && i < max_extents; i++) { ext_offset = get_ext_offset (extents[i].offset1, extents[i].offset2); ext_blk = get_ext_offset (extents[i].extent.blk1, extents[i].extent.blk2); if (treehead->flags & VAS_EBOOT_JFS_TREE_LEAF) { /* Read the leafnode. */ if (ext_offset <= blk && ((VasEBoot_le_to_cpu16 (extents[i].extent.length)) + (extents[i].extent.length2 << 16) + ext_offset) > blk) return (blk - ext_offset + ext_blk); } else if (blk >= ext_offset) found = i; } if (found != -1) { VasEBoot_uint64_t ret = 0; struct { struct VasEBoot_jfs_treehead treehead; struct VasEBoot_jfs_tree_extent extents[254]; } *tree; tree = VasEBoot_zalloc (sizeof (*tree)); if (!tree) return 0; if (!VasEBoot_disk_read (data->disk, (VasEBoot_disk_addr_t) ext_blk << (VasEBoot_le_to_cpu16 (data->sblock.log2_blksz) - VAS_EBOOT_DISK_SECTOR_BITS), 0, sizeof (*tree), (char *) tree)) { if (VasEBoot_memcmp (&tree->treehead, treehead, sizeof (struct VasEBoot_jfs_treehead)) || VasEBoot_memcmp (&tree->extents, extents, 254 * sizeof (struct VasEBoot_jfs_tree_extent))) ret = getblk (&tree->treehead, &tree->extents[0], 254, data, blk); else { VasEBoot_error (VAS_EBOOT_ERR_BAD_FS, "jfs: infinite recursion detected"); ret = 0; } } VasEBoot_free (tree); return ret; } VasEBoot_error (VAS_EBOOT_ERR_READ_ERROR, "jfs: block %" PRIuVAS_EBOOT_UINT64_T " not found", blk); return 0; } /* Get the block number for the block BLK in the node INODE in the mounted filesystem DATA. */ static VasEBoot_uint64_t VasEBoot_jfs_blkno (struct VasEBoot_jfs_data *data, struct VasEBoot_jfs_inode *inode, VasEBoot_uint64_t blk) { return getblk (&inode->file.tree, &inode->file.extents[0], 16, data, blk); } static VasEBoot_err_t VasEBoot_jfs_read_inode (struct VasEBoot_jfs_data *data, VasEBoot_uint32_t ino, struct VasEBoot_jfs_inode *inode) { struct VasEBoot_jfs_extent iag_inodes[VAS_EBOOT_JFS_IAG_INODES_COUNT]; VasEBoot_uint32_t iagnum = ino / 4096; unsigned inoext = (ino % 4096) / 32; unsigned inonum = (ino % 4096) % 32; VasEBoot_uint64_t iagblk; VasEBoot_uint64_t inoblk; iagblk = VasEBoot_jfs_blkno (data, &data->fileset, iagnum + 1); if (VasEBoot_errno) return VasEBoot_errno; /* Read in the IAG. */ if (VasEBoot_disk_read (data->disk, iagblk << (VasEBoot_le_to_cpu16 (data->sblock.log2_blksz) - VAS_EBOOT_DISK_SECTOR_BITS), VAS_EBOOT_JFS_IAG_INODES_OFFSET, sizeof (iag_inodes), &iag_inodes)) return VasEBoot_errno; inoblk = get_ext_offset (iag_inodes[inoext].blk1, iag_inodes[inoext].blk2); inoblk <<= (VasEBoot_le_to_cpu16 (data->sblock.log2_blksz) - VAS_EBOOT_DISK_SECTOR_BITS); inoblk += inonum; if (VasEBoot_disk_read (data->disk, inoblk, 0, sizeof (struct VasEBoot_jfs_inode), inode)) return VasEBoot_errno; return 0; } static struct VasEBoot_jfs_data * VasEBoot_jfs_mount (VasEBoot_disk_t disk) { struct VasEBoot_jfs_data *data = 0; data = VasEBoot_malloc (sizeof (struct VasEBoot_jfs_data)); if (!data) return 0; /* Read the superblock. */ if (VasEBoot_disk_read (disk, VAS_EBOOT_JFS_SBLOCK, 0, sizeof (struct VasEBoot_jfs_sblock), &data->sblock)) goto fail; if (VasEBoot_strncmp ((char *) (data->sblock.magic), "JFS1", 4)) { VasEBoot_error (VAS_EBOOT_ERR_BAD_FS, "not a JFS filesystem"); goto fail; } if (data->sblock.blksz == 0 || VasEBoot_le_to_cpu32 (data->sblock.blksz) != (1U << VasEBoot_le_to_cpu16 (data->sblock.log2_blksz)) || VasEBoot_le_to_cpu16 (data->sblock.log2_blksz) < VAS_EBOOT_DISK_SECTOR_BITS) { VasEBoot_error (VAS_EBOOT_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 (VasEBoot_disk_read (data->disk, VAS_EBOOT_JFS_FS1_INODE_BLK, 0, sizeof (struct VasEBoot_jfs_inode), &data->fileset)) goto fail; if (data->sblock.flags & VasEBoot_cpu_to_le32_compile_time (0x00200000)) data->namecomponentlen = 11; else data->namecomponentlen = 13; if (data->sblock.flags & VasEBoot_cpu_to_le32_compile_time (0x40000000)) data->caseins = 1; else data->caseins = 0; return data; fail: VasEBoot_free (data); if (VasEBoot_errno == VAS_EBOOT_ERR_OUT_OF_RANGE) VasEBoot_error (VAS_EBOOT_ERR_BAD_FS, "not a JFS filesystem"); return 0; } static struct VasEBoot_jfs_diropen * VasEBoot_jfs_opendir (struct VasEBoot_jfs_data *data, struct VasEBoot_jfs_inode *inode) { struct VasEBoot_jfs_internal_dirent *de; struct VasEBoot_jfs_diropen *diro; VasEBoot_disk_addr_t blk; de = (struct VasEBoot_jfs_internal_dirent *) inode->dir.dirents; if (!((VasEBoot_le_to_cpu32 (inode->mode) & VAS_EBOOT_JFS_FILETYPE_MASK) == VAS_EBOOT_JFS_FILETYPE_DIR)) { VasEBoot_error (VAS_EBOOT_ERR_BAD_FILE_TYPE, N_("not a directory")); return 0; } diro = VasEBoot_zalloc (sizeof (struct VasEBoot_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 & VAS_EBOOT_JFS_TREE_LEAF) { if (inode->dir.header.count > VAS_EBOOT_JFS_INODE_INLINE_ENTRIES) { VasEBoot_free (diro); VasEBoot_error (VAS_EBOOT_ERR_BAD_FS, N_("invalid JFS inode")); return 0; } diro->leaf = inode->dir.dirents; diro->next_leaf = (struct VasEBoot_jfs_leaf_next_dirent *) de; diro->sorted = inode->dir.header.sorted; diro->count = inode->dir.header.count; return diro; } diro->dirpage = VasEBoot_malloc (VasEBoot_le_to_cpu32 (data->sblock.blksz)); if (!diro->dirpage) { VasEBoot_free (diro); return 0; } if (inode->dir.header.sorted[0] >= VAS_EBOOT_JFS_DIR_MAX_SLOTS) { VasEBoot_error (VAS_EBOOT_ERR_BAD_FS, N_("invalid directory slot index")); VasEBoot_free (diro->dirpage); VasEBoot_free (diro); return 0; } blk = get_ext_offset (de[inode->dir.header.sorted[0]].ex.blk1, de[inode->dir.header.sorted[0]].ex.blk2); blk <<= (VasEBoot_le_to_cpu16 (data->sblock.log2_blksz) - VAS_EBOOT_DISK_SECTOR_BITS); /* Read in the nodes until we are on the leaf node level. */ do { int index; if (VasEBoot_disk_read (data->disk, blk, 0, VasEBoot_le_to_cpu32 (data->sblock.blksz), diro->dirpage->sorted)) { VasEBoot_free (diro->dirpage); VasEBoot_free (diro); return 0; } de = (struct VasEBoot_jfs_internal_dirent *) diro->dirpage->dirent; index = diro->dirpage->sorted[diro->dirpage->header.sindex * 32]; blk = (get_ext_offset (de[index].ex.blk1, de[index].ex.blk2) << (VasEBoot_le_to_cpu16 (data->sblock.log2_blksz) - VAS_EBOOT_DISK_SECTOR_BITS)); } while (!(diro->dirpage->header.flags & VAS_EBOOT_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 VasEBoot_jfs_closedir (struct VasEBoot_jfs_diropen *diro) { if (!diro) return; VasEBoot_free (diro->dirpage); VasEBoot_free (diro); } static void le_to_cpu16_copy (VasEBoot_uint16_t *out, VasEBoot_uint16_t *in, VasEBoot_size_t len) { while (len--) *out++ = VasEBoot_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 VasEBoot_err_t VasEBoot_jfs_getent (struct VasEBoot_jfs_diropen *diro) { int strpos = 0; struct VasEBoot_jfs_leaf_dirent *leaf; struct VasEBoot_jfs_leaf_next_dirent *next_leaf; int len; int nextent; VasEBoot_uint16_t filename[256]; /* The last node, read in more. */ if (diro->index == diro->count) { VasEBoot_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 & VAS_EBOOT_JFS_TREE_LEAF) || !VasEBoot_le_to_cpu64 (diro->dirpage->header.nextb)) return VAS_EBOOT_ERR_OUT_OF_RANGE; next = VasEBoot_le_to_cpu64 (diro->dirpage->header.nextb); next <<= (VasEBoot_le_to_cpu16 (diro->data->sblock.log2_blksz) - VAS_EBOOT_DISK_SECTOR_BITS); if (VasEBoot_disk_read (diro->data->disk, next, 0, VasEBoot_le_to_cpu32 (diro->data->sblock.blksz), diro->dirpage->sorted)) return VasEBoot_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 VasEBoot_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 = VasEBoot_le_to_cpu32 (leaf->inode); len -= diro->data->namecomponentlen; /* Move down to the leaf level. */ nextent = leaf->next; if (leaf->next != 255 && len > 0) 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. */ *VasEBoot_utf16_to_utf8 ((VasEBoot_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 VasEBoot_ssize_t VasEBoot_jfs_read_file (struct VasEBoot_jfs_data *data, VasEBoot_disk_read_hook_t read_hook, void *read_hook_data, VasEBoot_off_t pos, VasEBoot_size_t len, char *buf) { VasEBoot_off_t i; VasEBoot_off_t blockcnt; blockcnt = (len + pos + VasEBoot_le_to_cpu32 (data->sblock.blksz) - 1) >> VasEBoot_le_to_cpu16 (data->sblock.log2_blksz); for (i = pos >> VasEBoot_le_to_cpu16 (data->sblock.log2_blksz); i < blockcnt; i++) { VasEBoot_disk_addr_t blknr; VasEBoot_uint32_t blockoff = pos & (VasEBoot_le_to_cpu32 (data->sblock.blksz) - 1); VasEBoot_uint32_t blockend = VasEBoot_le_to_cpu32 (data->sblock.blksz); VasEBoot_uint64_t skipfirst = 0; blknr = VasEBoot_jfs_blkno (data, &data->currinode, i); if (VasEBoot_errno) return -1; /* Last block. */ if (i == blockcnt - 1) { blockend = (len + pos) & (VasEBoot_le_to_cpu32 (data->sblock.blksz) - 1); if (!blockend) blockend = VasEBoot_le_to_cpu32 (data->sblock.blksz); } /* First block. */ if (i == (pos >> VasEBoot_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; VasEBoot_disk_read (data->disk, blknr << (VasEBoot_le_to_cpu16 (data->sblock.log2_blksz) - VAS_EBOOT_DISK_SECTOR_BITS), skipfirst, blockend, buf); data->disk->read_hook = 0; if (VasEBoot_errno) return -1; buf += VasEBoot_le_to_cpu32 (data->sblock.blksz) - skipfirst; } return len; } /* Find the file with the pathname PATH on the filesystem described by DATA. */ static VasEBoot_err_t VasEBoot_jfs_find_file (struct VasEBoot_jfs_data *data, const char *path, VasEBoot_uint32_t start_ino) { const char *name; const char *next = path; struct VasEBoot_jfs_diropen *diro = NULL; if (VasEBoot_jfs_read_inode (data, start_ino, &data->currinode)) return VasEBoot_errno; while (1) { name = next; while (*name == '/') name++; if (name[0] == 0) return VAS_EBOOT_ERR_NONE; for (next = name; *next && *next != '/'; next++); if (name[0] == '.' && name + 1 == next) continue; if (name[0] == '.' && name[1] == '.' && name + 2 == next) { VasEBoot_uint32_t ino = VasEBoot_le_to_cpu32 (data->currinode.dir.header.idotdot); if (VasEBoot_jfs_read_inode (data, ino, &data->currinode)) return VasEBoot_errno; continue; } diro = VasEBoot_jfs_opendir (data, &data->currinode); if (!diro) return VasEBoot_errno; for (;;) { if (VasEBoot_jfs_getent (diro) == VAS_EBOOT_ERR_OUT_OF_RANGE) { VasEBoot_jfs_closedir (diro); return VasEBoot_error (VAS_EBOOT_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 ? VasEBoot_strncasecmp (name, diro->name, next - name) == 0 : VasEBoot_strncmp (name, diro->name, next - name) == 0) && !diro->name[next - name]) { VasEBoot_uint32_t ino = diro->ino; VasEBoot_uint32_t dirino = VasEBoot_le_to_cpu32 (data->currinode.inode); VasEBoot_jfs_closedir (diro); if (VasEBoot_jfs_read_inode (data, ino, &data->currinode)) break; /* Check if this is a symlink. */ if ((VasEBoot_le_to_cpu32 (data->currinode.mode) & VAS_EBOOT_JFS_FILETYPE_MASK) == VAS_EBOOT_JFS_FILETYPE_LNK) { VasEBoot_jfs_lookup_symlink (data, dirino); if (VasEBoot_errno) return VasEBoot_errno; } break; } } } } static VasEBoot_err_t VasEBoot_jfs_lookup_symlink (struct VasEBoot_jfs_data *data, VasEBoot_uint32_t ino) { VasEBoot_size_t size = VasEBoot_le_to_cpu64 (data->currinode.size); char *symlink; if (++data->linknest > VAS_EBOOT_JFS_MAX_SYMLNK_CNT) return VasEBoot_error (VAS_EBOOT_ERR_SYMLINK_LOOP, N_("too deep nesting of symlinks")); symlink = VasEBoot_malloc (size + 1); if (!symlink) return VasEBoot_errno; if (size <= sizeof (data->currinode.symlink.path)) VasEBoot_memcpy (symlink, (char *) (data->currinode.symlink.path), size); else if (VasEBoot_jfs_read_file (data, 0, 0, 0, size, symlink) < 0) { VasEBoot_free (symlink); return VasEBoot_errno; } symlink[size] = '\0'; /* The symlink is an absolute path, go back to the root inode. */ if (symlink[0] == '/') ino = 2; VasEBoot_jfs_find_file (data, symlink, ino); VasEBoot_free (symlink); return VasEBoot_errno; } static VasEBoot_err_t VasEBoot_jfs_dir (VasEBoot_device_t device, const char *path, VasEBoot_fs_dir_hook_t hook, void *hook_data) { struct VasEBoot_jfs_data *data = 0; struct VasEBoot_jfs_diropen *diro = 0; VasEBoot_dl_ref (my_mod); data = VasEBoot_jfs_mount (device->disk); if (!data) goto fail; if (VasEBoot_jfs_find_file (data, path, VAS_EBOOT_JFS_AGGR_INODE)) goto fail; diro = VasEBoot_jfs_opendir (data, &data->currinode); if (!diro) goto fail; /* Iterate over the dirents in the directory that was found. */ while (VasEBoot_jfs_getent (diro) != VAS_EBOOT_ERR_OUT_OF_RANGE) { struct VasEBoot_jfs_inode inode; struct VasEBoot_dirhook_info info; VasEBoot_memset (&info, 0, sizeof (info)); if (VasEBoot_jfs_read_inode (data, diro->ino, &inode)) goto fail; info.dir = (VasEBoot_le_to_cpu32 (inode.mode) & VAS_EBOOT_JFS_FILETYPE_MASK) == VAS_EBOOT_JFS_FILETYPE_DIR; info.mtimeset = 1; info.mtime = VasEBoot_le_to_cpu32 (inode.mtime.sec); if (hook (diro->name, &info, hook_data)) goto fail; } /* XXX: VAS_EBOOT_ERR_OUT_OF_RANGE is used for the last dirent. */ if (VasEBoot_errno == VAS_EBOOT_ERR_OUT_OF_RANGE) VasEBoot_errno = 0; fail: VasEBoot_jfs_closedir (diro); VasEBoot_free (data); VasEBoot_dl_unref (my_mod); return VasEBoot_errno; } /* Open a file named NAME and initialize FILE. */ static VasEBoot_err_t VasEBoot_jfs_open (struct VasEBoot_file *file, const char *name) { struct VasEBoot_jfs_data *data; VasEBoot_dl_ref (my_mod); data = VasEBoot_jfs_mount (file->device->disk); if (!data) goto fail; VasEBoot_jfs_find_file (data, name, VAS_EBOOT_JFS_AGGR_INODE); if (VasEBoot_errno) goto fail; /* It is only possible for open regular files. */ if (! ((VasEBoot_le_to_cpu32 (data->currinode.mode) & VAS_EBOOT_JFS_FILETYPE_MASK) == VAS_EBOOT_JFS_FILETYPE_REG)) { VasEBoot_error (VAS_EBOOT_ERR_BAD_FILE_TYPE, N_("not a regular file")); goto fail; } file->data = data; file->size = VasEBoot_le_to_cpu64 (data->currinode.size); return 0; fail: VasEBoot_dl_unref (my_mod); VasEBoot_free (data); return VasEBoot_errno; } static VasEBoot_ssize_t VasEBoot_jfs_read (VasEBoot_file_t file, char *buf, VasEBoot_size_t len) { struct VasEBoot_jfs_data *data = (struct VasEBoot_jfs_data *) file->data; return VasEBoot_jfs_read_file (data, file->read_hook, file->read_hook_data, file->offset, len, buf); } static VasEBoot_err_t VasEBoot_jfs_close (VasEBoot_file_t file) { VasEBoot_free (file->data); VasEBoot_dl_unref (my_mod); return VAS_EBOOT_ERR_NONE; } static VasEBoot_err_t VasEBoot_jfs_uuid (VasEBoot_device_t device, char **uuid) { struct VasEBoot_jfs_data *data; VasEBoot_disk_t disk = device->disk; VasEBoot_dl_ref (my_mod); data = VasEBoot_jfs_mount (disk); if (data) { *uuid = VasEBoot_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; VasEBoot_dl_unref (my_mod); VasEBoot_free (data); return VasEBoot_errno; } static VasEBoot_err_t VasEBoot_jfs_label (VasEBoot_device_t device, char **label) { struct VasEBoot_jfs_data *data; data = VasEBoot_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 = VasEBoot_strndup (data->sblock.volname, ptr - data->sblock.volname + 1); } else *label = VasEBoot_strndup (data->sblock.volname2, sizeof (data->sblock.volname2)); } else *label = 0; VasEBoot_free (data); return VasEBoot_errno; } static struct VasEBoot_fs VasEBoot_jfs_fs = { .name = "jfs", .fs_dir = VasEBoot_jfs_dir, .fs_open = VasEBoot_jfs_open, .fs_read = VasEBoot_jfs_read, .fs_close = VasEBoot_jfs_close, .fs_label = VasEBoot_jfs_label, .fs_uuid = VasEBoot_jfs_uuid, #ifdef VAS_EBOOT_UTIL .reserved_first_sector = 1, .blocklist_install = 1, #endif .next = 0 }; VAS_EBOOT_MOD_INIT(jfs) { if (!VasEBoot_is_lockdown ()) { VasEBoot_jfs_fs.mod = mod; VasEBoot_fs_register (&VasEBoot_jfs_fs); } my_mod = mod; } VAS_EBOOT_MOD_FINI(jfs) { if (!VasEBoot_is_lockdown ()) VasEBoot_fs_unregister (&VasEBoot_jfs_fs); }