1 /*        $NetBSD: readufs.c,v 1.15 2013/01/22 09:39:14 dholland Exp $          */
2 /*        from Id: readufs.c,v 1.8 2003/04/08 09:19:32 itohy Exp      */
3 
4 /*
5  * Read UFS (FFS / LFS)
6  *
7  * Written in 1999, 2002, 2003 by ITOH Yasufumi.
8  * Public domain.
9  *
10  * Intended to be used for boot programs (first stage).
11  * DON'T ADD ANY FANCY FEATURE.  THIS SHALL BE COMPACT.
12  */
13 
14 #include "readufs.h"
15 
16 #define fs          ufs_info
17 
18 static void raw_read_queue(void *buf, daddr_t blkpos, size_t bytelen);
19 static int ufs_read_indirect(daddr_t blk, int level, uint8_t **buf,
20                     unsigned *poff, size_t count);
21 
22 #ifdef DEBUG_WITH_STDIO
23 void ufs_list_dir(ino32_t dirino);
24 int main(int argc, char *argv[]);
25 #endif
26 
27 #ifdef DEBUG_WITH_STDIO
28 int fd;
29 
30 void
RAW_READ(void * buf,daddr_t blkpos,size_t bytelen)31 RAW_READ(void *buf, daddr_t blkpos, size_t bytelen)
32 {
33 
34           if (pread(fd, buf, bytelen, (off_t)dbtob(blkpos)) != (ssize_t) bytelen)
35                     err(1, "pread: buf %p, blk %d, len %u",
36                         buf, (int) blkpos, bytelen);
37 }
38 #endif
39 
40 struct ufs_info fs;
41 
42 /*
43  * Read contiguous sectors at once for speedup.
44  */
45 static size_t rq_len;
46 
47 static void
raw_read_queue(void * buf,daddr_t blkpos,size_t bytelen)48 raw_read_queue(void *buf, daddr_t blkpos, size_t bytelen)
49           /* bytelen:                    must be DEV_BSIZE aligned */
50 {
51           static daddr_t rq_start;
52           static char *rq_buf;
53 
54           if (rq_len) {
55                     if (bytelen && blkpos == rq_start + (ssize_t) btodb(rq_len)
56                                         && buf == rq_buf + rq_len) {
57                               rq_len += bytelen;
58                               return;
59                     } else {
60 #ifdef DEBUG_WITH_STDIO
61                               printf("raw_read_queue: read: buf %p, blk %d, len %d\n",
62                                          rq_buf, (int) rq_start, rq_len);
63 #endif
64                               RAW_READ(rq_buf, rq_start, rq_len);
65                     }
66           }
67           rq_buf = buf;
68           rq_start = blkpos;
69           rq_len = bytelen;
70 }
71 
72 #define RAW_READ_QUEUE_INIT() (rq_len = 0)
73 #define RAW_READ_QUEUE_FLUSH()          \
74                     raw_read_queue((void *) 0, (daddr_t) 0, (size_t) 0)
75 
76 
77 /*
78  * Read a file, specified by dinode.
79  * No support for holes or (short) symbolic links.
80  */
81 size_t
ufs_read(union ufs_dinode * di,void * buf,unsigned off,size_t count)82 ufs_read(union ufs_dinode *di, void *buf, unsigned off, size_t count)
83           /* off:    position in block */
84 {
85           struct ufs_info *ufsinfo = &fs;
86           size_t bsize = ufsinfo->bsize;
87           uint8_t *b = buf;
88           int i;
89           size_t disize, nread;
90           daddr_t pos;
91 #if defined(USE_UFS1) && defined(USE_UFS2)
92           enum ufs_ufstype uver = ufsinfo->ufstype;
93 #endif
94 
95 #ifdef DEBUG_WITH_STDIO
96           printf("ufs_read: off: %d, count %u\n", off, count);
97 #endif
98 
99           disize = DI_SIZE(di);
100 
101           if (disize < count + off * bsize)
102                     count = disize - off * bsize;
103 
104           /* FS block size alignment. */
105           nread = count;
106           count = (count + bsize - 1) & ~(bsize - 1);
107 
108           RAW_READ_QUEUE_INIT();
109 
110           /* Read direct blocks. */
111           for ( ; off < UFS_NDADDR && count > 0; off++) {
112 #if defined(USE_UFS1) && defined(USE_UFS2)
113                     if (uver == UFSTYPE_UFS1)
114                               pos = di->di1.di_db[off];
115                     else
116                               pos = di->di2.di_db[off];
117 #else
118                     pos = di->di_thisver.di_db[off];
119 #endif
120 #if 0
121                     printf("ufs_read: read: blk: %d\n",
122                               (int) pos << ufsinfo->fsbtodb);
123 #endif
124                     raw_read_queue(b, pos << ufsinfo->fsbtodb, bsize);
125                     b += bsize;
126                     count -= bsize;
127           }
128           off -= UFS_NDADDR;
129 
130           /* Read indirect blocks. */
131           for (i = 0; i < UFS_NIADDR && count > 0; i++) {
132 #if defined(USE_UFS1) && defined(USE_UFS2)
133                     if (uver == UFSTYPE_UFS1)
134                               pos = di->di1.di_ib[i];
135                     else
136                               pos = di->di2.di_ib[i];
137 #else
138                     pos = di->di_thisver.di_ib[i];
139 #endif
140                     count = ufs_read_indirect(pos, i, &b, &off, count);
141           }
142 
143           RAW_READ_QUEUE_FLUSH();
144 
145           return nread;
146 }
147 
148 static int
ufs_read_indirect(daddr_t blk,int level,uint8_t ** buf,unsigned * poff,size_t count)149 ufs_read_indirect(daddr_t blk, int level, uint8_t **buf, unsigned *poff, size_t count)
150           /* poff:   position in block */
151 {
152           struct ufs_info *ufsinfo = &fs;
153           size_t bsize = ufsinfo->bsize;
154           void *idbuf = alloca(bsize);
155 #ifdef USE_UFS1
156           int32_t *idbuf1 = idbuf;
157 #endif
158 #ifdef USE_UFS2
159           int64_t *idbuf2 = idbuf;
160 #endif
161           daddr_t pos;
162           unsigned off = *poff;
163           unsigned b;
164 
165 #ifdef DEBUG_WITH_STDIO
166           printf("ufs_read_indirect: off: %d, count %u\n", off, count);
167 #endif
168           if (off) {
169                     unsigned subindirsize = 1, indirsize;
170                     int i;
171 
172                     for (i = level; i > 0; i--)
173                               subindirsize *= ufsinfo->nindir;
174                     indirsize = subindirsize * ufsinfo->nindir;
175                     if (off >= indirsize) {
176                               /* no need to read any data */
177                               *poff = off - indirsize;
178                               return 0;
179                     }
180 
181                     b = off / subindirsize;
182                     off -= b * subindirsize;
183                     *poff = 0;
184           } else
185                     b = 0;
186 
187           /* read the indirect block */
188           RAW_READ(idbuf, blk << ufsinfo->fsbtodb, bsize);
189 
190           for ( ; b < ufsinfo->nindir && count > 0; b++) {
191 #if defined(USE_UFS1) && defined(USE_UFS2)
192                     if (ufsinfo->ufstype == UFSTYPE_UFS1)
193 #endif
194 #ifdef USE_UFS1
195                               pos = idbuf1[b];
196 #endif
197 #if defined(USE_UFS1) && defined(USE_UFS2)
198                     else
199 #endif
200 #ifdef USE_UFS2
201                               pos = idbuf2[b];
202 #endif
203 
204                     if (level)
205                               count = ufs_read_indirect(pos, level - 1, buf, &off, count);
206                     else {
207 #if 0
208                               printf("ufs_read: read: blk: %d\n",
209                                         (int) pos << ufsinfo->fsbtodb);
210 #endif
211                               raw_read_queue(*buf, pos << ufsinfo->fsbtodb, bsize);
212                               *buf += bsize;
213                               count -= bsize;
214                     }
215           }
216 
217           return count;
218 }
219 
220 /*
221  * look-up fn in directory dirino
222  */
223 ino32_t
ufs_lookup(ino32_t dirino,const char * fn)224 ufs_lookup(ino32_t dirino, const char *fn)
225 {
226           union ufs_dinode dirdi;
227           struct direct *pdir;
228           char *p, *endp;
229           size_t disize;
230 
231           if (ufs_get_inode(dirino, &dirdi))
232                     return 0;
233 
234           if ((dirdi.di_common.di_mode & IFMT) != IFDIR)
235                     return 0;                     /* Not a directory */
236 
237           disize = DI_SIZE(&dirdi);
238 
239 #if 0
240           p = alloca((disize + fs.bsize - 1) & ~(fs.bsize - 1));
241 #else     /* simplify calculation to reduce code size */
242           p = alloca(disize + fs.bsize);
243 #endif
244           ufs_read(&dirdi, p, 0, disize);
245           endp = p + disize;
246           for ( ; pdir = (void *) p, p < endp; p += pdir->d_reclen) {
247                     if (pdir->d_ino && !strcmp(fn, pdir->d_name))
248                               return pdir->d_ino;
249           }
250           return 0;                               /* No such file or directory */
251 }
252 
253 /*
254  * look-up a file in absolute pathname from the root directory
255  */
256 ino32_t
ufs_lookup_path(const char * path)257 ufs_lookup_path(const char *path)
258 {
259           char fn[FFS_MAXNAMLEN + 1];
260           char *p;
261           ino32_t ino = UFS_ROOTINO;
262 
263           do {
264                     while (*path == '/')
265                               path++;
266                     for (p = fn; *path && *path != '/'; )
267                               *p++ = *path++;
268                     *p++ = '\0';
269                     ino = ufs_lookup(ino, fn);
270           } while (ino && *path);
271 
272           return ino;
273 }
274 
275 #if 0
276 size_t
277 ufs_load_file(void *buf, ino32_t dirino, const char *fn)
278 {
279           size_t cnt, disize;
280           union ufs_dinode dinode;
281 
282           if (ufs_fn_inode(dirino, fn, &dinode))
283                     return (unsigned) 0;
284           disize = DI_SIZE(&dinode);
285           cnt = ufs_read(&dinode, buf, 0, disize);
286 
287           return cnt;
288 }
289 #endif
290 
291 int
ufs_init(void)292 ufs_init(void)
293 {
294           return 1
295 #ifdef USE_FFS
296                     && try_ffs()
297 #endif
298 #ifdef USE_LFS
299                     && try_lfs()
300 #endif
301                     ;
302 }
303 
304 #ifdef DEBUG_WITH_STDIO
305 void
ufs_list_dir(ino32_t dirino)306 ufs_list_dir(ino32_t dirino)
307 {
308           union ufs_dinode dirdi;
309           struct direct *pdir;
310           char *p, *endp;
311           size_t disize;
312 
313           if (ufs_get_inode(dirino, &dirdi))
314                     errx(1, "ino = %d: not found", dirino);
315 
316           disize = DI_SIZE(&dirdi);
317           p = alloca((disize + fs.bsize - 1) & ~(fs.bsize - 1));
318           ufs_read(&dirdi, p, 0, disize);
319           endp = p + disize;
320           for ( ; pdir = (void *) p, p < endp; p += pdir->d_reclen) {
321                     if (pdir->d_ino)
322                               printf("%6d %s\n", pdir->d_ino, pdir->d_name);
323           }
324 }
325 #endif
326 
327 #ifdef DEBUG_WITH_STDIO
328 int
main(argc,argv)329 main(argc, argv)
330           int argc __attribute__((unused));
331           char *argv[];
332 {
333           union ufs_dinode dinode;
334 
335           if ((fd = open(argv[1], O_RDONLY)) < 0)
336                     err(1, "open: %s", argv[1]);
337 
338           if (ufs_init())
339                     errx(1, "%s: unknown fs", argv[1]);
340 
341 #if 1
342           ufs_list_dir(UFS_ROOTINO);
343           {
344                     void *p;
345                     size_t cnt;
346                     ino32_t ino;
347                     size_t disize;
348 
349                     if ((ino = ufs_lookup_path(argv[2])) == 0)
350                               errx(1, "%s: not found", argv[2]);
351                     ufs_get_inode(ino, &dinode);
352                     disize = DI_SIZE(&dinode);
353                     p = malloc((disize + fs.bsize - 1) & ~(fs.bsize - 1));
354                     cnt = ufs_read(&dinode, p, 0, disize);
355                     write(3, p, cnt);
356                     free(p);
357           }
358 #endif
359 
360           return 0;
361 }
362 #endif
363