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
41 static int indirblk0, indirblk1;
42
43 static int openi(grub_ino_t);
44 static grub_ino_t dlook(grub_ino_t, char *);
45 static grub_daddr32_t sbmap(grub_daddr32_t);
46
47 /* read superblock and check fs magic */
48 int
ufs_mount(void)49 ufs_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 */
65 int
ufs_dir(char * dirname)66 ufs_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 */
105 int
ufs_read(char * buf,int len)106 ufs_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
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(grub_ino_t inode)150 openi(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 */
167 grub_daddr32_t
sbmap(grub_daddr32_t bn)168 sbmap(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 */
228 static grub_ino_t
dlook(grub_ino_t dir_ino,char * name)229 dlook(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