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