blob: a468790b306e91e989ed47ead3a26381431de84b [file] [log] [blame]
/*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*
* Copyright (C) Hewlett-Packard (Paul Bame) paul_bame@hp.com
*/
/*
* This is a set of functions that provides minimal filesystem
* functionality to the Linux bootstrapper. All we can do is
* open and read files... but that's all we need 8-)
*
* This file has been ported from the DEC 32-bit Linux version
* by David Mosberger (davidm@cs.arizona.edu).
*/
/* The following defines are necessary to satisfy ext2_fs.h and
* things it pulls in.
*/
#include <sys/stat.h>
#include <linux/types.h>
#include "ext2_fs.h"
#include "bootloader.h"
#define MAX_OPEN_FILES 5
#define EXTENT_MAX_DEPTH 5
static int ext2_blocksize;
static struct ext2_super_block sb;
static struct ext2_group_desc *gds;
static struct ext2_inode *root_inode = NULL;
static int ngroups = 0;
static int group_size;
static int directlim; /* Maximum direct blkno */
static int ind1lim; /* Maximum single-indir blkno */
static int ind2lim; /* Maximum double-indir blkno */
static int ptrs_per_blk; /* ptrs/indirect block */
static char blkbuf[EXT2_MAX_BLOCK_SIZE];
static int cached_iblkno = -1;
static char iblkbuf[EXT2_MAX_BLOCK_SIZE];
static int cached_diblkno = -1;
static char diblkbuf[EXT2_MAX_BLOCK_SIZE];
static long dev = -1;
static long partition_offset;
struct ext2_inode *fd2inode[MAX_FD];
static struct inode_table_entry {
struct ext2_inode inode;
int inumber;
int free;
unsigned short old_mode;
} inode_table[MAX_OPEN_FILES];
/* to work with existing palo I/O */
#define cons_read(dev, addr, length, seekptr) \
seekread(dev, (void *)addr, length, seekptr)
#define inplace(xform, var) (var) = xform((var))
#undef DEBUG
#define Debug 0
static struct ext3_extent_header *ext3_extent_header(struct ext2_inode *i)
{
return (struct ext3_extent_header *)&i->i_block[0];
}
static void swapsb(struct ext2_super_block *sb)
{
inplace(__le32_to_cpu, sb->s_inodes_count);
inplace(__le32_to_cpu, sb->s_blocks_count);
inplace(__le32_to_cpu, sb->s_r_blocks_count);
inplace(__le32_to_cpu, sb->s_free_blocks_count);
inplace(__le32_to_cpu, sb->s_free_inodes_count);
inplace(__le32_to_cpu, sb->s_first_data_block);
inplace(__le32_to_cpu, sb->s_log_block_size);
inplace(__le32_to_cpu, sb->s_log_frag_size);
inplace(__le32_to_cpu, sb->s_blocks_per_group);
inplace(__le32_to_cpu, sb->s_frags_per_group);
inplace(__le32_to_cpu, sb->s_inodes_per_group);
inplace(__le32_to_cpu, sb->s_mtime);
inplace(__le32_to_cpu, sb->s_wtime);
inplace(__le16_to_cpu, sb->s_mnt_count);
inplace(__le16_to_cpu, sb->s_max_mnt_count);
inplace(__le16_to_cpu, sb->s_magic);
inplace(__le16_to_cpu, sb->s_state);
inplace(__le16_to_cpu, sb->s_errors);
inplace(__le16_to_cpu, sb->s_minor_rev_level);
inplace(__le32_to_cpu, sb->s_lastcheck);
inplace(__le32_to_cpu, sb->s_checkinterval);
inplace(__le32_to_cpu, sb->s_creator_os);
inplace(__le32_to_cpu, sb->s_rev_level);
inplace(__le16_to_cpu, sb->s_def_resuid);
inplace(__le16_to_cpu, sb->s_def_resgid);
inplace(__le32_to_cpu, sb->s_first_ino);
inplace(__le16_to_cpu, sb->s_inode_size);
inplace(__le16_to_cpu, sb->s_block_group_nr);
inplace(__le32_to_cpu, sb->s_feature_compat);
inplace(__le32_to_cpu, sb->s_feature_incompat);
inplace(__le32_to_cpu, sb->s_feature_ro_compat);
inplace(__le32_to_cpu, sb->s_algorithm_usage_bitmap);
inplace(__le16_to_cpu, sb->s_desc_size);
/* whew! */
}
static void swapgrp(struct ext2_group_desc *g)
{
inplace(__le32_to_cpu, g->bg_block_bitmap);
inplace(__le32_to_cpu, g->bg_inode_bitmap);
inplace(__le32_to_cpu, g->bg_inode_table);
inplace(__le16_to_cpu, g->bg_free_blocks_count);
inplace(__le16_to_cpu, g->bg_free_inodes_count);
inplace(__le16_to_cpu, g->bg_used_dirs_count);
}
static void swapextenthdr(struct ext3_extent_header *hdr)
{
inplace(__le16_to_cpu, hdr->eh_magic);
inplace(__le16_to_cpu, hdr->eh_entries);
inplace(__le16_to_cpu, hdr->eh_max);
inplace(__le16_to_cpu, hdr->eh_depth);
inplace(__le32_to_cpu, hdr->eh_generation);
}
static void swapino(struct ext2_inode *i)
{
int n;
inplace(__le16_to_cpu, i->i_mode);
inplace(__le16_to_cpu, i->i_uid);
inplace(__le32_to_cpu, i->i_size);
inplace(__le32_to_cpu, i->i_atime);
inplace(__le32_to_cpu, i->i_ctime);
inplace(__le32_to_cpu, i->i_mtime);
inplace(__le32_to_cpu, i->i_dtime);
inplace(__le16_to_cpu, i->i_gid);
inplace(__le16_to_cpu, i->i_links_count);
inplace(__le32_to_cpu, i->i_blocks);
inplace(__le32_to_cpu, i->i_flags);
/* Don't know how to handle the unions right now -- ignore them */
#if 0
union {
struct {
__u32 l_i_reserved1;
} linux1;
struct {
__u32 h_i_translator;
} hurd1;
struct {
__u32 m_i_reserved1;
} masix1;
} osd1; /* OS dependent 1 */
#endif
if ((i->i_flags & EXT3_EXTENTS_FL)) {
/* the extent header is in the i_block array */
struct ext3_extent_header *hdr = ext3_extent_header(i);
swapextenthdr(hdr);
if (Debug)
printf("ext4: extent based inode; depth %d, size %d\n",
hdr->eh_depth, hdr->eh_entries);
} else {
for (n = 0; n < EXT2_N_BLOCKS; n++) {
inplace(__le32_to_cpu, i->i_block[n]);
}
}
inplace(__le32_to_cpu, i->i_generation);
inplace(__le32_to_cpu, i->i_file_acl);
inplace(__le32_to_cpu, i->i_dir_acl);
inplace(__le32_to_cpu, i->i_faddr);
#if 0
union {
struct {
__u8 l_i_frag; /* Fragment number */
__u8 l_i_fsize; /* Fragment size */
__u16 i_pad1;
__u32 l_i_reserved2[2];
} linux2;
struct {
__u8 h_i_frag; /* Fragment number */
__u8 h_i_fsize; /* Fragment size */
__u16 h_i_mode_high;
__u16 h_i_uid_high;
__u16 h_i_gid_high;
__u32 h_i_author;
} hurd2;
struct {
__u8 m_i_frag; /* Fragment number */
__u8 m_i_fsize; /* Fragment size */
__u16 m_pad1;
__u32 m_i_reserved2[2];
} masix2;
} osd2; /* OS dependent 2 */
#endif
}
static struct ext2_dir_entry_2 de_temp;
static struct ext2_dir_entry_2 *swapde(struct ext2_dir_entry_2 *source)
{
struct ext2_dir_entry_2 *de = &de_temp;
memcpy(de, source, sizeof(de_temp));
if (Debug) printf("swapde: 0x%x 0x%x / ",
de->inode, de->rec_len);
inplace(__le32_to_cpu, de->inode);
inplace(__le16_to_cpu, de->rec_len);
if (Debug) printf("0x%x 0x%x %d %d %.*s\n",
de->inode, de->rec_len,
de->name_len, de->file_type,
de->name_len, de->name);
return de;
}
static struct ext2_group_desc *ext2_gds(int i)
{
char *ptr = (char *)gds;
return (struct ext2_group_desc *)(ptr + group_size * i);
}
/*
* Initialize an ext2 partition starting at offset P_OFFSET; this is
* sort-of the same idea as "mounting" it. Read in the relevant
* control structures and make them available to the user. Returns 0
* if successful, -1 on failure.
*/
int ext2_mount(long cons_dev, long p_offset, long quiet)
{
long sb_block = 1;
long sb_offset;
int i;
dev = cons_dev;
partition_offset = p_offset;
/* initialize the inode table */
for (i = 0; i < MAX_OPEN_FILES; i++) {
inode_table[i].free = 1;
inode_table[i].inumber = 0;
}
/* clear the root inode pointer (very important!) */
root_inode = NULL;
/* read in the first superblock */
sb_offset = sb_block * EXT2_MIN_BLOCK_SIZE;
if (cons_read(dev, (char *)&sb, sizeof(sb), partition_offset + sb_offset)
!= sizeof(sb))
{
printf("ext2 sb read failed\n");
return -1;
}
if (__le16_to_cpu(sb.s_magic) != EXT2_SUPER_MAGIC) {
if (!quiet) {
printf("ext2_init: bad magic 0x%x\n", sb.s_magic);
}
return -1;
}
swapsb(&sb);
ngroups = (sb.s_blocks_count -
sb.s_first_data_block +
EXT2_BLOCKS_PER_GROUP(&sb) - 1)
/ EXT2_BLOCKS_PER_GROUP(&sb);
if (sb.s_feature_incompat & EXT4_FEATURE_INCOMPAT_64BIT)
group_size = sb.s_desc_size;
else
group_size = sizeof(struct ext2_group_desc);
if (Debug) printf("filesystem group size %d\n", group_size);
gds = (struct ext2_group_desc *)malloc(ngroups * group_size);
ext2_blocksize = EXT2_BLOCK_SIZE(&sb);
if (Debug) printf("ext2 block size %d\n", ext2_blocksize);
/* read in the group descriptors (immediately follows superblock) */
cons_read(dev, gds, ngroups * group_size,
partition_offset +
ext2_blocksize * (EXT2_MIN_BLOCK_SIZE/ext2_blocksize + 1));
for (i = 0; i < ngroups; i++)
{
swapgrp(ext2_gds(i));
}
/*
* Calculate direct/indirect block limits for this file system
* (ext2_blocksize dependent):
*/
ext2_blocksize = EXT2_BLOCK_SIZE(&sb);
if (Debug) printf("ext2 block size %d\n", ext2_blocksize);
directlim = EXT2_NDIR_BLOCKS - 1;
ptrs_per_blk = ext2_blocksize/sizeof(unsigned int);
ind1lim = ptrs_per_blk + directlim;
ind2lim = (ptrs_per_blk * ptrs_per_blk) + directlim;
return 0;
}
static void printinode(const char *s, struct ext2_inode *ip)
{
int i;
printf("%s: inode %p mode 0%o uid %d gid %d size %d\n",
s, ip, ip->i_mode, ip->i_uid, ip->i_gid, ip->i_size);
for (i = 0; i < EXT2_NDIR_BLOCKS; i++)
{
printf("\tblock %d at %d (0x%x)\n", i, ip->i_block[i],
ip->i_block[i]);
}
}
/*
* Read the specified inode from the disk and return it to the user.
* Returns NULL if the inode can't be read...
*/
static struct ext2_inode *ext2_iget(int ino)
{
int i;
struct ext2_inode *ip;
struct inode_table_entry *itp = 0;
int group;
__u64 offset;
ip = 0;
for (i = 0; i < MAX_OPEN_FILES; i++) {
#ifdef DEBUG
printf("ext2_iget: looping, entry %d inode %d free %d\n",
i, inode_table[i].inumber, inode_table[i].free);
#endif
if (inode_table[i].free) {
itp = &inode_table[i];
ip = &itp->inode;
break;
}
}
if (!ip) {
printf("ext2_iget: no free inodes\n");
return NULL;
}
group = (ino-1) / sb.s_inodes_per_group;
#ifdef DEBUG
printf("group is %d\n", group);
#endif
offset = partition_offset
+ ((__u64) ext2_gds(group)->bg_inode_table * ext2_blocksize)
+ (((ino - 1) % EXT2_INODES_PER_GROUP(&sb))
* EXT2_INODE_SIZE(&sb));
#ifdef DEBUG
printf("ext2_iget: reading %ld bytes at offset %lld "
"(%ld + (%d * %d) + ((%d) %% %d) * %d) "
"(inode %d -> table %d)\n",
sizeof(struct ext2_inode), offset, partition_offset,
ext2_gds(group)->bg_inode_table, ext2_blocksize,
ino - 1, EXT2_INODES_PER_GROUP(&sb), EXT2_INODE_SIZE(&sb),
ino, (int) (itp - inode_table));
#endif
if (cons_read(dev, ip, sizeof(struct ext2_inode), offset)
!= sizeof(struct ext2_inode))
{
printf("ext2_iget: read error\n");
return NULL;
}
swapino(ip);
if (Debug) printinode("iget", ip);
if (ip->i_flags & EXT3_EXTENTS_FL) {
struct ext3_extent_header *hdr = ext3_extent_header(ip);
if (hdr->eh_magic != EXT3_EXT_MAGIC) {
printf("ext2_iget: wrong extent magic in inode\n");
return NULL;
}
if (hdr->eh_depth > EXTENT_MAX_DEPTH) {
printf("ext2_iget: file has too deep an extent tree]n");
return NULL;
}
}
itp->free = 0;
itp->inumber = ino;
itp->old_mode = ip->i_mode;
return ip;
}
/*
* Release our hold on an inode. Since this is a read-only application,
* don't worry about putting back any changes...
*/
static void ext2_iput(struct ext2_inode *ip)
{
struct inode_table_entry *itp;
/* Find and free the inode table slot we used... */
itp = (struct inode_table_entry *)ip;
#ifdef DEBUG
printf("ext2_iput: inode %d table %d\n", itp->inumber,
(int) (itp - inode_table));
#endif
itp->inumber = 0;
itp->free = 1;
}
/*
* Recursive function to find the mapping of a block in the extent
* tree We make a load of assumptions here, firstly, since we're
* dealing with filesystems < 2GB we assume all the _hi elements are
* zero. Secondly we assume monotonic logical block traversal for
* kernel/initrd loading, so the cache is static per depth in the
* tree.
*/
static int ext3_extent_leaf_find(struct ext3_extent_header *hdr, int blkoff)
{
struct ext3_extent *leaf = (struct ext3_extent *)(hdr + 1);
int i;
for (i = 0; i < hdr->eh_entries; i++) {
__u32 block = __le32_to_cpu(leaf[i].ee_block);
__u16 len = __le16_to_cpu(leaf[i].ee_len);
__u32 start = __le32_to_cpu(leaf[i].ee_start);
if (block <= blkoff && block + len > blkoff)
return blkoff - block + start;
}
/* block is not in map: this means a hole */
return 0;
}
static int ext3_extent_load_find(struct ext2_inode *ip, int leaf, int d,
int blkoff);
static int ext3_extent_node_find(struct ext2_inode *ip,
struct ext3_extent_header *hdr, int blkoff)
{
struct ext3_extent_idx *node = (struct ext3_extent_idx *)(hdr + 1);
struct ext3_extent_idx *prev = node;
int i;
__u32 start = __le32_to_cpu(prev->ei_block);
__u32 leaf = __le32_to_cpu(prev->ei_leaf);
for (i = 1; i < hdr->eh_entries; i++) {
__u32 block = __le32_to_cpu(node[i].ei_block);
if (start <= blkoff && block > blkoff)
break;
prev = &node[i];
start = __le32_to_cpu(prev->ei_block);
leaf = __le32_to_cpu(prev->ei_leaf);
}
return ext3_extent_load_find(ip, leaf, hdr->eh_depth - 1, blkoff);
}
static int ext3_extent_load_find(struct ext2_inode *ip, int leaf, int d,
int blkoff)
{
static char *blockbuf;
static int cached_blockno[EXTENT_MAX_DEPTH];
struct ext3_extent_header *hdr;
if (!blockbuf) {
blockbuf = malloc(EXTENT_MAX_DEPTH * EXT2_MAX_BLOCK_SIZE);
if (!blockbuf) {
printf("Failed to allocate memory for block buffer\n");
return -1;
}
}
hdr = (struct ext3_extent_header *) &blockbuf[d * EXT2_MAX_BLOCK_SIZE];
if (cached_blockno[d] != leaf) {
if (Debug)
printf("load extent tree[%d] block at %d\n", d, leaf);
if (cons_read(dev, hdr, EXT2_MAX_BLOCK_SIZE,
leaf * ext2_blocksize) !=
EXT2_MAX_BLOCK_SIZE) {
printf("ext3_extent_load_find: read error\n");
return -1;
}
cached_blockno[d] = leaf;
swapextenthdr(hdr);
}
/* these checks could be done once after load, but belt and braces */
if (hdr->eh_magic != EXT3_EXT_MAGIC) {
printf("ext3_extent_load_find: wrong extent magic in block\n");
return -1;
}
if (hdr->eh_depth != d) {
printf("ext3_extent_load_find: wrong depth %d!=%d\n",
hdr->eh_depth, d);
return -1;
}
if (sizeof(hdr) + sizeof(struct ext3_extent)*hdr->eh_entries >
EXT2_MAX_BLOCK_SIZE) {
printf("ext3_extent_load_find: extent is larger than buffer\n");
return -1;
}
if (hdr->eh_depth == 0)
return ext3_extent_leaf_find(hdr, blkoff);
else
return ext3_extent_node_find(ip, hdr, blkoff);
}
/*
* Map a block using the extents tree
*/
static int ext3_extent_blkno(struct ext2_inode *ip, int blkoff)
{
struct ext3_extent_header *hdr = ext3_extent_header(ip);
if (hdr->eh_depth == 0)
return ext3_extent_leaf_find(hdr, blkoff);
else
return ext3_extent_node_find(ip, hdr, blkoff);
}
/*
* Map a block offset into a file into an absolute block number.
* (traverse the indirect blocks if necessary). Note: Double-indirect
* blocks allow us to map over 64Mb on a 1k file system. Therefore, for
* our purposes, we will NOT bother with triple indirect blocks.
*
* The "allocate" argument is set if we want to *allocate* a block
* and we don't already have one allocated.
*/
static int ext2_blkno(struct ext2_inode *ip, int blkoff)
{
unsigned int *ilp;
unsigned int *dlp;
int blkno;
int iblkno;
int diblkno;
__u64 offset;
if (ip->i_flags & EXT3_EXTENTS_FL)
return ext3_extent_blkno(ip, blkoff);
ilp = (unsigned int *)iblkbuf;
dlp = (unsigned int *)diblkbuf;
/* If it's a direct block, it's easy! */
if (blkoff <= directlim) {
return ip->i_block[blkoff];
}
/* Is it a single-indirect? */
if (blkoff <= ind1lim) {
iblkno = ip->i_block[EXT2_IND_BLOCK];
if (iblkno == 0) {
return 0;
}
/* Read the indirect block */
if (cached_iblkno != iblkno) {
offset = partition_offset + (__u64)iblkno * ext2_blocksize;
if (cons_read(dev, iblkbuf, ext2_blocksize, offset)
!= ext2_blocksize)
{
printf("ext2_blkno: error on iblk read\n");
return 0;
}
cached_iblkno = iblkno;
}
blkno = ilp[blkoff-(directlim+1)];
inplace(__le32_to_cpu, blkno);
return blkno;
}
/* Is it a double-indirect? */
if (blkoff <= ind2lim) {
/* Find the double-indirect block */
diblkno = ip->i_block[EXT2_DIND_BLOCK];
if (diblkno == 0) {
return 0;
}
/* Read in the double-indirect block */
if (cached_diblkno != diblkno) {
offset = partition_offset + (__u64) diblkno * ext2_blocksize;
if (cons_read(dev, diblkbuf, ext2_blocksize, offset)
!= ext2_blocksize)
{
printf("ext2_blkno: err reading dindr blk\n");
return 0;
}
cached_diblkno = diblkno;
}
/* Find the single-indirect block pointer ... */
iblkno = dlp[(blkoff - (ind1lim+1)) / ptrs_per_blk];
inplace(__le32_to_cpu, iblkno);
if (iblkno == 0) {
return 0;
}
/* Read the indirect block */
if (cached_iblkno != iblkno) {
offset = partition_offset + (__u64) iblkno * ext2_blocksize;
if (cons_read(dev, iblkbuf, ext2_blocksize, offset)
!= ext2_blocksize)
{
printf("ext2_blkno: err on iblk read\n");
return 0;
}
cached_iblkno = iblkno;
}
/* Find the block itself. */
blkno = ilp[(blkoff-(ind1lim+1)) % ptrs_per_blk];
inplace(__le32_to_cpu, blkno);
return blkno;
}
if (blkoff > ind2lim) {
printf("ext2_blkno: block number too large: %d\n", blkoff);
return 0;
}
return -1;
}
static int print_ext2_blkno(struct ext2_inode *ip, int blkoff)
{
int r = ext2_blkno(ip, blkoff);
if (0) printf("ext2_blkno(%p, blk %d) ==> %d\n",
ip, blkoff, r);
return r;
}
static int ext2_breadi(struct ext2_inode *ip, long blkno, long nblks,
char *buffer)
{
long dev_blkno, ncontig, nbytes, tot_bytes;
__u64 offset;
if (Debug) printf("ext2_breadi(%p, %ld, %ld, %p)\n",
ip, blkno, nblks, buffer);
tot_bytes = 0;
if ((blkno+nblks)*ext2_blocksize > ip->i_size)
nblks = (ip->i_size + ext2_blocksize) / ext2_blocksize - blkno;
if (Debug) printf("nblks = %ld\n", nblks);
while (nblks) {
/*
* Contiguous reads are a lot faster, so we try to group
* as many blocks as possible:
*/
ncontig = 0; nbytes = 0;
dev_blkno = print_ext2_blkno(ip, blkno);
if (Debug) printf("dev_blkno = %ld\n", dev_blkno);
do {
++blkno; ++ncontig; --nblks;
nbytes += ext2_blocksize;
} while (nblks &&
print_ext2_blkno(ip, blkno) == dev_blkno + ncontig);
if (Debug) printf("dev_blkno = %ld\n", dev_blkno);
if (dev_blkno == 0) {
/* This is a "hole" */
memset(buffer, 0, nbytes);
} else {
/* Read it for real */
offset = partition_offset + (__u64) dev_blkno * ext2_blocksize;
#ifdef DEBUG
printf("ext2_bread: reading %ld bytes at offset %lld\n",
nbytes, offset);
#endif
if (cons_read(dev, buffer, nbytes, offset)
!= nbytes)
{
printf("ext2_bread: read error\n");
return -1;
}
if (Debug) blockprint(offset, buffer, 128);
}
buffer += nbytes;
tot_bytes += nbytes;
}
return tot_bytes;
}
static struct ext2_dir_entry_2 *ext2_readdiri(struct ext2_inode *dir_inode,
int rewind)
{
struct ext2_dir_entry_2 *dp;
static int diroffset = 0, blockoffset = 0;
/* Reading a different directory, invalidate previous state */
if (rewind) {
diroffset = 0;
blockoffset = 0;
/* read first block */
if (ext2_breadi(dir_inode, 0, 1, blkbuf) < 0)
return NULL;
}
repeat:
#ifdef DEBUG
printf("ext2_readdiri: blkoffset %d diroffset %d len %d\n",
blockoffset, diroffset, dir_inode->i_size);
#endif
if (blockoffset >= ext2_blocksize) {
diroffset += ext2_blocksize;
if (diroffset >= dir_inode->i_size)
return NULL;
#ifdef DEBUG
printf("ext2_readdiri: reading block at %d\n",
diroffset);
#endif
/* assume that this will read the whole block */
if (ext2_breadi(dir_inode,
diroffset / ext2_blocksize,
1, blkbuf) < 0)
return NULL;
blockoffset = 0;
}
dp = (struct ext2_dir_entry_2 *) (blkbuf + blockoffset);
dp = swapde(dp);
blockoffset += dp->rec_len;
/* ext2 deletes a file by zeroing its inode. We skip deleted
* files, corrupt entries and entries that aren't a regular
* file or a symlink */
if (dp->name_len == 0 || dp->inode == 0)
goto repeat;
if (dp->file_type != EXT2_FT_REG_FILE &&
dp->file_type != EXT2_FT_SYMLINK &&
dp->file_type != EXT2_FT_DIR)
goto repeat;
#ifdef DEBUG
printf("ext2_readdiri: returning %p = %.*s\n", dp, dp->name_len, dp->name);
#endif
return dp;
}
static struct ext2_inode *ext2_namei(const char *name)
{
char namebuf[256];
char *component;
struct ext2_inode *dir_inode;
struct ext2_dir_entry_2 *dp;
int next_ino;
/* squirrel away a copy of "namebuf" that we can modify: */
strcpy(namebuf, name);
/* start at the root: */
if (!root_inode)
root_inode = ext2_iget(EXT2_ROOT_INO);
dir_inode = root_inode;
if (!dir_inode)
return NULL;
component = strtok(namebuf, "/");
while (component) {
int component_length;
int rewind = 0;
/*
* Search for the specified component in the current
* directory inode.
*/
next_ino = -1;
component_length = strlen(component);
/* rewind the first time through */
while ((dp = ext2_readdiri(dir_inode, !rewind++))) {
if ((dp->name_len == component_length) &&
(strncmp(component, dp->name,
component_length) == 0))
{
/* Found it! */
#ifdef DEBUG
printf("ext2_namei: found entry %s\n",
component);
#endif
next_ino = dp->inode;
break;
}
#ifdef DEBUG
printf("ext2_namei: looping\n");
#endif
}
#ifdef DEBUG
printf("ext2_namei: next_ino = %d\n", next_ino);
#endif
/*
* At this point, we're done with this directory whether
* we've succeeded or failed...
*/
if (dir_inode != root_inode)
ext2_iput(dir_inode);
/*
* If next_ino is negative, then we've failed (gone
* all the way through without finding anything)
*/
if (next_ino < 0) {
return NULL;
}
/*
* Otherwise, we can get this inode and find the next
* component string...
*/
dir_inode = ext2_iget(next_ino);
if (!dir_inode)
return NULL;
component = strtok(NULL, "/");
}
/*
* If we get here, then we got through all the components.
* Whatever we got must match up with the last one.
*/
return dir_inode;
}
/*
* Note: don't mix any kind of file lookup or other I/O with this or
* you will lose horribly (as it reuses blkbuf)
*/
const char * ext2_readdir(int fd, int rewind, unsigned char *file_type)
{
struct ext2_inode * ip = fd2inode[fd];
struct ext2_dir_entry_2 * ent;
if (file_type)
*file_type = 0;
if (!S_ISDIR(ip->i_mode)) {
printf("fd %d (inode %d) is not a directory (mode %x)\n",
fd, inode_table[fd].inumber, ip->i_mode);
return NULL;
}
ent = ext2_readdiri(ip, rewind);
if (ent) {
ent->name[ent->name_len] = '\0';
if (file_type)
*file_type = ent->file_type;
return ent->name;
} else {
return NULL;
}
}
int ext2_filesize(int fd)
{
struct ext2_inode * ip = fd2inode[fd];
return ip->i_size;
}
#if 0
static int ext2_fstat(int fd, struct stat* buf)
{
struct ext2_inode * ip = &inode_table[fd].inode;
if (fd >= MAX_OPEN_FILES)
return -1;
memset(buf, 0, sizeof(struct stat));
/* fill in relevant fields */
buf->st_ino = inode_table[fd].inumber;
buf->st_mode = ip->i_mode;
buf->st_flags = ip->i_flags;
buf->st_nlink = ip->i_links_count;
buf->st_uid = ip->i_uid;
buf->st_gid = ip->i_gid;
buf->st_size = ip->i_size;
buf->st_blocks = ip->i_blocks;
buf->st_atime = ip->i_atime;
buf->st_mtime = ip->i_mtime;
buf->st_ctime = ip->i_ctime;
return 0; /* NOTHING CAN GO WROGN! */
}
#endif
static struct ext2_inode * ext2_follow_link(struct ext2_inode * from,
const char * base)
{
char *linkto;
if (from->i_blocks) {
linkto = blkbuf;
if (ext2_breadi(from, 0, 1, blkbuf) == -1)
return NULL;
#ifdef DEBUG
printf("long link!\n");
#endif
} else {
int n;
for (n = 0; n < EXT2_N_BLOCKS; n++) {
inplace(__le32_to_cpu, from->i_block[n]);
}
linkto = (char*)from->i_block;
}
#ifdef DEBUG
printf("symlink to %s\n", linkto);
#endif
/* Resolve relative links */
if (linkto[0] != '/') {
char *end = strrchr(base, '/');
if (end) {
char fullname[(end - base + 1) + strlen(linkto) + 1];
strncpy(fullname, base, end - base + 1);
fullname[end - base + 1] = '\0';
strcat(fullname, linkto);
#ifdef DEBUG
printf("resolved to %s\n", fullname);
#endif
return ext2_namei(fullname);
} else {
/* Assume it's in the root */
return ext2_namei(linkto);
}
} else {
return ext2_namei(linkto);
}
}
static int ext2_read(int fd, char *buf, unsigned count, __u64 devaddr)
{
struct ext2_inode * ip;
ip = fd2inode[fd];
if (Debug) printf("ext2_read(%d, 0x%p, %d, %lld)\n",
fd, buf, count, devaddr);
return ext2_breadi(ip,
devaddr / ext2_blocksize,
count / ext2_blocksize,
buf);
}
static void ext2_describe(int fd, int *bufalign,
int *blocksize)
{
describe(dev, bufalign, blocksize);
if (blocksize != 0)
*blocksize = ext2_blocksize;
}
int ext2_open(const char *filename)
{
/*
* Unix-like open routine. Returns a small integer (actually
* an index into the inode table...
*/
struct ext2_inode * ip;
int fd = -1;
ip = ext2_namei(filename);
if (ip) {
while (S_ISLNK(ip->i_mode)) {
ip = ext2_follow_link(ip, filename);
if (!ip) return -1;
}
fd = fileio_open(ext2_describe, ext2_read);
if (fd >= 0)
{
fd2inode[fd] = ip;
}
}
return fd;
}
void ext2_close(int fd)
{
/* blah, hack, don't close the root inode ever */
if (&inode_table[fd].inode != root_inode)
ext2_iput(&inode_table[fd].inode);
}