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, see <http://www.gnu.org/licenses/>.
17  */
18 /*
19  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
20  * Use is subject to license terms.
21  */
22 
23 /* From Solaris usr/src/stand/lib/fs/ufs/ufsops.c */
24 
25 #include <fsimage_grub.h>
26 
27 #include "ufs.h"
28 
29 /* These are the pools of buffers, etc. */
30 
31 #define SUPERBLOCK ((struct fs *)(FSYS_BUF + 0x2000))
32 #define	INODE ((struct icommon *)(FSYS_BUF + 0x1000))
33 #define DIRENT (FSYS_BUF + 0x4000)
34 #define MAXBSIZE ((FSYS_BUFLEN - 0x4000) / 2)
35 #define INDIRBLK1 ((grub_daddr32_t *)(FSYS_BUF + 0x4000)) /* 2+ indir blk */
36 #define	INDIRBLK0 ((grub_daddr32_t *)(FSYS_BUF+ 0x4000 + MAXBSIZE))  /* 1st indirect blk */
37 
38 #define	indirblk0 (*fsig_int1(ffi))
39 #define	indirblk1 (*fsig_int2(ffi))
40 
41 static int openi(fsi_file_t *, grub_ino_t);
42 static grub_ino_t dlook(fsi_file_t *, grub_ino_t, char *);
43 static grub_daddr32_t sbmap(fsi_file_t *, grub_daddr32_t);
44 
45 /* read superblock and check fs magic */
46 static int
ufs_mount(fsi_file_t * ffi,const char * options)47 ufs_mount(fsi_file_t *ffi, const char *options)
48 {
49 	if (/*! IS_PC_SLICE_TYPE_SOLARIS(current_slice) || */
50 	    !devread(ffi, UFS_SBLOCK, 0, UFS_SBSIZE, (char *)SUPERBLOCK) ||
51 	    SUPERBLOCK->fs_magic != UFS_MAGIC ||
52 	    MAXBSIZE < SUPERBLOCK->fs_bsize)
53 		return 0;
54 
55 	return 1;
56 }
57 
58 
59 /*
60  * searching for a file, if successful, inode will be loaded in INODE
61  * The entry point should really be named ufs_open(char *pathname).
62  * For now, keep it consistent with the rest of fsys modules.
63  */
64 static int
ufs_dir(fsi_file_t * ffi,char * dirname)65 ufs_dir(fsi_file_t *ffi, char *dirname)
66 {
67 	grub_ino_t inode = ROOTINO;	/* start from root */
68 	char *fname, ch;
69 
70 	indirblk0 = indirblk1 = 0;
71 
72 	/* skip leading slashes */
73 	while (*dirname == '/')
74 		dirname++;
75 
76 	while (inode && *dirname && !isspace((uint8_t)*dirname)) {
77 		if (!openi(ffi, inode))
78 			return 0;
79 
80 		/* parse for next path component */
81 		fname = dirname;
82 		while (*dirname && !isspace((uint8_t)*dirname) && *dirname != '/')
83 			dirname++;
84 		ch = *dirname;
85 		*dirname = 0;	/* ensure null termination */
86 
87 		inode = dlook(ffi, inode, fname);
88 		*dirname = ch;
89 		while (*dirname == '/')
90 			dirname++;
91 	}
92 
93 	/* return 1 only if inode exists and is a regular file */
94 	if  (! openi(ffi, inode))
95 		return (0);
96 	filepos = 0;
97 	filemax = INODE->ic_sizelo;
98 	return (inode && ((INODE->ic_smode & IFMT) == IFREG));
99 }
100 
101 /*
102  * This is the high-level read function.
103  */
104 static int
ufs_read(fsi_file_t * ffi,char * buf,int len)105 ufs_read(fsi_file_t *ffi, char *buf, int len)
106 {
107   	int off, size, ret = 0, ok;
108 	grub_daddr32_t lblk, dblk;
109 
110   	while (len) {
111 	  	off = blkoff(SUPERBLOCK, filepos);
112 		lblk = lblkno(SUPERBLOCK, filepos);
113 		size = SUPERBLOCK->fs_bsize;
114 		size -= off;
115 		if (size > len)
116 		  	size = len;
117 
118 		if ((dblk = sbmap(ffi, lblk)) <= 0) {
119 			/* we are in a file hole, just zero the buf */
120 			grub_memset(buf, 0, size);
121 		} else {
122 			disk_read_func = disk_read_hook;
123 			ok = devread(ffi, fsbtodb(SUPERBLOCK, dblk),
124 				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 
138 int
ufs_embed(int * start_sector,int needed_sectors)139 ufs_embed (int *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 */
149 static int
openi(fsi_file_t * ffi,grub_ino_t inode)150 openi(fsi_file_t *ffi, 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(ffi, 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  */
167 grub_daddr32_t
sbmap(fsi_file_t * ffi,grub_daddr32_t bn)168 sbmap(fsi_file_t *ffi, 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(ffi, 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(ffi, 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 */
228 static grub_ino_t
dlook(fsi_file_t * ffi,grub_ino_t dir_ino,char * name)229 dlook(fsi_file_t *ffi, 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 		  	/* get logical block number */
244 			lbn = lblkno(SUPERBLOCK, loc);
245 			/* resolve indrect blocks */
246 			dbn = sbmap(ffi, lbn);
247 			if (dbn == 0)
248 				return (0);
249 
250 			dblk = fsbtodb(SUPERBLOCK, dbn);
251 			if (!devread(ffi, dblk, 0, SUPERBLOCK->fs_bsize,
252 			    (char *)DIRENT)) {
253 				return 0;
254 			}
255 		}
256 
257 		dp = (struct direct *)(DIRENT + off);
258 		if (dp->d_ino && substring(name, dp->d_name) == 0)
259 			return (dp->d_ino);
260 		loc += dp->d_reclen;
261 	}
262 	return (0);
263 }
264 
265 fsi_plugin_ops_t *
fsi_init_plugin(int version,fsi_plugin_t * fp,const char ** name)266 fsi_init_plugin(int version, fsi_plugin_t *fp, const char **name)
267 {
268 	static fsig_plugin_ops_t ops = {
269 		FSIMAGE_PLUGIN_VERSION,
270 		.fpo_mount = ufs_mount,
271 		.fpo_dir = ufs_dir,
272 		.fpo_read = ufs_read
273 	};
274 
275 	*name = "ufs";
276 	return (fsig_init(fp, &ops));
277 }
278