|  | /* | 
|  | *  GRUB  --  GRand Unified Bootloader | 
|  | *  Copyright (C) 1999, 2001  Free Software Foundation, Inc. | 
|  | * | 
|  | *  This program 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 2 of the License, or | 
|  | *  (at your option) any later version. | 
|  | * | 
|  | *  This program 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 this program; if not, write to the Free Software | 
|  | *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | 
|  | */ | 
|  |  | 
|  | #ifdef FSYS_EXT2FS | 
|  |  | 
|  | #include "shared.h" | 
|  | #include "filesys.h" | 
|  |  | 
|  | static int mapblock1, mapblock2; | 
|  |  | 
|  | /* sizes are always in bytes, BLOCK values are always in DEV_BSIZE (sectors) */ | 
|  | #define DEV_BSIZE 512 | 
|  |  | 
|  | /* include/linux/fs.h */ | 
|  | #define BLOCK_SIZE 1024		/* initial block size for superblock read */ | 
|  | /* made up, defaults to 1 but can be passed via mount_opts */ | 
|  | #define WHICH_SUPER 1 | 
|  | /* kind of from fs/ext2/super.c */ | 
|  | #define SBLOCK (WHICH_SUPER * BLOCK_SIZE / DEV_BSIZE)	/* = 2 */ | 
|  |  | 
|  | /* include/asm-i386/types.h */ | 
|  | typedef __signed__ char __s8; | 
|  | typedef unsigned char __u8; | 
|  | typedef __signed__ short __s16; | 
|  | typedef unsigned short __u16; | 
|  | typedef __signed__ int __s32; | 
|  | typedef unsigned int __u32; | 
|  |  | 
|  | /* | 
|  | * Constants relative to the data blocks, from ext2_fs.h | 
|  | */ | 
|  | #define EXT2_NDIR_BLOCKS                12 | 
|  | #define EXT2_IND_BLOCK                  EXT2_NDIR_BLOCKS | 
|  | #define EXT2_DIND_BLOCK                 (EXT2_IND_BLOCK + 1) | 
|  | #define EXT2_TIND_BLOCK                 (EXT2_DIND_BLOCK + 1) | 
|  | #define EXT2_N_BLOCKS                   (EXT2_TIND_BLOCK + 1) | 
|  |  | 
|  | /* include/linux/ext2_fs.h */ | 
|  | struct ext2_super_block | 
|  | { | 
|  | __u32 s_inodes_count;	/* Inodes count */ | 
|  | __u32 s_blocks_count;	/* Blocks count */ | 
|  | __u32 s_r_blocks_count;	/* Reserved blocks count */ | 
|  | __u32 s_free_blocks_count;	/* Free blocks count */ | 
|  | __u32 s_free_inodes_count;	/* Free inodes count */ | 
|  | __u32 s_first_data_block;	/* First Data Block */ | 
|  | __u32 s_log_block_size;	/* Block size */ | 
|  | __s32 s_log_frag_size;	/* Fragment size */ | 
|  | __u32 s_blocks_per_group;	/* # Blocks per group */ | 
|  | __u32 s_frags_per_group;	/* # Fragments per group */ | 
|  | __u32 s_inodes_per_group;	/* # Inodes per group */ | 
|  | __u32 s_mtime;		/* Mount time */ | 
|  | __u32 s_wtime;		/* Write time */ | 
|  | __u16 s_mnt_count;		/* Mount count */ | 
|  | __s16 s_max_mnt_count;	/* Maximal mount count */ | 
|  | __u16 s_magic;		/* Magic signature */ | 
|  | __u16 s_state;		/* File system state */ | 
|  | __u16 s_errors;		/* Behaviour when detecting errors */ | 
|  | __u16 s_pad; | 
|  | __u32 s_lastcheck;		/* time of last check */ | 
|  | __u32 s_checkinterval;	/* max. time between checks */ | 
|  | __u32 s_creator_os;		/* OS */ | 
|  | __u32 s_rev_level;		/* Revision level */ | 
|  | __u16 s_def_resuid;		/* Default uid for reserved blocks */ | 
|  | __u16 s_def_resgid;		/* Default gid for reserved blocks */ | 
|  | __u32 s_reserved[235];	/* Padding to the end of the block */ | 
|  | }; | 
|  |  | 
|  | struct ext2_group_desc | 
|  | { | 
|  | __u32 bg_block_bitmap;	/* Blocks bitmap block */ | 
|  | __u32 bg_inode_bitmap;	/* Inodes bitmap block */ | 
|  | __u32 bg_inode_table;	/* Inodes table block */ | 
|  | __u16 bg_free_blocks_count;	/* Free blocks count */ | 
|  | __u16 bg_free_inodes_count;	/* Free inodes count */ | 
|  | __u16 bg_used_dirs_count;	/* Directories count */ | 
|  | __u16 bg_pad; | 
|  | __u32 bg_reserved[3]; | 
|  | }; | 
|  |  | 
|  | struct ext2_inode | 
|  | { | 
|  | __u16 i_mode;		/* File mode */ | 
|  | __u16 i_uid;		/* Owner Uid */ | 
|  | __u32 i_size;		/* 4: Size in bytes */ | 
|  | __u32 i_atime;		/* Access time */ | 
|  | __u32 i_ctime;		/* 12: Creation time */ | 
|  | __u32 i_mtime;		/* Modification time */ | 
|  | __u32 i_dtime;		/* 20: Deletion Time */ | 
|  | __u16 i_gid;		/* Group Id */ | 
|  | __u16 i_links_count;	/* 24: Links count */ | 
|  | __u32 i_blocks;		/* Blocks count */ | 
|  | __u32 i_flags;		/* 32: File flags */ | 
|  | union | 
|  | { | 
|  | struct | 
|  | { | 
|  | __u32 l_i_reserved1; | 
|  | } | 
|  | linux1; | 
|  | struct | 
|  | { | 
|  | __u32 h_i_translator; | 
|  | } | 
|  | hurd1; | 
|  | struct | 
|  | { | 
|  | __u32 m_i_reserved1; | 
|  | } | 
|  | masix1; | 
|  | } | 
|  | osd1;			/* OS dependent 1 */ | 
|  | __u32 i_block[EXT2_N_BLOCKS];	/* 40: Pointers to blocks */ | 
|  | __u32 i_version;		/* File version (for NFS) */ | 
|  | __u32 i_file_acl;		/* File ACL */ | 
|  | __u32 i_dir_acl;		/* Directory ACL */ | 
|  | __u32 i_faddr;		/* Fragment address */ | 
|  | 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 */ | 
|  | }; | 
|  |  | 
|  | /* linux/limits.h */ | 
|  | #define NAME_MAX         255	/* # chars in a file name */ | 
|  |  | 
|  | /* linux/posix_type.h */ | 
|  | typedef long linux_off_t; | 
|  |  | 
|  | /* linux/ext2fs.h */ | 
|  | #define EXT2_NAME_LEN 255 | 
|  | struct ext2_dir_entry | 
|  | { | 
|  | __u32 inode;		/* Inode number */ | 
|  | __u16 rec_len;		/* Directory entry length */ | 
|  | __u8 name_len;		/* Name length */ | 
|  | __u8 file_type; | 
|  | char name[EXT2_NAME_LEN];	/* File name */ | 
|  | }; | 
|  |  | 
|  | /* linux/ext2fs.h */ | 
|  | /* | 
|  | * EXT2_DIR_PAD defines the directory entries boundaries | 
|  | * | 
|  | * NOTE: It must be a multiple of 4 | 
|  | */ | 
|  | #define EXT2_DIR_PAD                    4 | 
|  | #define EXT2_DIR_ROUND                  (EXT2_DIR_PAD - 1) | 
|  | #define EXT2_DIR_REC_LEN(name_len)      (((name_len) + 8 + EXT2_DIR_ROUND) & \ | 
|  | ~EXT2_DIR_ROUND) | 
|  |  | 
|  |  | 
|  | /* ext2/super.c */ | 
|  | #define log2(n) ffz(~(n)) | 
|  |  | 
|  | #define EXT2_SUPER_MAGIC      0xEF53	/* include/linux/ext2_fs.h */ | 
|  | #define EXT2_ROOT_INO              2	/* include/linux/ext2_fs.h */ | 
|  | #define PATH_MAX                1024	/* include/linux/limits.h */ | 
|  | #define MAX_LINK_COUNT             5	/* number of symbolic links to follow */ | 
|  |  | 
|  | /* made up, these are pointers into FSYS_BUF */ | 
|  | /* read once, always stays there: */ | 
|  | #define SUPERBLOCK \ | 
|  | ((struct ext2_super_block *)(FSYS_BUF)) | 
|  | #define GROUP_DESC \ | 
|  | ((struct ext2_group_desc *) \ | 
|  | ((int)SUPERBLOCK + sizeof(struct ext2_super_block))) | 
|  | #define INODE \ | 
|  | ((struct ext2_inode *)((int)GROUP_DESC + EXT2_BLOCK_SIZE(SUPERBLOCK))) | 
|  | #define DATABLOCK1 \ | 
|  | ((int)((int)INODE + sizeof(struct ext2_inode))) | 
|  | #define DATABLOCK2 \ | 
|  | ((int)((int)DATABLOCK1 + EXT2_BLOCK_SIZE(SUPERBLOCK))) | 
|  |  | 
|  | /* linux/ext2_fs.h */ | 
|  | #define EXT2_ADDR_PER_BLOCK(s)          (EXT2_BLOCK_SIZE(s) / sizeof (__u32)) | 
|  | #define EXT2_ADDR_PER_BLOCK_BITS(s)		(log2(EXT2_ADDR_PER_BLOCK(s))) | 
|  |  | 
|  | /* linux/ext2_fs.h */ | 
|  | #define EXT2_BLOCK_SIZE_BITS(s)        ((s)->s_log_block_size + 10) | 
|  | /* kind of from ext2/super.c */ | 
|  | #define EXT2_BLOCK_SIZE(s)	(1 << EXT2_BLOCK_SIZE_BITS(s)) | 
|  | /* linux/ext2fs.h */ | 
|  | #define EXT2_DESC_PER_BLOCK(s) \ | 
|  | (EXT2_BLOCK_SIZE(s) / sizeof (struct ext2_group_desc)) | 
|  | /* linux/stat.h */ | 
|  | #define S_IFMT  00170000 | 
|  | #define S_IFLNK  0120000 | 
|  | #define S_IFREG  0100000 | 
|  | #define S_IFDIR  0040000 | 
|  | #define S_ISLNK(m)	(((m) & S_IFMT) == S_IFLNK) | 
|  | #define S_ISREG(m)      (((m) & S_IFMT) == S_IFREG) | 
|  | #define S_ISDIR(m)      (((m) & S_IFMT) == S_IFDIR) | 
|  |  | 
|  | /* include/asm-i386/bitops.h */ | 
|  | /* | 
|  | * ffz = Find First Zero in word. Undefined if no zero exists, | 
|  | * so code should check against ~0UL first.. | 
|  | */ | 
|  | static __inline__ unsigned long | 
|  | ffz (unsigned long word) | 
|  | { | 
|  | __asm__ ("bsfl %1,%0" | 
|  | :	   "=r" (word) | 
|  | :	   "r" (~word)); | 
|  | return word; | 
|  | } | 
|  |  | 
|  | /* check filesystem types and read superblock into memory buffer */ | 
|  | int | 
|  | ext2fs_mount (void) | 
|  | { | 
|  | int retval = 1; | 
|  |  | 
|  | if ((((current_drive & 0x80) || (current_slice != 0)) | 
|  | && (current_slice != PC_SLICE_TYPE_EXT2FS) | 
|  | && (current_slice != PC_SLICE_TYPE_LINUX_RAID) | 
|  | && (! IS_PC_SLICE_TYPE_BSD_WITH_FS (current_slice, FS_EXT2FS)) | 
|  | && (! IS_PC_SLICE_TYPE_BSD_WITH_FS (current_slice, FS_OTHER))) | 
|  | || part_length < (SBLOCK + (sizeof (struct ext2_super_block) / DEV_BSIZE)) | 
|  | || !devread (SBLOCK, 0, sizeof (struct ext2_super_block), | 
|  | (char *) SUPERBLOCK) | 
|  | || SUPERBLOCK->s_magic != EXT2_SUPER_MAGIC) | 
|  | retval = 0; | 
|  |  | 
|  | return retval; | 
|  | } | 
|  |  | 
|  | /* Takes a file system block number and reads it into BUFFER. */ | 
|  | static int | 
|  | ext2_rdfsb (int fsblock, int buffer) | 
|  | { | 
|  | #ifdef E2DEBUG | 
|  | printf ("fsblock %d buffer %d\n", fsblock, buffer); | 
|  | #endif /* E2DEBUG */ | 
|  | return devread (fsblock * (EXT2_BLOCK_SIZE (SUPERBLOCK) / DEV_BSIZE), 0, | 
|  | EXT2_BLOCK_SIZE (SUPERBLOCK), (char *) buffer); | 
|  | } | 
|  |  | 
|  | /* from | 
|  | ext2/inode.c:ext2_bmap() | 
|  | */ | 
|  | /* Maps LOGICAL_BLOCK (the file offset divided by the blocksize) into | 
|  | a physical block (the location in the file system) via an inode. */ | 
|  | static int | 
|  | ext2fs_block_map (int logical_block) | 
|  | { | 
|  |  | 
|  | #ifdef E2DEBUG | 
|  | unsigned char *i; | 
|  | for (i = (unsigned char *) INODE; | 
|  | i < ((unsigned char *) INODE + sizeof (struct ext2_inode)); | 
|  | i++) | 
|  | { | 
|  | printf ("%c", "0123456789abcdef"[*i >> 4]); | 
|  | printf ("%c", "0123456789abcdef"[*i % 16]); | 
|  | if (!((i + 1 - (unsigned char *) INODE) % 16)) | 
|  | { | 
|  | printf ("\n"); | 
|  | } | 
|  | else | 
|  | { | 
|  | printf (" "); | 
|  | } | 
|  | } | 
|  | printf ("logical block %d\n", logical_block); | 
|  | #endif /* E2DEBUG */ | 
|  |  | 
|  | /* if it is directly pointed to by the inode, return that physical addr */ | 
|  | if (logical_block < EXT2_NDIR_BLOCKS) | 
|  | { | 
|  | #ifdef E2DEBUG | 
|  | printf ("returning %d\n", (unsigned char *) (INODE->i_block[logical_block])); | 
|  | printf ("returning %d\n", INODE->i_block[logical_block]); | 
|  | #endif /* E2DEBUG */ | 
|  | return INODE->i_block[logical_block]; | 
|  | } | 
|  | /* else */ | 
|  | logical_block -= EXT2_NDIR_BLOCKS; | 
|  | /* try the indirect block */ | 
|  | if (logical_block < EXT2_ADDR_PER_BLOCK (SUPERBLOCK)) | 
|  | { | 
|  | if (mapblock1 != 1 | 
|  | && !ext2_rdfsb (INODE->i_block[EXT2_IND_BLOCK], DATABLOCK1)) | 
|  | { | 
|  | errnum = ERR_FSYS_CORRUPT; | 
|  | return -1; | 
|  | } | 
|  | mapblock1 = 1; | 
|  | return ((__u32 *) DATABLOCK1)[logical_block]; | 
|  | } | 
|  | /* else */ | 
|  | logical_block -= EXT2_ADDR_PER_BLOCK (SUPERBLOCK); | 
|  | /* now try the double indirect block */ | 
|  | if (logical_block < (1 << (EXT2_ADDR_PER_BLOCK_BITS (SUPERBLOCK) * 2))) | 
|  | { | 
|  | int bnum; | 
|  | if (mapblock1 != 2 | 
|  | && !ext2_rdfsb (INODE->i_block[EXT2_DIND_BLOCK], DATABLOCK1)) | 
|  | { | 
|  | errnum = ERR_FSYS_CORRUPT; | 
|  | return -1; | 
|  | } | 
|  | mapblock1 = 2; | 
|  | if ((bnum = (((__u32 *) DATABLOCK1) | 
|  | [logical_block >> EXT2_ADDR_PER_BLOCK_BITS (SUPERBLOCK)])) | 
|  | != mapblock2 | 
|  | && !ext2_rdfsb (bnum, DATABLOCK2)) | 
|  | { | 
|  | errnum = ERR_FSYS_CORRUPT; | 
|  | return -1; | 
|  | } | 
|  | mapblock2 = bnum; | 
|  | return ((__u32 *) DATABLOCK2) | 
|  | [logical_block & (EXT2_ADDR_PER_BLOCK (SUPERBLOCK) - 1)]; | 
|  | } | 
|  | /* else */ | 
|  | mapblock2 = -1; | 
|  | logical_block -= (1 << (EXT2_ADDR_PER_BLOCK_BITS (SUPERBLOCK) * 2)); | 
|  | if (mapblock1 != 3 | 
|  | && !ext2_rdfsb (INODE->i_block[EXT2_TIND_BLOCK], DATABLOCK1)) | 
|  | { | 
|  | errnum = ERR_FSYS_CORRUPT; | 
|  | return -1; | 
|  | } | 
|  | mapblock1 = 3; | 
|  | if (!ext2_rdfsb (((__u32 *) DATABLOCK1) | 
|  | [logical_block >> (EXT2_ADDR_PER_BLOCK_BITS (SUPERBLOCK) | 
|  | * 2)], | 
|  | DATABLOCK2)) | 
|  | { | 
|  | errnum = ERR_FSYS_CORRUPT; | 
|  | return -1; | 
|  | } | 
|  | if (!ext2_rdfsb (((__u32 *) DATABLOCK2) | 
|  | [(logical_block >> EXT2_ADDR_PER_BLOCK_BITS (SUPERBLOCK)) | 
|  | & (EXT2_ADDR_PER_BLOCK (SUPERBLOCK) - 1)], | 
|  | DATABLOCK2)) | 
|  | { | 
|  | errnum = ERR_FSYS_CORRUPT; | 
|  | return -1; | 
|  | } | 
|  | return ((__u32 *) DATABLOCK2) | 
|  | [logical_block & (EXT2_ADDR_PER_BLOCK (SUPERBLOCK) - 1)]; | 
|  | } | 
|  |  | 
|  | /* preconditions: all preconds of ext2fs_block_map */ | 
|  | int | 
|  | ext2fs_read (char *buf, int len) | 
|  | { | 
|  | int logical_block; | 
|  | int offset; | 
|  | int map; | 
|  | int ret = 0; | 
|  | int size = 0; | 
|  |  | 
|  | #ifdef E2DEBUG | 
|  | static char hexdigit[] = "0123456789abcdef"; | 
|  | unsigned char *i; | 
|  | for (i = (unsigned char *) INODE; | 
|  | i < ((unsigned char *) INODE + sizeof (struct ext2_inode)); | 
|  | i++) | 
|  | { | 
|  | printf ("%c", hexdigit[*i >> 4]); | 
|  | printf ("%c", hexdigit[*i % 16]); | 
|  | if (!((i + 1 - (unsigned char *) INODE) % 16)) | 
|  | { | 
|  | printf ("\n"); | 
|  | } | 
|  | else | 
|  | { | 
|  | printf (" "); | 
|  | } | 
|  | } | 
|  | #endif /* E2DEBUG */ | 
|  | while (len > 0) | 
|  | { | 
|  | /* find the (logical) block component of our location */ | 
|  | logical_block = filepos >> EXT2_BLOCK_SIZE_BITS (SUPERBLOCK); | 
|  | offset = filepos & (EXT2_BLOCK_SIZE (SUPERBLOCK) - 1); | 
|  | map = ext2fs_block_map (logical_block); | 
|  | #ifdef E2DEBUG | 
|  | printf ("map=%d\n", map); | 
|  | #endif /* E2DEBUG */ | 
|  | if (map < 0) | 
|  | break; | 
|  |  | 
|  | size = EXT2_BLOCK_SIZE (SUPERBLOCK); | 
|  | size -= offset; | 
|  | if (size > len) | 
|  | size = len; | 
|  |  | 
|  | disk_read_func = disk_read_hook; | 
|  |  | 
|  | devread (map * (EXT2_BLOCK_SIZE (SUPERBLOCK) / DEV_BSIZE), | 
|  | offset, size, buf); | 
|  |  | 
|  | disk_read_func = NULL; | 
|  |  | 
|  | buf += size; | 
|  | len -= size; | 
|  | filepos += size; | 
|  | ret += size; | 
|  | } | 
|  |  | 
|  | if (errnum) | 
|  | ret = 0; | 
|  |  | 
|  | return ret; | 
|  | } | 
|  |  | 
|  |  | 
|  | /* Based on: | 
|  | def_blk_fops points to | 
|  | blkdev_open, which calls (I think): | 
|  | sys_open() | 
|  | do_open() | 
|  | open_namei() | 
|  | dir_namei() which accesses current->fs->root | 
|  | fs->root was set during original mount: | 
|  | (something)... which calls (I think): | 
|  | ext2_read_super() | 
|  | iget() | 
|  | __iget() | 
|  | read_inode() | 
|  | ext2_read_inode() | 
|  | uses desc_per_block_bits, which is set in ext2_read_super() | 
|  | also uses group descriptors loaded during ext2_read_super() | 
|  | lookup() | 
|  | ext2_lookup() | 
|  | ext2_find_entry() | 
|  | ext2_getblk() | 
|  |  | 
|  | */ | 
|  |  | 
|  | /* preconditions: ext2fs_mount already executed, therefore supblk in buffer | 
|  | *   known as SUPERBLOCK | 
|  | * returns: 0 if error, nonzero iff we were able to find the file successfully | 
|  | * postconditions: on a nonzero return, buffer known as INODE contains the | 
|  | *   inode of the file we were trying to look up | 
|  | * side effects: messes up GROUP_DESC buffer area | 
|  | */ | 
|  | int | 
|  | ext2fs_dir (char *dirname) | 
|  | { | 
|  | int current_ino = EXT2_ROOT_INO;	/* start at the root */ | 
|  | int updir_ino = current_ino;	/* the parent of the current directory */ | 
|  | int group_id;			/* which group the inode is in */ | 
|  | int group_desc;		/* fs pointer to that group */ | 
|  | int desc;			/* index within that group */ | 
|  | int ino_blk;			/* fs pointer of the inode's information */ | 
|  | int str_chk;			/* used to hold the results of a string compare */ | 
|  | struct ext2_group_desc *gdp; | 
|  | struct ext2_inode *raw_inode;	/* inode info corresponding to current_ino */ | 
|  |  | 
|  | char linkbuf[PATH_MAX];	/* buffer for following symbolic links */ | 
|  | int link_count = 0; | 
|  |  | 
|  | char *rest; | 
|  | char ch;			/* temp char holder */ | 
|  |  | 
|  | int off;			/* offset within block of directory entry (off mod blocksize) */ | 
|  | int loc;			/* location within a directory */ | 
|  | int blk;			/* which data blk within dir entry (off div blocksize) */ | 
|  | long map;			/* fs pointer of a particular block from dir entry */ | 
|  | struct ext2_dir_entry *dp;	/* pointer to directory entry */ | 
|  | #ifdef E2DEBUG | 
|  | unsigned char *i; | 
|  | #endif	/* E2DEBUG */ | 
|  |  | 
|  | /* loop invariants: | 
|  | current_ino = inode to lookup | 
|  | dirname = pointer to filename component we are cur looking up within | 
|  | the directory known pointed to by current_ino (if any) | 
|  | */ | 
|  |  | 
|  | while (1) | 
|  | { | 
|  | #ifdef E2DEBUG | 
|  | printf ("inode %d\n", current_ino); | 
|  | printf ("dirname=%s\n", dirname); | 
|  | #endif /* E2DEBUG */ | 
|  |  | 
|  | /* look up an inode */ | 
|  | group_id = (current_ino - 1) / (SUPERBLOCK->s_inodes_per_group); | 
|  | group_desc = group_id >> log2 (EXT2_DESC_PER_BLOCK (SUPERBLOCK)); | 
|  | desc = group_id & (EXT2_DESC_PER_BLOCK (SUPERBLOCK) - 1); | 
|  | #ifdef E2DEBUG | 
|  | printf ("ipg=%d, dpb=%d\n", SUPERBLOCK->s_inodes_per_group, | 
|  | EXT2_DESC_PER_BLOCK (SUPERBLOCK)); | 
|  | printf ("group_id=%d group_desc=%d desc=%d\n", group_id, group_desc, desc); | 
|  | #endif /* E2DEBUG */ | 
|  | if (!ext2_rdfsb ( | 
|  | (WHICH_SUPER + group_desc + SUPERBLOCK->s_first_data_block), | 
|  | (int) GROUP_DESC)) | 
|  | { | 
|  | return 0; | 
|  | } | 
|  | gdp = GROUP_DESC; | 
|  | ino_blk = gdp[desc].bg_inode_table + | 
|  | (((current_ino - 1) % (SUPERBLOCK->s_inodes_per_group)) | 
|  | >> log2 (EXT2_BLOCK_SIZE (SUPERBLOCK) / sizeof (struct ext2_inode))); | 
|  | #ifdef E2DEBUG | 
|  | printf ("inode table fsblock=%d\n", ino_blk); | 
|  | #endif /* E2DEBUG */ | 
|  | if (!ext2_rdfsb (ino_blk, (int) INODE)) | 
|  | { | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* reset indirect blocks! */ | 
|  | mapblock2 = mapblock1 = -1; | 
|  |  | 
|  | raw_inode = INODE + | 
|  | ((current_ino - 1) | 
|  | & (EXT2_BLOCK_SIZE (SUPERBLOCK) / sizeof (struct ext2_inode) - 1)); | 
|  | #ifdef E2DEBUG | 
|  | printf ("ipb=%d, sizeof(inode)=%d\n", | 
|  | (EXT2_BLOCK_SIZE (SUPERBLOCK) / sizeof (struct ext2_inode)), | 
|  | sizeof (struct ext2_inode)); | 
|  | printf ("inode=%x, raw_inode=%x\n", INODE, raw_inode); | 
|  | printf ("offset into inode table block=%d\n", (int) raw_inode - (int) INODE); | 
|  | for (i = (unsigned char *) INODE; i <= (unsigned char *) raw_inode; | 
|  | i++) | 
|  | { | 
|  | printf ("%c", "0123456789abcdef"[*i >> 4]); | 
|  | printf ("%c", "0123456789abcdef"[*i % 16]); | 
|  | if (!((i + 1 - (unsigned char *) INODE) % 16)) | 
|  | { | 
|  | printf ("\n"); | 
|  | } | 
|  | else | 
|  | { | 
|  | printf (" "); | 
|  | } | 
|  | } | 
|  | printf ("first word=%x\n", *((int *) raw_inode)); | 
|  | #endif /* E2DEBUG */ | 
|  |  | 
|  | /* copy inode to fixed location */ | 
|  | memmove ((void *) INODE, (void *) raw_inode, sizeof (struct ext2_inode)); | 
|  |  | 
|  | #ifdef E2DEBUG | 
|  | printf ("first word=%x\n", *((int *) INODE)); | 
|  | #endif /* E2DEBUG */ | 
|  |  | 
|  | /* If we've got a symbolic link, then chase it. */ | 
|  | if (S_ISLNK (INODE->i_mode)) | 
|  | { | 
|  | int len; | 
|  | if (++link_count > MAX_LINK_COUNT) | 
|  | { | 
|  | errnum = ERR_SYMLINK_LOOP; | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* Find out how long our remaining name is. */ | 
|  | len = 0; | 
|  | while (dirname[len] && !isspace (dirname[len])) | 
|  | len++; | 
|  |  | 
|  | /* Get the symlink size. */ | 
|  | filemax = (INODE->i_size); | 
|  | if (filemax + len > sizeof (linkbuf) - 2) | 
|  | { | 
|  | errnum = ERR_FILELENGTH; | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | if (len) | 
|  | { | 
|  | /* Copy the remaining name to the end of the symlink data. | 
|  | Note that DIRNAME and LINKBUF may overlap! */ | 
|  | memmove (linkbuf + filemax, dirname, len); | 
|  | } | 
|  | linkbuf[filemax + len] = '\0'; | 
|  |  | 
|  | /* Read the symlink data. */ | 
|  | if (INODE->i_blocks) | 
|  | { | 
|  | /* Read the necessary blocks, and reset the file pointer. */ | 
|  | len = grub_read (linkbuf, filemax); | 
|  | filepos = 0; | 
|  | if (!len) | 
|  | return 0; | 
|  | } | 
|  | else | 
|  | { | 
|  | /* Copy the data directly from the inode. */ | 
|  | len = filemax; | 
|  | memmove (linkbuf, (char *) INODE->i_block, len); | 
|  | } | 
|  |  | 
|  | #ifdef E2DEBUG | 
|  | printf ("symlink=%s\n", linkbuf); | 
|  | #endif | 
|  |  | 
|  | dirname = linkbuf; | 
|  | if (*dirname == '/') | 
|  | { | 
|  | /* It's an absolute link, so look it up in root. */ | 
|  | current_ino = EXT2_ROOT_INO; | 
|  | updir_ino = current_ino; | 
|  | } | 
|  | else | 
|  | { | 
|  | /* Relative, so look it up in our parent directory. */ | 
|  | current_ino = updir_ino; | 
|  | } | 
|  |  | 
|  | /* Try again using the new name. */ | 
|  | continue; | 
|  | } | 
|  |  | 
|  | /* if end of filename, INODE points to the file's inode */ | 
|  | if (!*dirname || isspace (*dirname)) | 
|  | { | 
|  | if (!S_ISREG (INODE->i_mode)) | 
|  | { | 
|  | errnum = ERR_BAD_FILETYPE; | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | filemax = (INODE->i_size); | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | /* else we have to traverse a directory */ | 
|  | updir_ino = current_ino; | 
|  |  | 
|  | /* skip over slashes */ | 
|  | while (*dirname == '/') | 
|  | dirname++; | 
|  |  | 
|  | /* if this isn't a directory of sufficient size to hold our file, abort */ | 
|  | if (!(INODE->i_size) || !S_ISDIR (INODE->i_mode)) | 
|  | { | 
|  | errnum = ERR_BAD_FILETYPE; | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* skip to next slash or end of filename (space) */ | 
|  | for (rest = dirname; (ch = *rest) && !isspace (ch) && ch != '/'; | 
|  | rest++); | 
|  |  | 
|  | /* look through this directory and find the next filename component */ | 
|  | /* invariant: rest points to slash after the next filename component */ | 
|  | *rest = 0; | 
|  | loc = 0; | 
|  |  | 
|  | do | 
|  | { | 
|  |  | 
|  | #ifdef E2DEBUG | 
|  | printf ("dirname=%s, rest=%s, loc=%d\n", dirname, rest, loc); | 
|  | #endif /* E2DEBUG */ | 
|  |  | 
|  | /* if our location/byte offset into the directory exceeds the size, | 
|  | give up */ | 
|  | if (loc >= INODE->i_size) | 
|  | { | 
|  | if (print_possibilities < 0) | 
|  | { | 
|  | # if 0 | 
|  | putchar ('\n'); | 
|  | # endif | 
|  | } | 
|  | else | 
|  | { | 
|  | errnum = ERR_FILE_NOT_FOUND; | 
|  | *rest = ch; | 
|  | } | 
|  | return (print_possibilities < 0); | 
|  | } | 
|  |  | 
|  | /* else, find the (logical) block component of our location */ | 
|  | blk = loc >> EXT2_BLOCK_SIZE_BITS (SUPERBLOCK); | 
|  |  | 
|  | /* we know which logical block of the directory entry we are looking | 
|  | for, now we have to translate that to the physical (fs) block on | 
|  | the disk */ | 
|  | map = ext2fs_block_map (blk); | 
|  | #ifdef E2DEBUG | 
|  | printf ("fs block=%d\n", map); | 
|  | #endif /* E2DEBUG */ | 
|  | mapblock2 = -1; | 
|  | if ((map < 0) || !ext2_rdfsb (map, DATABLOCK2)) | 
|  | { | 
|  | errnum = ERR_FSYS_CORRUPT; | 
|  | *rest = ch; | 
|  | return 0; | 
|  | } | 
|  | off = loc & (EXT2_BLOCK_SIZE (SUPERBLOCK) - 1); | 
|  | dp = (struct ext2_dir_entry *) (DATABLOCK2 + off); | 
|  | /* advance loc prematurely to next on-disk directory entry  */ | 
|  | loc += dp->rec_len; | 
|  |  | 
|  | /* NOTE: ext2fs filenames are NOT null-terminated */ | 
|  |  | 
|  | #ifdef E2DEBUG | 
|  | printf ("directory entry ino=%d\n", dp->inode); | 
|  | if (dp->inode) | 
|  | printf ("entry=%s\n", dp->name); | 
|  | #endif /* E2DEBUG */ | 
|  |  | 
|  | if (dp->inode) | 
|  | { | 
|  | int saved_c = dp->name[dp->name_len]; | 
|  |  | 
|  | dp->name[dp->name_len] = 0; | 
|  | str_chk = substring (dirname, dp->name); | 
|  |  | 
|  | # ifndef STAGE1_5 | 
|  | if (print_possibilities && ch != '/' | 
|  | && (!*dirname || str_chk <= 0)) | 
|  | { | 
|  | if (print_possibilities > 0) | 
|  | print_possibilities = -print_possibilities; | 
|  | print_a_completion (dp->name); | 
|  | } | 
|  | # endif | 
|  |  | 
|  | dp->name[dp->name_len] = saved_c; | 
|  | } | 
|  |  | 
|  | } | 
|  | while (!dp->inode || (str_chk || (print_possibilities && ch != '/'))); | 
|  |  | 
|  | current_ino = dp->inode; | 
|  | *(dirname = rest) = ch; | 
|  | } | 
|  | /* never get here */ | 
|  | } | 
|  |  | 
|  | #endif /* FSYS_EXT2_FS */ |