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