1/*
2 *  GRUB  --  GRand Unified Bootloader
3 *  Copyright (C) 2006 Free Software Foundation, Inc.
4 *
5 *  This program is free software; you can redistribute it and/or modify
6 *  it under the terms of the GNU General Public License as published by
7 *  the Free Software Foundation; either version 2 of the License, or
8 *  (at your option) any later version.
9 *
10 *  This program is distributed in the hope that it will be useful,
11 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 *  GNU General Public License for more details.
14 *
15 *  You should have received a copy of the GNU General Public License
16 *  along with this program; if not, write to the Free Software
17 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19/*
20 * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
21 * Use is subject to license terms.
22 */
23
24/* From Solaris usr/src/stand/lib/fs/ufs/ufsops.c */
25
26#ifdef	FSYS_UFS
27
28#include "shared.h"
29#include "filesys.h"
30
31#include "ufs.h"
32
33/* These are the pools of buffers, etc. */
34
35#define SUPERBLOCK ((struct fs *)(FSYS_BUF + 0x2000))
36#define	INODE ((struct icommon *)(FSYS_BUF + 0x1000))
37#define DIRENT (FSYS_BUF + 0x4000)
38#define INDIRBLK1 ((grub_daddr32_t *)(FSYS_BUF + 0x4000)) /* 2+ indir blk */
39#define	INDIRBLK0 ((grub_daddr32_t *)(FSYS_BUF+ 0x6000))  /* 1st indirect blk */
40
41static int indirblk0, indirblk1;
42
43static int openi(grub_ino_t);
44static grub_ino_t dlook(grub_ino_t, char *);
45static grub_daddr32_t sbmap(grub_daddr32_t);
46
47/* read superblock and check fs magic */
48int
49ufs_mount(void)
50{
51	if (! IS_PC_SLICE_TYPE_SOLARIS(current_slice) ||
52	    !devread(UFS_SBLOCK, 0, UFS_SBSIZE, (char *)SUPERBLOCK) ||
53	    SUPERBLOCK->fs_magic != UFS_MAGIC)
54		return 0;
55
56	return 1;
57}
58
59
60/*
61 * searching for a file, if successful, inode will be loaded in INODE
62 * The entry point should really be named ufs_open(char *pathname).
63 * For now, keep it consistent with the rest of fsys modules.
64 */
65int
66ufs_dir(char *dirname)
67{
68	grub_ino_t inode = ROOTINO;	/* start from root */
69	char *fname, ch;
70
71	indirblk0 = indirblk1 = 0;
72
73	/* skip leading slashes */
74	while (*dirname == '/')
75		dirname++;
76
77	while (inode && *dirname && !isspace(*dirname)) {
78		if (!openi(inode))
79			return 0;
80
81		/* parse for next path component */
82		fname = dirname;
83		while (*dirname && !isspace(*dirname) && *dirname != '/')
84			dirname++;
85		ch = *dirname;
86		*dirname = 0;	/* ensure null termination */
87
88		inode = dlook(inode, fname);
89		*dirname = ch;
90		while (*dirname == '/')
91			dirname++;
92	}
93
94	/* return 1 only if inode exists and is a regular file */
95	if  (! openi(inode))
96		return (0);
97	filepos = 0;
98	filemax = INODE->ic_sizelo;
99	return (inode && ((INODE->ic_smode & IFMT) == IFREG));
100}
101
102/*
103 * This is the high-level read function.
104 */
105int
106ufs_read(char *buf, int len)
107{
108  	int off, size, ret = 0, ok;
109	grub_daddr32_t lblk, dblk;
110
111  	while (len) {
112	  	off = blkoff(SUPERBLOCK, filepos);
113		lblk = lblkno(SUPERBLOCK, filepos);
114		size = SUPERBLOCK->fs_bsize;
115		size -= off;
116		if (size > len)
117		  	size = len;
118
119		if ((dblk = sbmap(lblk)) <= 0) {
120			/* we are in a file hole, just zero the buf */
121			grub_memset(buf, 0, size);
122		} else {
123			disk_read_func = disk_read_hook;
124			ok = devread(fsbtodb(SUPERBLOCK, dblk), off, size, buf);
125			disk_read_func = 0;
126			if (!ok)
127		  		return 0;
128		}
129		buf += size;
130		len -= size;
131		filepos += size;
132		ret += size;
133	}
134
135	return (ret);
136}
137
138int
139ufs_embed (unsigned long long *start_sector, int needed_sectors)
140{
141	if (needed_sectors > 14)
142        	return 0;
143
144	*start_sector = 2;
145	return 1;
146}
147
148/* read inode and place content in INODE */
149static int
150openi(grub_ino_t inode)
151{
152	grub_daddr32_t dblk;
153	int off;
154
155	/* get block and byte offset into the block */
156	dblk = fsbtodb(SUPERBLOCK, itod(SUPERBLOCK, inode));
157	off = itoo(SUPERBLOCK, inode) * sizeof (struct icommon);
158
159	return (devread(dblk, off, sizeof (struct icommon), (char *)INODE));
160}
161
162/*
163 * Performs fileblock mapping. Convert file block no. to disk block no.
164 * Returns 0 when block doesn't exist and <0 when block isn't initialized
165 * (i.e belongs to a hole in the file).
166 */
167grub_daddr32_t
168sbmap(grub_daddr32_t bn)
169{
170  	int level, bound, i, index;
171	grub_daddr32_t nb, blkno;
172	grub_daddr32_t *db = INODE->ic_db;
173
174	/* blocks 0..UFS_NDADDR are direct blocks */
175	if (bn < UFS_NDADDR) {
176		return db[bn];
177	}
178
179	/* determine how many levels of indirection. */
180	level = 0;
181	bn -= UFS_NDADDR;
182	bound = UFS_NINDIR(SUPERBLOCK);
183	while (bn >= bound) {
184		level++;
185		bn -= bound;
186		bound *= UFS_NINDIR(SUPERBLOCK);
187	}
188	if (level >= UFS_NIADDR)	/* bn too big */
189		return ((grub_daddr32_t)0);
190
191	/* fetch the first indirect block */
192	nb = INODE->ic_ib[level];
193	if (nb == 0) {
194		return ((grub_daddr32_t)0);
195	}
196	if (indirblk0 != nb) {
197		indirblk0 = 0;
198		blkno = fsbtodb(SUPERBLOCK, nb);
199		if (!devread(blkno, 0, SUPERBLOCK->fs_bsize,
200		    (char *)INDIRBLK0))
201			return (0);
202		indirblk0 = nb;
203	}
204	bound /= UFS_NINDIR(SUPERBLOCK);
205	index = (bn / bound) % UFS_NINDIR(SUPERBLOCK);
206	nb = INDIRBLK0[index];
207
208	/* fetch through the indirect blocks */
209	for (i = 1; i <= level; i++) {
210		if (indirblk1 != nb) {
211			blkno = fsbtodb(SUPERBLOCK, nb);
212			if (!devread(blkno, 0, SUPERBLOCK->fs_bsize,
213			    (char *)INDIRBLK1))
214				return (0);
215			indirblk1 = nb;
216		}
217		bound /= UFS_NINDIR(SUPERBLOCK);
218		index = (bn / bound) % UFS_NINDIR(SUPERBLOCK);
219		nb = INDIRBLK1[index];
220		if (nb == 0)
221			return ((grub_daddr32_t)0);
222	}
223
224	return (nb);
225}
226
227/* search directory content for name, return inode number */
228static grub_ino_t
229dlook(grub_ino_t dir_ino, char *name)
230{
231	int loc, off;
232	grub_daddr32_t lbn, dbn, dblk;
233	struct direct *dp;
234
235	if ((INODE->ic_smode & IFMT) != IFDIR)
236		return 0;
237
238	loc = 0;
239	while (loc < INODE->ic_sizelo) {
240	  	/* offset into block */
241		off = blkoff(SUPERBLOCK, loc);
242		if (off == 0) {		/* need to read in a new block */
243
244		  	/* get logical block number */
245			lbn = lblkno(SUPERBLOCK, loc);
246			/* resolve indrect blocks */
247			dbn = sbmap(lbn);
248			if (dbn == 0)
249				return (0);
250
251			dblk = fsbtodb(SUPERBLOCK, dbn);
252			if (!devread(dblk, 0, SUPERBLOCK->fs_bsize,
253			    (char *)DIRENT)) {
254				return 0;
255			}
256		}
257
258		dp = (struct direct *)(DIRENT + off);
259		if (dp->d_ino && substring(name, dp->d_name) == 0)
260			return (dp->d_ino);
261		loc += dp->d_reclen;
262	}
263	return (0);
264}
265
266#endif /* FSYS_UFS */
267