There was a slight probem with reading 1 byte fragments as is done with menu.lst, that 
has been fixed, as well as some simple caching for such reads.
/*
 *  GRUB  --  GRand Unified Bootloader
 *  Copyright (C) 2002 Russ Dill <[EMAIL PROTECTED]>
 *
 *  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.
 */
 
/* fsys_minix.c used as a skeleton, cramfs code in kernel used as
 * documentation and some code */

#include "shared.h"
#include "filesys.h"
#include "mini_inflate.h"

//#define DEBUG_CRAMFS

#ifdef DEBUG_CRAMFS
# define debug_cramfs(str, args...) printf(str, ## args)
#else
# define debug_cramfs(str, args...) do {;} while(0)
#endif

/* include/asm-i386/type.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;


#define BLOCK_SIZE 	SECTOR_SIZE

#define CRAMFS_MAGIC		0x28cd3d45	/* some random number */
#define CRAMFS_SIGNATURE	"Compressed ROMFS"

/*
 * Reasonably terse representation of the inode data.
 */
struct cramfs_inode {
	u32 mode:16, uid:16;
	/* SIZE for device files is i_rdev */
	u32 size:24, gid:8;
	/* NAMELEN is the length of the file name, divided by 4 and
           rounded up.  (cramfs doesn't support hard links.) */
	/* OFFSET: For symlinks and non-empty regular files, this
	   contains the offset (divided by 4) of the file data in
	   compressed form (starting with an array of block pointers;
	   see README).  For non-empty directories it is the offset
	   (divided by 4) of the inode of the first file in that
	   directory.  For anything else, offset is zero. */
	u32 namelen:6, offset:26;
};

/*
 * Superblock information at the beginning of the FS.
 */
struct cramfs_super {
	u32 magic;		/* 0x28cd3d45 - random number */
	u32 size;		/* Not used.  mkcramfs currently
                                   writes a constant 1<<16 here. */
	u32 flags;		/* 0 */
	u32 future;		/* 0 */
	u8 signature[16];	/* "Compressed ROMFS" */
	u8 fsid[16];		/* random number */
	u8 name[16];		/* user-defined name */
	struct cramfs_inode root;	/* Root inode data */
};

/*
 * Valid values in super.flags.  Currently we refuse to mount
 * if (flags & ~CRAMFS_SUPPORTED_FLAGS).  Maybe that should be
 * changed to test super.future instead.
 */
#define CRAMFS_SUPPORTED_FLAGS (0xff)

/* Uncompression interfaces to the underlying zlib */
int cramfs_uncompress_block(void *dst, int dstlen, void *src, int srclen);
int cramfs_uncompress_init(void);
int cramfs_uncompress_exit(void);

/* 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)

#define PATH_MAX                1024	/* include/linux/limits.h */
#define MAX_LINK_COUNT             5	/* number of symbolic links to follow */

#define NAMELEN_MAX (((1 << (6 + 1)) - 1) << 2) /* 252 */

#define CRAMFS_BLOCK (4096L)
#define CRAMFS_MAX_BLOCKS ((1 << 24) / CRAMFS_BLOCK)

/* made up, these are pointers into FSYS_BUF */
/* read once, always stays there: */
struct cramfs_buf {
	struct cramfs_super super;
	struct cramfs_inode inode;
	char name[NAMELEN_MAX + 1];
	u32 block_ptrs[CRAMFS_MAX_BLOCKS];
	char data[CRAMFS_BLOCK * 2];
	char temp[CRAMFS_BLOCK];
	/* menu.lst is read 1 byte at a time, try to aleviate *
	 * the performance problem */
	long cached_block;		/* the uncompressed block in cramfs_buf->data */
	long decompressed_block;	/* the decompressed block in cramfs_buf->temp */
	long decompressed_size;		/* the size that is got decompressed to */
};

static struct cramfs_buf *cramfs_buf;

#define CRAMFS_ROOT_INO (sizeof(struct cramfs_super) - sizeof(struct cramfs_inode))

#ifndef STAGE1_5
#define cramfs_memcmp grub_memcmp
#define cramfs_strlen grub_strlen
#else
int
cramfs_memcmp (const char *s1, const char *s2, int n)
{
  while (n)
    {
      if (*s1 < *s2)
	return -1;
      else if (*s1 > *s2)
	return 1;
      s1++;
      s2++;
      n--;
    }

  return 0;
}


