fsys_ufs.c revision 1b8adde7ba7d5e04395c141c5400dc2cffd7d809
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2006 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.
*/
/*
* Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/* From Solaris usr/src/stand/lib/fs/ufs/ufsops.c */
#ifdef FSYS_UFS
#include "shared.h"
#include "filesys.h"
#include "ufs.h"
/* These are the pools of buffers, etc. */
#define SUPERBLOCK ((struct fs *)(FSYS_BUF + 0x2000))
#define INODE ((struct icommon *)(FSYS_BUF + 0x1000))
#define DIRENT (FSYS_BUF + 0x4000)
#define INDIRBLK1 ((grub_daddr32_t *)(FSYS_BUF + 0x4000)) /* 2+ indir blk */
#define INDIRBLK0 ((grub_daddr32_t *)(FSYS_BUF+ 0x6000)) /* 1st indirect blk */
static int indirblk0, indirblk1;
static int openi(grub_ino_t);
static grub_ino_t dlook(grub_ino_t, char *);
static grub_daddr32_t sbmap(grub_daddr32_t);
/* read superblock and check fs magic */
int
ufs_mount(void)
{
if (! IS_PC_SLICE_TYPE_SOLARIS(current_slice) ||
!devread(UFS_SBLOCK, 0, UFS_SBSIZE, (char *)SUPERBLOCK) ||
SUPERBLOCK->fs_magic != UFS_MAGIC)
return 0;
return 1;
}
/*
* searching for a file, if successful, inode will be loaded in INODE
* The entry point should really be named ufs_open(char *pathname).
* For now, keep it consistent with the rest of fsys modules.
*/
int
ufs_dir(char *dirname)
{
grub_ino_t inode = ROOTINO; /* start from root */
char *fname, ch;
indirblk0 = indirblk1 = 0;
/* skip leading slashes */
while (*dirname == '/')
dirname++;
while (inode && *dirname && !isspace(*dirname)) {
if (!openi(inode))
return 0;
/* parse for next path component */
fname = dirname;
while (*dirname && !isspace(*dirname) && *dirname != '/')
dirname++;
ch = *dirname;
*dirname = 0; /* ensure null termination */
inode = dlook(inode, fname);
*dirname = ch;
while (*dirname == '/')
dirname++;
}
/* return 1 only if inode exists and is a regular file */
if (! openi(inode))
return (0);
filepos = 0;
filemax = INODE->ic_sizelo;
return (inode && ((INODE->ic_smode & IFMT) == IFREG));
}
/*
* This is the high-level read function.
*/
int
ufs_read(char *buf, int len)
{
int off, size, ret = 0, ok;
grub_daddr32_t lblk, dblk;
while (len) {
off = blkoff(SUPERBLOCK, filepos);
lblk = lblkno(SUPERBLOCK, filepos);
size = SUPERBLOCK->fs_bsize;
size -= off;
if (size > len)
size = len;
if ((dblk = sbmap(lblk)) <= 0) {
/* we are in a file hole, just zero the buf */
grub_memset(buf, 0, size);
} else {
disk_read_func = disk_read_hook;
ok = devread(fsbtodb(SUPERBLOCK, dblk), off, size, buf);
disk_read_func = 0;
if (!ok)
return 0;
}
buf += size;
len -= size;
filepos += size;
ret += size;
}
return (ret);
}
int
ufs_embed (int *start_sector, int needed_sectors)
{
if (needed_sectors > 14)
return 0;
*start_sector = 2;
return 1;
}
/* read inode and place content in INODE */
static int
openi(grub_ino_t inode)
{
grub_daddr32_t dblk;
int off;
/* get block and byte offset into the block */
dblk = fsbtodb(SUPERBLOCK, itod(SUPERBLOCK, inode));
off = itoo(SUPERBLOCK, inode) * sizeof (struct icommon);
return (devread(dblk, off, sizeof (struct icommon), (char *)INODE));
}
/*
* 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).
*/
grub_daddr32_t
sbmap(grub_daddr32_t bn)
{
int level, bound, i, index;
grub_daddr32_t nb, blkno;
grub_daddr32_t *db = INODE->ic_db;
/* 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 ((grub_daddr32_t)0);
/* fetch the first indirect block */
nb = INODE->ic_ib[level];
if (nb == 0) {
return ((grub_daddr32_t)0);
}
if (indirblk0 != nb) {
indirblk0 = 0;
blkno = fsbtodb(SUPERBLOCK, nb);
if (!devread(blkno, 0, SUPERBLOCK->fs_bsize,
(char *)INDIRBLK0))
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);
if (!devread(blkno, 0, SUPERBLOCK->fs_bsize,
(char *)INDIRBLK1))
return (0);
indirblk1 = nb;
}
bound /= UFS_NINDIR(SUPERBLOCK);
index = (bn / bound) % UFS_NINDIR(SUPERBLOCK);
nb = INDIRBLK1[index];
if (nb == 0)
return ((grub_daddr32_t)0);
}
return (nb);
}
/* search directory content for name, return inode number */
static grub_ino_t
dlook(grub_ino_t dir_ino, char *name)
{
int loc, off;
grub_daddr32_t lbn, dbn, dblk;
struct direct *dp;
if ((INODE->ic_smode & IFMT) != IFDIR)
return 0;
loc = 0;
while (loc < INODE->ic_sizelo) {
/* offset into block */
off = blkoff(SUPERBLOCK, loc);
if (off == 0) { /* need to read in a new block */
/* get logical block number */
lbn = lblkno(SUPERBLOCK, loc);
/* resolve indrect blocks */
dbn = sbmap(lbn);
if (dbn == 0)
return (0);
dblk = fsbtodb(SUPERBLOCK, dbn);
if (!devread(dblk, 0, SUPERBLOCK->fs_bsize,
(char *)DIRENT)) {
return 0;
}
}
dp = (struct direct *)(DIRENT + off);
if (dp->d_ino && substring(name, dp->d_name) == 0)
return (dp->d_ino);
loc += dp->d_reclen;
}
return (0);
}
#endif /* FSYS_UFS */