blob: b9e7d8cb34fe4e7c7d5df45ba57713dafaf2a696 [file] [log] [blame] [raw]
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2000, 2001 Free Software Foundation, Inc.
* Copyright (c) 2004 Valery Hromov
*
* 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.
*/
/*
* Elements of this file were originally from the FreeBSD "biosboot"
* bootloader file "disk.c" dated 4/12/95.
*
* The license and header comments from that file are included here.
*/
/*
* Mach Operating System
* Copyright (c) 1992, 1991 Carnegie Mellon University
* All Rights Reserved.
*
* Permission to use, copy, modify and distribute this software and its
* documentation is hereby granted, provided that both the copyright
* notice and this permission notice appear in all copies of the
* software, derivative works or modified versions, and any portions
* thereof, and that both notices appear in supporting documentation.
*
* CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
* CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
* ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
*
* Carnegie Mellon requests users of this software to return to
*
* Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
* School of Computer Science
* Carnegie Mellon University
* Pittsburgh PA 15213-3890
*
* any improvements or extensions that they make and grant Carnegie Mellon
* the rights to redistribute these changes.
*
* from: Mach, Revision 2.2 92/04/04 11:35:49 rpd
*/
/* Part of the source are from Sun Microsystems */
/*
* Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#ifdef FSYS_UFS1
#include "shared.h"
#include "filesys.h"
#include "ufs.h"
static int sblock_try[] = SBLOCKSEARCH;
//static ufs1_daddr_t sblockloc;
static ufs1_daddr_t indirblk0, indirblk1;
/* pointer to superblock */
#define SUPERBLOCK ((struct fs *) ( FSYS_BUF + 8192 ))
#define INODE_UFS1 ((struct ufs1_dinode *) ( FSYS_BUF + 4096 ))
#define INDIRBLK1 ((ufs1_daddr_t *)(FSYS_BUF + 0x4000)) /* 2+ indir blk */
#define INDIRBLK0 ((ufs1_daddr_t *)(FSYS_BUF + 0x6000)) /* 1st indirect blk */
#define INDIRBLK_LEN_MAX 0x2000
#define NAME_BUF (FSYS_BUF - 512)
#define NAME_BUF_LEN 512
int
ufs1_mount (void)
{
int retval = 0;
int i;
//sblockloc = -1;
for (i = 0; sblock_try[i] != -1; ++i)
{
if (! ((unsigned long)part_length < (sblock_try[i] + (SBLOCKSIZE / DEV_BSIZE))
|| ! devread (0, sblock_try[i], SBLOCKSIZE, (char *) SUPERBLOCK, 0xedde0d90)))
{
if (SUPERBLOCK->fs_magic != FS_UFS1_MAGIC) continue;
retval = 1;
//sblockloc = sblock_try[i];
break;
}
}
return retval;
}
static void get_offset_and_bsize(ufs1_daddr_t file_block, unsigned int *offset, grub_int32_t *bsize) {
if(SUPERBLOCK->fs_bsize > INDIRBLK_LEN_MAX) {
*offset = ((file_block - UFS_NDADDR) % UFS_NINDIR(SUPERBLOCK));
*bsize = INDIRBLK_LEN_MAX;
if(*offset + INDIRBLK_LEN_MAX > SUPERBLOCK->fs_bsize) {
*offset = (SUPERBLOCK->fs_bsize - INDIRBLK_LEN_MAX) / sizeof (int);
}
} else {
*offset = 0;
*bsize = SUPERBLOCK->fs_bsize;
}
}
/*
* Performs fileblock mapping. Convert file block no. to disk block no.
* Returns 0 when block doesn't exist and <0 when block isn't initialized
* (i.e belongs to a hole in the file).
*/
static ufs1_daddr_t
block_map (ufs1_daddr_t file_block)
{
int level, bound, i, index;
ufs1_daddr_t nb, blkno, bn = file_block;
ufs1_daddr_t *db = INODE_UFS1->di_db;
grub_int32_t bsize;
unsigned int offset;
/* blocks 0..UFS_NDADDR are direct blocks */
if (bn < UFS_NDADDR) {
return db[bn];
}
/* determine how many levels of indirection. */
level = 0;
bn -= UFS_NDADDR;
bound = UFS_NINDIR(SUPERBLOCK);
while (bn >= bound) {
level++;
bn -= bound;
bound *= UFS_NINDIR(SUPERBLOCK);
}
if (level >= UFS_NIADDR) /* bn too big */
return 0;
/* fetch the first indirect block */
nb = INODE_UFS1->di_ib[level];
if (nb == 0) {
return 0;
}
if (indirblk0 != nb) {
indirblk0 = 0;
blkno = fsbtodb(SUPERBLOCK, nb);
get_offset_and_bsize(file_block, &offset, &bsize);
if (!devread(blkno, offset * sizeof(int), bsize,
(char *)INDIRBLK0, 0xedde0d90)) {
if(!errnum) errnum = ERR_FSYS_CORRUPT;
return 0;
}
indirblk0 = nb;
}
bound /= UFS_NINDIR(SUPERBLOCK);
index = (bn / bound) % UFS_NINDIR(SUPERBLOCK);
nb = INDIRBLK0[index];
/* fetch through the indirect blocks */
for (i = 1; i <= level; i++) {
if (indirblk1 != nb) {
blkno = fsbtodb(SUPERBLOCK, nb);
get_offset_and_bsize(file_block, &offset, &bsize);
if (!devread(blkno, offset * sizeof(int), bsize,
(char *)INDIRBLK1, 0xedde0d90)) {
if(!errnum) errnum = ERR_FSYS_CORRUPT;
return 0;
}
indirblk1 = nb;
}
bound /= UFS_NINDIR(SUPERBLOCK);
index = (bn / bound) % UFS_NINDIR(SUPERBLOCK);
nb = INDIRBLK1[index];
if (nb == 0)
return 0;
}
return nb;
}
unsigned long
ufs1_read (char *buf, unsigned long len, unsigned long write)
{
unsigned long off, size, ret = 0, s;
ufs1_daddr_t logno, map;
errnum = 0;
while (len && !errnum)
{
off = blkoff1 (SUPERBLOCK, filepos);
logno = lblkno (SUPERBLOCK, filepos);
size = blksize1 (SUPERBLOCK, INODE_UFS1, logno);
//size = SUPERBLOCK->fs_bsize;
size -= off;
if(size > len) size = len;
map = block_map (logno);
if(map <= 0)
{
if(errnum) return 0;
if(buf) grub_memset(buf, 0, size);
}
else
{
disk_read_func = disk_read_hook;
s = devread(fsbtodb(SUPERBLOCK, map), off, size, buf, write);
disk_read_func = NULL;
if(!s) return 0;
}
if (buf)
buf += size;
len -= size; /* len always >= 0 */
filepos += size;
ret += size;
}
if (errnum)
ret = 0;
return ret;
}
int
ufs1_dir (char *dirname)
{
char *rest, ch;
unsigned long block, off, loc, ino = ROOTINO;
ufs1_daddr_t map;
struct direct *dp;
int j, k;
char ch1;
#ifdef GRUB_UTIL
char tmp_name[NAME_BUF_LEN];
#else
char *tmp_name = (char *)(NAME_BUF); /* MAXNAMLEN is 255, so 512 byte buffer is needed. */
#endif
indirblk0 = indirblk1 = 0;
/* main loop to find destination inode */
loop:
/* load current inode (defaults to the root inode) */
if (!devread (fsbtodb (SUPERBLOCK, ino_to_fsba (SUPERBLOCK, ino)),
ino % (SUPERBLOCK->fs_inopb) * sizeof (struct ufs1_dinode),
sizeof (struct ufs1_dinode), (char *) INODE_UFS1, 0xedde0d90))
{
return 0; /* XXX what return value? */
}
/* if we have a real file (and we're not just printing possibilities),
then this is where we want to exit */
if (!*dirname || isspace (*dirname))
{
if ((INODE_UFS1->di_mode & IFMT) != IFREG)
{
errnum = ERR_BAD_FILETYPE;
return 0;
}
filemax = INODE_UFS1->di_size;
return 1;
}
/* continue with file/directory name interpretation */
while (*dirname == '/')
dirname++;
if (!(INODE_UFS1->di_size) || ((INODE_UFS1->di_mode & IFMT) != IFDIR))
{
errnum = ERR_BAD_FILETYPE;
return 0;
}
//for (rest = dirname; (ch = *rest) && !isspace (ch) && ch != '/'; rest++);
for (rest = dirname; (ch = *rest) && !isspace (ch) && ch != '/'; rest++)
{
if (ch == '\\')
{
rest++;
if (! (ch = *rest))
break;
}
}
*rest = 0;
loc = 0;
/* loop for reading a the entries in a directory */
do
{
if (loc >= INODE_UFS1->di_size)
{
if (print_possibilities < 0)
return 1;
errnum = ERR_FILE_NOT_FOUND;
*rest = ch;
return 0;
}
if (!(off = blkoff1 (SUPERBLOCK, loc)))
{
block = lblkno (SUPERBLOCK, loc);
if ((map = block_map (block)) < 0
|| !devread (fsbtodb (SUPERBLOCK, map), 0,
blksize1 (SUPERBLOCK, INODE_UFS1, block),
(char *) FSYS_BUF, 0xedde0d90))
{
errnum = ERR_FSYS_CORRUPT;
*rest = ch;
return 0;
}
}
dp = (struct direct *) (FSYS_BUF + off);
loc += dp->d_reclen;
/* copy dp->name to tmp_name, and quote the spaces with a '\\' */
for (j = 0, k = 0; j < dp->d_namlen_1 && k < NAME_BUF_LEN - 1; j++)
{
if (! (ch1 = dp->d_name[j])) break;
if (ch1 == ' ') tmp_name[k++] = '\\';
tmp_name[k++] = ch1;
}
tmp_name[k] = 0;
#ifndef STAGE1_5
if (dp->d_ino && print_possibilities && ch != '/'
&& (!*dirname || substring (dirname, tmp_name, 0) <= 0))
{
if (print_possibilities > 0)
print_possibilities = -print_possibilities;
print_a_completion (tmp_name);
}
#endif /* STAGE1_5 */
}
while (!dp->d_ino || (substring (dirname, dp->d_name, 0) != 0
|| (print_possibilities && ch != '/')));
/* only get here if we have a matching directory entry */
ino = dp->d_ino;
*(dirname = rest) = ch;
/* go back to main loop at top of function */
goto loop;
}
unsigned long
ufs1_embed (unsigned long *start_sector, unsigned long needed_sectors)
{
if (needed_sectors > 14)
return 0;
*start_sector = 2;
return 1;
}
#endif /* FSYS_UFS1 */