int
cramfs_strlen (const char *str)
{
  int len = 0;

  while (*str++)
    len++;

  return len;
}


#endif
/* check filesystem types and read superblock into memory buffer */
int 
cramfs_mount(void)
{	
	debug_cramfs("attempting to mount a cramfs\n");

	cramfs_buf = (struct cramfs_buf *) FSYS_BUF;
	if (part_length < sizeof(struct cramfs_super) / BLOCK_SIZE) {
		debug_cramfs("partition too short\n");
       		return 0;
	}
	  
	if (!devread(0, 0, sizeof(struct cramfs_super), (char *) &cramfs_buf->super)) {
		debug_cramfs("cannot read superblock\n");
		return 0;
	}
  
	if (cramfs_buf->super.magic != CRAMFS_MAGIC) {
		debug_cramfs("magic does not match\n");
		return 0;
	}
	
	if (cramfs_memcmp(CRAMFS_SIGNATURE, cramfs_buf->super.signature, 16)) {
		debug_cramfs("signiture does not match\n");
		return 0;
	}

	if (cramfs_buf->super.flags & ~CRAMFS_SUPPORTED_FLAGS) {
		debug_cramfs("unsupported flags\n");
		return 0;
	}

	if (!S_ISDIR(cramfs_buf->super.root.mode)) {
		debug_cramfs("root is not a directory\n");
		return 0;
	}
	
	debug_cramfs("cramfs mounted\n");
	return 1;
}

/* read from INODE into BUF */
int
cramfs_read (char *buf, int len)
{
	u32 start;
	u32 end;
	int nblocks;
	int block;
	int block_len;
	int ret = 0;
	long size = 0;
	long devread_ret;

	nblocks = (cramfs_buf->inode.size - 1) / CRAMFS_BLOCK + 1;
	block = filepos / CRAMFS_BLOCK;

	if (!devread(0, cramfs_buf->inode.offset << 2, nblocks << 2, (char *) &cramfs_buf->block_ptrs))
		return 0;

	if (block)
		start = cramfs_buf->block_ptrs[block - 1];
	else start = (cramfs_buf->inode.offset + nblocks) << 2;
	
	debug_cramfs("reading a file of %d blocks starting at offset %d (block %d)\n", nblocks, start, block);
	debug_cramfs("filepos is %d\n", filepos);

	while (block < nblocks && len > 0) {
		end = cramfs_buf->block_ptrs[block];
		block_len = end - start;
		
		debug_cramfs("reading to %d bytes at block %d at offset %d, %d bytes...",
			len, block, start, block_len);
		if (cramfs_buf->cached_block != block) {
			disk_read_func = disk_read_hook;
			devread_ret = devread(0, start, block_len, cramfs_buf->data);
			disk_read_func = NULL;
			cramfs_buf->cached_block = block;
		} else debug_cramfs("%d was cached...", block);
		
		if (!ret && (filepos % CRAMFS_BLOCK)) {
			/* its the first read, and its not block aligned */
			debug_cramfs("doing a non-aligned decompression of block %d at offset %d\n", 
					block, filepos % CRAMFS_BLOCK);
			if (cramfs_buf->decompressed_block != block) {
				size = decompress_block(cramfs_buf->temp, 4096,	cramfs_buf->data + 2);
				cramfs_buf->decompressed_size = size;
				cramfs_buf->decompressed_block = block;
			} else size = cramfs_buf->decompressed_size;
			size -= filepos % CRAMFS_BLOCK;
			if (size > len) size = len;
			if (size > 0)
				memcpy(buf, cramfs_buf->temp + (filepos % CRAMFS_BLOCK), size);		
		} else  {
			/* just another full block read */
			size = decompress_block(buf, len, cramfs_buf->data + 2);
		}
		if (size < 0) {
			debug_cramfs("error in decomp (error %d)\n", size);
			cramfs_buf->cached_block = -1;
			cramfs_buf->decompressed_block = -1;
			return 0;
		}
		debug_cramfs("decomp`d %d bytes\n", size);
		buf += size;
		len -= size;
		filepos += size;
		ret += size;

		block++;
		start = end;
	}

	return ret;
}

