ext2fs.c

来自「EFI(Extensible Firmware Interface)是下一代BI」· C语言 代码 · 共 1,005 行 · 第 1/2 页

C
1,005
字号
/* *  Copyright (C) 2001-2003 Hewlett-Packard Co. *	Contributed by Stephane Eranian <eranian@hpl.hp.com> * * This file is part of the ELILO, the EFI Linux boot loader. * *  ELILO 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, or (at your option) *  any later version. * *  ELILO 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 ELILO; see the file COPYING.  If not, write to the Free *  Software Foundation, 59 Temple Place - Suite 330, Boston, MA *  02111-1307, USA. * * Please check out the elilo.txt for complete documentation on how * to use this program. * * The ext2 code in this file is derived from aboot-0.7 (the Linux/Alpha * bootloader) and credits are attributed to: * * This file has been ported from the DEC 32-bit Linux version * by David Mosberger (davidm@cs.arizona.edu). */#include <efi.h>#include <efilib.h>#include "elilo.h"#define FS_NAME L"ext2fs"#ifdef PATH_MAX#error You must have included some Linux header file by error#endif#define PATH_MAX		4095#define EXT2FS_PATH_MAXLEN	PATH_MAX#include "fs/ext2fs.h"#include "fs/ext2_private.h"/* * should we decide to spin off ext2, let's say to a boottime driver * we would have to change this */#define EXT2FS_MEMTYPE	EfiLoaderData/* ext2 file size is __u32 */#define EXT2_FILESIZE_MAX	(0x100000000UL)/*  * Number of simultaneous open files. This needs to be high because * directories are kept open while traversing long path names. */#define MAX_OPEN_FILES		32 typedef struct inode_table_entry {	struct	ext2_inode	inode;	int			inumber;	int			free;	unsigned short		old_mode;	UINT32			pos;	/* current position in file ext2 uses __u32 !*/} inode_entry_t;typedef struct {	struct ext2_super_block sb;	struct ext2_group_desc *gds;	struct ext2_inode *root_inode;	int ngroups;	int directlim;			/* Maximum direct blkno */	int ind1lim;			/* Maximum single-indir blkno */	int ind2lim;			/* Maximum double-indir blkno */	int ptrs_per_blk;		/* ptrs/indirect block */	CHAR8 blkbuf[EXT2_MAX_BLOCK_SIZE];	int cached_iblkno;	CHAR8 iblkbuf[EXT2_MAX_BLOCK_SIZE];	int cached_diblkno;	CHAR8 diblkbuf[EXT2_MAX_BLOCK_SIZE];	long blocksize;	inode_entry_t inode_table[MAX_OPEN_FILES];	/* fields added to fit the protocol construct */	EFI_BLOCK_IO *blkio;	UINT32	     mediaid;	EFI_HANDLE   dev;} ext2fs_priv_state_t;typedef union {	ext2fs_interface_t pub_intf;	struct {		ext2fs_interface_t  pub_intf;		ext2fs_priv_state_t priv_data;	} ext2fs_priv;} ext2fs_t;#define FS_PRIVATE(n)	(&(((ext2fs_t *)n)->ext2fs_priv.priv_data))typedef union {	EFI_HANDLE *dev;	ext2fs_t  *intf;} dev_tab_t;static dev_tab_t *dev_tab;	/* holds all devices we found */static UINTN ndev;		/* how many entries in dev_tab */static EFI_GUID Ext2FsProtocol = EXT2FS_PROTOCOL;static INTNread_bytes(EFI_BLOCK_IO *blkio, UINT32 mediaid, UINTN offset, VOID *addr, UINTN size){	EFI_STATUS status;	UINT8 *buffer;	UINTN count, buffer_size;	EFI_LBA base;	INTN ret  = EFI_INVALID_PARAMETER;	base        = offset / blkio->Media->BlockSize;	count       = (size + blkio->Media->BlockSize -1) / blkio->Media->BlockSize;	buffer_size = count * blkio->Media->BlockSize;	DBG_PRT((L"readblock(%x, %d,%d) base=%d count=%d", blkio, offset, size, base, count));	/*	 * slow but avoid large buffer on the stack	 */	buffer = (UINT8 *)alloc(buffer_size, EfiLoaderData);	if (buffer == NULL) {		ERR_PRT((L"cannot allocate ext2fs buffer size=%d", buffer_size));		return ret;	}	DBG_PRT((L"readblock(%x, %d, %d, %d, %x)", blkio, mediaid, base, buffer_size, buffer));	status = blkio->ReadBlocks(blkio, mediaid, base, buffer_size, buffer); 	if (EFI_ERROR(status)) {		ERR_PRT((L"readblock(%d,%d)=%r", base, buffer_size, status));		goto error;	}	DBG_PRT((L"readblock(%d,%d)->%r", offset, buffer_size, status));	Memcpy(addr, buffer+(offset-(base*blkio->Media->BlockSize)), size);	ret = 0;error:	free(buffer);	return ret;}/* * 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(ext2fs_priv_state_t *e2fs, int ino){	int i;	struct ext2_inode *ip;	struct inode_table_entry *itp = 0;	int group;	long offset;	ip = 0;	for (i = 0; i < MAX_OPEN_FILES; i++) {		DBG_PRT((L"ext2_iget: looping, entry %d inode %d free %d",		       i, e2fs->inode_table[i].inumber, e2fs->inode_table[i].free));		if (e2fs->inode_table[i].free) {			itp = &e2fs->inode_table[i];			ip = &itp->inode;			break;		}	}	if (!ip) {		ERR_PRT((L"ext2_iget: no free inodes"));		return NULL;	}	group = (ino-1) / e2fs->sb.s_inodes_per_group;	DBG_PRT((L" itp-inode_table=%d bg_inode_table=%d  group=%d ino=%d\n", (UINTN)(itp-e2fs->inode_table),			(UINTN)(e2fs->gds[group].bg_inode_table), (UINTN)group, (UINTN)ino));	offset = ((long) e2fs->gds[group].bg_inode_table * e2fs->blocksize)		+ (((ino - 1) % EXT2_INODES_PER_GROUP(&e2fs->sb)) * EXT2_INODE_SIZE(&e2fs->sb));		DBG_PRT((L"ext2_iget: reading %d bytes at offset %d"	       " ((%d * %d) + ((%d) %% %d) * %d) "	       "(inode %d -> table %d)", 	       sizeof(struct ext2_inode), 	       (UINTN)offset,	       (UINTN)e2fs->gds[group].bg_inode_table, (UINTN)e2fs->blocksize,	       (UINTN)(ino - 1), (UINTN)EXT2_INODES_PER_GROUP(&e2fs->sb), EXT2_INODE_SIZE(&e2fs->sb),	       (UINTN)ino, (UINTN) (itp - e2fs->inode_table)));	if (read_bytes(e2fs->blkio, e2fs->mediaid, offset, ip, sizeof(struct ext2_inode))) {		ERR_PRT((L"ext2_iget: read error"));		return NULL;	}		DBG_PRT((L"mode=%x uid=%d size=%d gid=%d links=%d flags=%d",					(UINTN)ip->i_mode,					(UINTN)ip->i_uid,					(UINTN)ip->i_size,					(UINTN)ip->i_gid,					(UINTN)ip->i_flags));	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(ext2fs_priv_state_t *e2fs, struct ext2_inode *ip){	struct inode_table_entry *itp;	/* Find and free the inode table slot we used... */	itp = (struct inode_table_entry *)ip;	DBG_PRT((L"ext2_iput: inode %d table %d", itp->inumber, (int) (itp - e2fs->inode_table)));	itp->inumber = 0;	itp->free = 1;}/* * 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(ext2fs_priv_state_t *e2fs, struct ext2_inode *ip, int blkoff){	unsigned int *lp;	unsigned int *ilp;	unsigned int *dlp;	int blkno;	int iblkno;	int diblkno;	long offset;	ilp = (unsigned int *)e2fs->iblkbuf;	dlp = (unsigned int *)e2fs->diblkbuf;	lp = (unsigned int *)e2fs->blkbuf;	/* If it's a direct block, it's easy! */	if (blkoff <= e2fs->directlim) {		return ip->i_block[blkoff];	}	/* Is it a single-indirect? */	if (blkoff <= e2fs->ind1lim) {		iblkno = ip->i_block[EXT2_IND_BLOCK];		if (iblkno == 0) {			return 0;		}		/* Read the indirect block */		if (e2fs->cached_iblkno != iblkno) {			offset = iblkno * e2fs->blocksize;			if (read_bytes(e2fs->blkio, e2fs->mediaid, offset, e2fs->iblkbuf, e2fs->blocksize)) {				ERR_PRT((L"ext2_blkno: error on iblk read"));				return 0;			}			e2fs->cached_iblkno = iblkno;		}		blkno = ilp[blkoff-(e2fs->directlim+1)];		return blkno;	}	/* Is it a double-indirect? */	if (blkoff <= e2fs->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 (e2fs->cached_diblkno != diblkno) {			offset = diblkno * e2fs->blocksize;			if (read_bytes(e2fs->blkio, e2fs->mediaid, offset,  e2fs->diblkbuf, e2fs->blocksize)) {				ERR_PRT((L"ext2_blkno: err reading dindr blk"));				return 0;			}			e2fs->cached_diblkno = diblkno;		}		/* Find the single-indirect block pointer ... */		iblkno = dlp[(blkoff - (e2fs->ind1lim+1)) / e2fs->ptrs_per_blk];		if (iblkno == 0) {			return 0;		}		/* Read the indirect block */    		if (e2fs->cached_iblkno != iblkno) {			offset = iblkno * e2fs->blocksize;			if (read_bytes(e2fs->blkio, e2fs->mediaid, offset, e2fs->iblkbuf, e2fs->blocksize)) {				ERR_PRT((L"ext2_blkno: err on iblk read"));				return 0;			}			e2fs->cached_iblkno = iblkno;		}		/* Find the block itself. */		blkno = ilp[(blkoff-(e2fs->ind1lim+1)) % e2fs->ptrs_per_blk];		return blkno;	}	if (blkoff > e2fs->ind2lim) {		ERR_PRT((L"ext2_blkno: block number too large: %d", blkoff));		return 0;	}	return -1;}static intext2_breadi(ext2fs_priv_state_t *e2fs, struct ext2_inode *ip, long blkno, long nblks, CHAR8 *buffer){ 	long dev_blkno, ncontig, offset, nbytes, tot_bytes;	tot_bytes = 0;	if ((blkno+nblks)*e2fs->blocksize > ip->i_size)		nblks = (ip->i_size + e2fs->blocksize) / e2fs->blocksize - blkno;	while (nblks) {		/*		 * Contiguous reads are a lot faster, so we try to group		 * as many blocks as possible:		 */		ncontig = 0; nbytes = 0;		dev_blkno = ext2_blkno(e2fs,ip, blkno);		do {			++blkno; ++ncontig; --nblks;			nbytes += e2fs->blocksize;		} while (nblks &&			 ext2_blkno(e2fs, ip, blkno) == dev_blkno + ncontig);		if (dev_blkno == 0) {			/* This is a "hole" */			Memset(buffer, 0, nbytes);		} else {			/* Read it for real */			offset = dev_blkno*e2fs->blocksize;			DBG_PRT((L"ext2_bread: reading %d bytes at offset %d", nbytes, offset));			if (read_bytes(e2fs->blkio, e2fs->mediaid, offset, buffer, nbytes)) {				ERR_PRT((L"ext2_bread: read error"));				return -1;			}		}		buffer    += nbytes;		tot_bytes += nbytes;	}	return tot_bytes;}static struct ext2_dir_entry_2 *ext2_readdiri(ext2fs_priv_state_t *e2fs, 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(e2fs, dir_inode, 0, 1, e2fs->blkbuf) < 0)			return NULL;	}	DBG_PRT((L"ext2_readdiri: blkoffset %d diroffset %d len %d", blockoffset, diroffset, dir_inode->i_size));	if (blockoffset >= e2fs->blocksize) {		diroffset += e2fs->blocksize;		if (diroffset >= dir_inode->i_size)			return NULL;		ERR_PRT((L"ext2_readdiri: reading block at %d", diroffset));		/* assume that this will read the whole block */		if (ext2_breadi(e2fs, dir_inode, diroffset / e2fs->blocksize, 1, e2fs->blkbuf) < 0) return NULL;		blockoffset = 0;	}	dp = (struct ext2_dir_entry_2 *) (e2fs->blkbuf + blockoffset);	blockoffset += dp->rec_len;#if 0	Print(L"ext2_readdiri: returning %x = ");	{ INTN i; for(i=0; i < dp->name_len; i++) Print(L"%c", (CHAR16)dp->name[i]); Print(L"\n");}#endif	return dp;}/* * the string 'name' is modified by this call as per the parsing that * is done in strtok_simple() */static struct ext2_inode *ext2_namei(ext2fs_priv_state_t *e2fs, CHAR8 *name){	CHAR8 *component;	struct ext2_inode *dir_inode;	struct ext2_dir_entry_2 *dp;	int next_ino;	/* start at the root: */	if (!e2fs->root_inode)		e2fs->root_inode = ext2_iget(e2fs, EXT2_ROOT_INO);	dir_inode = e2fs->root_inode;	if (!dir_inode)	  return NULL;	component = strtok_simple(name, '/');	while (component) {		int component_length;		int rewind = 0;		/*		 * Search for the specified component in the current		 * directory inode.		 */		next_ino = -1;		component_length = strlena(component);		DBG_PRT((L"ext2_namei: component = %a", component));		/* rewind the first time through */		while ((dp = ext2_readdiri(e2fs, dir_inode, !rewind++))) {			if ((dp->name_len == component_length) &&			    (strncmpa(component, dp->name,				     component_length) == 0))			{				/* Found it! */				DBG_PRT((L"ext2_namei: found entry %a", component));				next_ino = dp->inode;				break;			}			DBG_PRT((L"ext2_namei: looping"));		}			DBG_PRT((L"ext2_namei: next_ino = %d", next_ino));		/*		 * At this point, we're done with this directory whether		 * we've succeeded or failed...		 */		if (dir_inode != e2fs->root_inode) ext2_iput(e2fs, 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(e2fs, next_ino);		if (!dir_inode)		  return NULL;		component = strtok_simple(NULL, '/');	}	/*	 * If we get here, then we got through all the components.

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?