/* preconditions: cramfs_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: none yet  */
int
cramfs_dir(char *dirname)
{
	int str_chk;			     /* used ot hold the results of a string
					        compare */

	u32 current_ino;  		     /* inode info for current_ino */
	u32 parent_ino;

	char linkbuf[PATH_MAX];	  	   /* buffer for following sym-links */
	int link_count = 0;
	
	char *rest;
	char ch;
	
	u32 dir_size;			     /* size of this directory */
	u32 off;			     /* offset of this directory */
	u32 loc;			     /* location within a directory */
	
	int namelen;

  /* 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) */

#ifdef DEBUG_CRAMFS
	printf("\n");
#endif  

	current_ino = CRAMFS_ROOT_INO;
	parent_ino = current_ino;

	for (;;) {
		debug_cramfs("inode offset %d, dirname %s\n", current_ino, dirname);

		if (!devread(0, current_ino, sizeof(struct cramfs_inode), (char *) &cramfs_buf->inode))
			return 0;

		/* If we've got a symbolic link, then chase it. */
		if (S_ISLNK(cramfs_buf->inode.mode)) {
			int len;

			if (++link_count > MAX_LINK_COUNT) {
				errnum = ERR_SYMLINK_LOOP;
				return 0;
			}
			debug_cramfs("S_ISLNK(%s)\n", dirname);

			/* Find out how long our remaining name is. */
			len = 0;
			while (dirname[len] && !isspace(dirname[len]))
				len++;

			/* Get the symlink size. */
			filemax = cramfs_buf->inode.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 necessary blocks, and reset the file pointer. */
			len = grub_read(linkbuf, filemax);
			filepos = 0;
			if (!len)
				return 0;

			debug_cramfs("symlink=%s\n", linkbuf);

			dirname = linkbuf;
			if (*dirname == '/') {
				/* It's an absolute link, so look it up in root. */
				current_ino = CRAMFS_ROOT_INO;
				parent_ino = current_ino;
			} else {
				/* Relative, so look it up in our parent directory. */
				current_ino = parent_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(cramfs_buf->inode.mode)) {
				errnum = ERR_BAD_FILETYPE;
				return 0;
			}
			filemax = cramfs_buf->inode.size;
			debug_cramfs("file found, size %d\n", filemax);
			cramfs_buf->cached_block = -1;
			cramfs_buf->decompressed_block = -1;
			return 1;
		}

		/* else we have to traverse a directory */
		parent_ino = current_ino;

		/* skip over slashes */
		while (*dirname == '/') dirname++;

		/* if this isn't a directory of sufficient size to hold our file, 
		   abort */
		if (!(cramfs_buf->inode.size) || !S_ISDIR(cramfs_buf->inode.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;
		off = cramfs_buf->inode.offset << 2;
		dir_size = cramfs_buf->inode.size;

		do {
			debug_cramfs("dirname=`%s', rest=`%s', loc=%d\n", dirname, rest, loc);

			/* if our location/byte offset into the directory exceeds the size,
			   give up */
			if (loc >= dir_size) {
				if (print_possibilities < 0) {
#if 0
					putchar ('\n');
#endif
				} else {
					errnum = ERR_FILE_NOT_FOUND;
					*rest = ch;
				}
				return (print_possibilities < 0);
			}
			
			current_ino = off + loc;
			
			/* read in this inode */
			if (!devread(0, current_ino, sizeof(struct cramfs_inode), (char *) &cramfs_buf->inode))
				return 0;
			if (!devread(0, current_ino + sizeof(struct cramfs_inode), 
					cramfs_buf->inode.namelen << 2, cramfs_buf->name))
				return 0;
			cramfs_buf->name[cramfs_buf->inode.namelen << 2] = '\0';
			namelen = cramfs_strlen(cramfs_buf->name);
			
			/* advance loc prematurely to next on-disk directory entry  */
			loc += sizeof(struct cramfs_inode) + (cramfs_buf->inode.namelen << 2);

			debug_cramfs("directory entry index=%d\n", loc + off);
			debug_cramfs("entry=%s\n", cramfs_buf->name);

			str_chk = substring(dirname, cramfs_buf->name);

#ifndef STAGE1_5
			if (print_possibilities && ch != '/'
				&& (!*dirname || str_chk <= 0)) {
				if (print_possibilities > 0)
					print_possibilities = -print_possibilities;
				print_a_completion(cramfs_buf->name);
			}
# endif


		} while (str_chk || (print_possibilities && ch != '/'));

		*(dirname = rest) = ch;
	}
	/* never get here */
}

Reply via email to