1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25
26 #include <sys/param.h>
27 #include <sys/vnode.h>
28 #include <sys/fs/ufs_fsdir.h>
29 #include <sys/fs/ufs_fs.h>
30 #include <sys/fs/ufs_inode.h>
31 #include <sys/sysmacros.h>
32 #include <sys/promif.h>
33
34 #include <sys/stat.h>
35 #include <sys/bootvfs.h>
36 #include <sys/bootdebug.h>
37 #include <sys/salib.h>
38 #include <sys/sacache.h>
39
40
41 int print_cache_stats = 0;
42
43 /*
44 * This fd is used when talking to the device file itself.
45 */
46 static fileid_t *head;
47 /*
48 * hooks into ufs logging support
49 */
50 extern void lufs_boot_init(fileid_t *);
51 extern void lufs_closeall(void);
52 extern void lufs_merge_deltas(fileid_t *);
53
54 /* Only got one of these...ergo, only 1 fs open at once */
55 /* static */
56 devid_t *ufs_devp;
57
58 struct dirinfo {
59 int loc;
60 fileid_t *fi;
61 };
62
63 /*
64 * Function prototypes
65 */
66 static int boot_ufs_mountroot(char *str);
67 static int boot_ufs_unmountroot(void);
68 static int boot_ufs_open(char *filename, int flags);
69 static int boot_ufs_close(int fd);
70 static ssize_t boot_ufs_read(int fd, caddr_t buf, size_t size);
71 static off_t boot_ufs_lseek(int, off_t, int);
72 static int boot_ufs_fstat(int fd, struct bootstat *stp);
73 static void boot_ufs_closeall(int flag);
74 static int boot_ufs_getdents(int fd, struct dirent *dep, unsigned size);
75
76 struct boot_fs_ops boot_ufs_ops = {
77 "ufs",
78 boot_ufs_mountroot,
79 boot_ufs_unmountroot,
80 boot_ufs_open,
81 boot_ufs_close,
82 boot_ufs_read,
83 boot_ufs_lseek,
84 boot_ufs_fstat,
85 boot_ufs_closeall,
86 boot_ufs_getdents
87 };
88
89 static ino_t find(fileid_t *filep, char *path);
90 static ino_t dlook(fileid_t *filep, char *path);
91 static daddr32_t sbmap(fileid_t *filep, daddr32_t bn);
92 static struct direct *readdir(struct dirinfo *dstuff);
93
94 /* These are the pools of buffers, etc. */
95 #define NBUFS (NIADDR+1)
96 /* Compilers like to play with alignment, so force the issue here */
97 static union {
98 char *blk[NBUFS];
99 daddr32_t *dummy;
100 } b;
101 daddr32_t blknos[NBUFS];
102
103 /*
104 * There is only 1 open (mounted) device at any given time.
105 * So we can keep a single, global devp file descriptor to
106 * use to index into the di[] array. This is not true for the
107 * fi[] array. We can have more than one file open at once,
108 * so there is no global fd for the fi[].
109 * The user program must save the fd passed back from open()
110 * and use it to do subsequent read()'s.
111 */
112
113 static int
openi(fileid_t * filep,ino_t inode)114 openi(fileid_t *filep, ino_t inode)
115 {
116 int retval;
117 struct dinode *dp;
118 devid_t *devp = filep->fi_devp;
119
120 /* Try the inode cache first */
121 if ((filep->fi_inode = get_icache(devp->di_dcookie, inode)) != NULL)
122 return (0);
123 /* Nope, not there so lets read it off the disk. */
124 filep->fi_offset = 0;
125 filep->fi_blocknum = fsbtodb(&devp->un_fs.di_fs,
126 itod(&devp->un_fs.di_fs, inode));
127
128 /* never more than 1 disk block */
129 filep->fi_count = devp->un_fs.di_fs.fs_bsize;
130 filep->fi_memp = filep->fi_buf;
131
132 /* Maybe the block is in the disk block cache */
133 if ((filep->fi_memp = get_bcache(filep)) == NULL) {
134 /* Not in the block cache so read it from disk */
135 if (retval = set_bcache(filep))
136 return (retval);
137 lufs_merge_deltas(filep);
138 }
139
140 dp = (struct dinode *)filep->fi_memp;
141 filep->fi_inode = (struct inode *)
142 bkmem_alloc(sizeof (struct inode));
143 bzero((char *)filep->fi_inode, sizeof (struct inode));
144 filep->fi_inode->i_ic =
145 dp[itoo(&devp->un_fs.di_fs, inode)].di_un.di_icom;
146 filep->fi_inode->i_number = inode;
147 if (set_ricache(devp->di_dcookie, inode, (void *)filep->fi_inode,
148 sizeof (struct inode)))
149 filep->fi_inode->i_flag = FI_NOCACHE;
150 return (0);
151 }
152
153 static fileid_t *
find_fp(int fd)154 find_fp(int fd)
155 {
156 fileid_t *filep = head;
157
158 if (fd >= 0) {
159 while ((filep = filep->fi_forw) != head)
160 if (fd == filep->fi_filedes)
161 return (filep->fi_taken ? filep : 0);
162 }
163
164 return (0);
165 }
166
167 static ino_t
find(fileid_t * filep,char * path)168 find(fileid_t *filep, char *path)
169 {
170 char *q;
171 char c;
172 ino_t inode;
173 char lpath[MAXPATHLEN];
174 char *lpathp = lpath;
175 int len, r;
176 devid_t *devp;
177
178 if (path == NULL || *path == '\0') {
179 printf("null path\n");
180 return ((ino_t)0);
181 }
182
183 bzero(lpath, sizeof (lpath));
184 bcopy(path, lpath, strlen(path));
185 devp = filep->fi_devp;
186 while (*lpathp) {
187 /* if at the beginning of pathname get root inode */
188 r = (lpathp == lpath);
189 if (r && openi(filep, (ino_t)UFSROOTINO))
190 return ((ino_t)0);
191 while (*lpathp == '/')
192 lpathp++; /* skip leading slashes */
193 q = lpathp;
194 while (*q != '/' && *q != '\0')
195 q++; /* find end of component */
196 c = *q;
197 *q = '\0'; /* terminate component */
198
199 /* Bail out early if opening root */
200 if (r && (*lpathp == '\0'))
201 return ((ino_t)UFSROOTINO);
202 if ((inode = dlook(filep, lpathp)) != 0) {
203 if (openi(filep, inode))
204 return ((ino_t)0);
205 if ((filep->fi_inode->i_smode & IFMT) == IFLNK) {
206 filep->fi_blocknum =
207 fsbtodb(&devp->un_fs.di_fs,
208 filep->fi_inode->i_db[0]);
209 filep->fi_count = DEV_BSIZE;
210 /* check the block cache */
211 if ((filep->fi_memp =
212 get_bcache(filep)) == NULL) {
213 if (set_bcache(filep))
214 return ((ino_t)0);
215 lufs_merge_deltas(filep);
216 }
217 len = strlen(filep->fi_memp);
218 if (filep->fi_memp[0] == '/')
219 /* absolute link */
220 lpathp = lpath;
221 /* copy rest of unprocessed path up */
222 bcopy(q, lpathp + len, strlen(q + 1) + 2);
223 /* point to unprocessed path */
224 *(lpathp + len) = c;
225 /* prepend link in before unprocessed path */
226 bcopy(filep->fi_memp, lpathp, len);
227 lpathp = lpath;
228 continue;
229 } else
230 *q = c;
231 if (c == '\0')
232 break;
233 lpathp = q;
234 continue;
235 } else {
236 return ((ino_t)0);
237 }
238 }
239 return (inode);
240 }
241
242 /*
243 * Map <file, file logical block> into a file system block number.
244 * Reads indirect blocks as needed to find the block. Returns zero when
245 * block isn't there; returns negative fsbn when block is uninitialized.
246 */
247 static daddr32_t
sbmap(fileid_t * filep,daddr32_t bn)248 sbmap(fileid_t *filep, daddr32_t bn)
249 {
250 struct inode *inodep;
251 int i, j, sh;
252 daddr32_t nb, *bap;
253 daddr32_t *db;
254 devid_t *devp;
255
256 devp = filep->fi_devp;
257 inodep = filep->fi_inode;
258 db = inodep->i_db;
259
260 /*
261 * blocks 0..NDADDR are direct blocks
262 */
263 if (bn < NDADDR) {
264 nb = db[bn];
265 return (nb);
266 }
267
268 /*
269 * addresses NIADDR have single and double indirect blocks.
270 * the first step is to determine how many levels of indirection.
271 */
272 sh = 1;
273 bn -= NDADDR;
274 for (j = NIADDR; j > 0; j--) {
275 sh *= NINDIR(&devp->un_fs.di_fs);
276 if (bn < sh)
277 break;
278 bn -= sh;
279 }
280 if (j == 0) {
281 return ((daddr32_t)0);
282 }
283
284 /*
285 * fetch the first indirect block address from the inode
286 */
287 nb = inodep->i_ib[NIADDR - j];
288 if (nb == 0) {
289 return ((daddr32_t)0);
290 }
291
292 /*
293 * fetch through the indirect blocks
294 */
295 for (; j <= NIADDR; j++) {
296 if (blknos[j] != nb) {
297 filep->fi_blocknum = fsbtodb(&devp->un_fs.di_fs, nb);
298 filep->fi_count = devp->un_fs.di_fs.fs_bsize;
299 /* First look through the disk block cache */
300 if ((filep->fi_memp = get_bcache(filep)) == NULL) {
301 if (set_bcache(filep)) /* Gotta do I/O */
302 return (0);
303 lufs_merge_deltas(filep);
304 }
305 b.blk[j] = filep->fi_memp;
306 blknos[j] = nb;
307 }
308 bap = (daddr32_t *)b.blk[j];
309 sh /= NINDIR(&devp->un_fs.di_fs);
310 i = (bn / sh) % NINDIR(&devp->un_fs.di_fs);
311 nb = bap[i];
312 if (nb == 0) {
313 return ((daddr32_t)0);
314 }
315 }
316 return (nb);
317 }
318
319 static ino_t
dlook(fileid_t * filep,char * path)320 dlook(fileid_t *filep, char *path)
321 {
322 devid_t *devp = filep->fi_devp;
323 struct direct *dp;
324 struct inode *ip;
325 struct dirinfo dirp;
326 int len;
327 ino_t in;
328 #ifdef DEBUG
329 static int warned = 0;
330 #endif
331
332 ip = filep->fi_inode;
333 if (path == NULL || *path == '\0')
334 return (0);
335 if ((ip->i_smode & IFMT) != IFDIR)
336 return (0);
337 if (ip->i_size == 0)
338 return (0);
339 len = strlen(path);
340
341 /*
342 * First look through the directory entry cache
343 */
344 if ((in = get_dcache(devp->di_dcookie, path, ip->i_number)) != 0)
345 return (in);
346
347 /*
348 * If the entire directory is cached, return failure
349 */
350 if (ip->i_flag & FI_CACHED)
351 return (0);
352
353 /*
354 * Otherwise, read the entire directory into the cache
355 */
356 in = 0;
357 dirp.loc = 0;
358 dirp.fi = filep;
359 if (!(ip->i_flag & FI_NOCACHE))
360 ip->i_flag |= FI_CACHED;
361 for (dp = readdir(&dirp); dp != NULL; dp = readdir(&dirp)) {
362 if (dp->d_ino == 0)
363 continue;
364 if (dp->d_namlen == len && strcmp(path, dp->d_name) == 0)
365 in = dp->d_ino;
366
367 /*
368 * Allow "*" to print all names at that level, w/out match
369 */
370 if (strcmp(path, "*") == 0)
371 printf("%s\n", dp->d_name);
372
373 if (ip->i_flag & FI_NOCACHE)
374 continue;
375
376 /*
377 * Put this entry into the cache. If the entry has been
378 * partially cached, check before inserting. This should be
379 * rare if sized correctly
380 */
381 if ((ip->i_flag & FI_PARTIAL_CACHE) &&
382 (get_dcache(devp->di_dcookie, dp->d_name, dp->d_ino) != 0))
383 continue;
384
385 if (set_rdcache(devp->di_dcookie, dp->d_name, ip->i_number,
386 dp->d_ino)) {
387 ip->i_flag &= ~FI_CACHED;
388 ip->i_flag |= FI_PARTIAL_CACHE;
389 #ifdef DEBUG
390 if (!warned) {
391 printf("ufsboot: directory cache too small\n");
392 warned++;
393 }
394 #endif
395 }
396 }
397 return (in);
398 }
399
400 /*
401 * get next entry in a directory.
402 */
403 struct direct *
readdir(struct dirinfo * dstuff)404 readdir(struct dirinfo *dstuff)
405 {
406 struct direct *dp;
407 fileid_t *filep;
408 daddr32_t lbn, d;
409 int off;
410 devid_t *devp;
411
412 filep = dstuff->fi;
413 devp = filep->fi_devp;
414 for (;;) {
415 if (dstuff->loc >= filep->fi_inode->i_size) {
416 return (NULL);
417 }
418 off = blkoff(&devp->un_fs.di_fs, dstuff->loc);
419 if (off == 0) {
420 lbn = lblkno(&devp->un_fs.di_fs, dstuff->loc);
421 d = sbmap(filep, lbn);
422
423 if (d <= 0)
424 return (NULL);
425
426 filep->fi_blocknum = fsbtodb(&devp->un_fs.di_fs, d);
427 filep->fi_count =
428 blksize(&devp->un_fs.di_fs, filep->fi_inode, lbn);
429 /* check the block cache */
430 if ((filep->fi_memp = get_bcache(filep)) == NULL) {
431 if (set_bcache(filep))
432 return (NULL);
433 lufs_merge_deltas(filep);
434 }
435 }
436 dp = (struct direct *)(filep->fi_memp + off);
437 dstuff->loc += dp->d_reclen;
438 if (dp->d_ino == 0)
439 continue;
440 return (dp);
441 }
442 }
443
444 /*
445 * Get the next block of data from the file. If possible, dma right into
446 * user's buffer
447 */
448 static int
getblock(fileid_t * filep,caddr_t buf,int count,int * rcount)449 getblock(fileid_t *filep, caddr_t buf, int count, int *rcount)
450 {
451 struct fs *fs;
452 caddr_t p;
453 int off, size, diff, zeroize;
454 daddr32_t lbn, fsbn;
455 devid_t *devp;
456 static int pos;
457 static char ind[] = "|/-\\"; /* that's entertainment? */
458 static int blks_read;
459 devp = filep->fi_devp;
460 p = filep->fi_memp;
461 if ((signed)filep->fi_count <= 0) {
462
463 /* find the amt left to be read in the file */
464 diff = filep->fi_inode->i_size - filep->fi_offset;
465 if (diff <= 0) {
466 printf("Short read\n");
467 return (-1);
468 }
469
470 fs = &devp->un_fs.di_fs;
471 /* which block (or frag) in the file do we read? */
472 lbn = lblkno(fs, filep->fi_offset);
473
474 /* which physical block on the device do we read? */
475 fsbn = sbmap(filep, lbn);
476
477 /*
478 * zero fsbn -> unallocated hole.
479 * negative fsbn -> allocated but uninitialized.
480 * since we only read from the fs, treat both the same.
481 */
482 zeroize = (fsbn <= 0);
483
484 filep->fi_blocknum = fsbtodb(fs, fsbn);
485
486 off = blkoff(fs, filep->fi_offset);
487
488 /* either blksize or fragsize */
489 size = blksize(fs, filep->fi_inode, lbn);
490 filep->fi_count = size;
491 filep->fi_memp = filep->fi_buf;
492
493 /*
494 * optimization if we are reading large blocks of data then
495 * we can go directly to user's buffer
496 */
497 *rcount = 0;
498 if (off == 0 && count >= size) {
499 filep->fi_memp = buf;
500 if (zeroize) {
501 bzero(buf, size);
502 } else if (diskread(filep)) {
503 return (-1);
504 }
505 *rcount = size;
506 filep->fi_count = 0;
507 read_opt++;
508 if ((blks_read++ & 0x3) == 0)
509 printf("%c\b", ind[pos++ & 3]);
510 return (0);
511 } else {
512 if (zeroize) {
513 bzero(filep->fi_memp, size);
514 } else if (diskread(filep))
515 return (-1);
516 }
517
518 /*
519 * round and round she goes (though not on every block..
520 * - OBP's take a fair bit of time to actually print stuff)
521 * On x86, the screen oriented bootconf program doesn't
522 * want this noise...
523 */
524 if ((blks_read++ & 0x3) == 0)
525 printf("%c\b", ind[pos++ & 3]);
526
527 if (filep->fi_offset - off + size >= filep->fi_inode->i_size)
528 filep->fi_count = diff + off;
529 filep->fi_count -= off;
530 p = &filep->fi_memp[off];
531 }
532 filep->fi_memp = p;
533 return (0);
534 }
535
536
537 /*
538 * This is the high-level read function. It works like this.
539 * We assume that our IO device buffers up some amount of
540 * data and that we can get a ptr to it. Thus we need
541 * to actually call the device func about filesize/blocksize times
542 * and this greatly increases our IO speed. When we already
543 * have data in the buffer, we just return that data (with bcopy() ).
544 */
545
546 static ssize_t
boot_ufs_read(int fd,caddr_t buf,size_t count)547 boot_ufs_read(int fd, caddr_t buf, size_t count)
548 {
549 size_t i, j;
550 caddr_t n;
551 int rcount;
552 fileid_t *filep;
553
554 if (!(filep = find_fp(fd))) {
555 return (-1);
556 }
557
558 if (filep->fi_offset + count > filep->fi_inode->i_size)
559 count = filep->fi_inode->i_size - filep->fi_offset;
560
561 /* that was easy */
562 if ((i = count) == 0)
563 return (0);
564
565 n = buf;
566 while (i > 0) {
567 /* If we need to reload the buffer, do so */
568 if ((j = filep->fi_count) == 0) {
569 getblock(filep, buf, i, &rcount);
570 i -= rcount;
571 buf += rcount;
572 filep->fi_offset += rcount;
573 } else {
574 /* else just bcopy from our buffer */
575 j = MIN(i, j);
576 bcopy(filep->fi_memp, buf, (unsigned)j);
577 buf += j;
578 filep->fi_memp += j;
579 filep->fi_offset += j;
580 filep->fi_count -= j;
581 i -= j;
582 }
583 }
584 return (buf - n);
585 }
586
587 /*
588 * This routine will open a device as it is known by the V2 OBP.
589 * Interface Defn:
590 * err = boot_ufs_mountroot(string);
591 * err = 0 on success
592 * err = -1 on failure
593 * string: char string describing the properties of the device.
594 * We must not dork with any fi[]'s here. Save that for later.
595 */
596
597 static int
boot_ufs_mountroot(char * str)598 boot_ufs_mountroot(char *str)
599 {
600 int h;
601
602 /*
603 * Open the device and setup the read of the ufs superblock
604 * only the first time mountroot is called. Subsequent calls
605 * to mountroot succeed immediatly
606 */
607 if (ufs_devp == NULL) {
608
609 /*
610 * Encode the knowledge that we normally boot from the 'a'
611 * slice of the leaf device on the OBP path; we also permit
612 * a 'nolabel' device, i.e. the entire device. Since v2path
613 * points to 'str' as well, changing str should have the
614 * desired result.
615 */
616 if (strchr(str, ':') == NULL) {
617 (void) strcat(str, ":a");
618 }
619 h = prom_open(str);
620 if (h == 0) {
621 printf("Cannot open %s\n", str);
622 return (-1);
623 }
624
625 ufs_devp = (devid_t *)bkmem_alloc(sizeof (devid_t));
626 ufs_devp->di_taken = 1;
627 ufs_devp->di_dcookie = h;
628 ufs_devp->di_desc = (char *)bkmem_alloc(strlen(str) + 1);
629 (void) strcpy(ufs_devp->di_desc, str);
630 bzero(ufs_devp->un_fs.dummy, SBSIZE);
631 head = (fileid_t *)bkmem_alloc(sizeof (fileid_t));
632 head->fi_back = head->fi_forw = head;
633 head->fi_filedes = 0;
634 head->fi_taken = 0;
635
636 /* Setup read of the superblock */
637 head->fi_devp = ufs_devp;
638 head->fi_blocknum = SBLOCK;
639 head->fi_count = (uint_t)SBSIZE;
640 head->fi_memp = (caddr_t)&(ufs_devp->un_fs.di_fs);
641 head->fi_offset = 0;
642
643 if (diskread(head) ||
644 ufs_devp->un_fs.di_fs.fs_magic != FS_MAGIC) {
645 boot_ufs_closeall(1);
646 return (-1);
647 }
648 lufs_boot_init(head);
649 }
650 return (0);
651 }
652
653 /*
654 * Unmount the currently mounted root fs. In practice, this means
655 * closing all open files and releasing resources. All of this
656 * is done by boot_ufs_closeall().
657 */
658
659 int
boot_ufs_unmountroot(void)660 boot_ufs_unmountroot(void)
661 {
662 if (ufs_devp == NULL)
663 return (-1);
664
665 boot_ufs_closeall(1);
666
667 return (0);
668 }
669
670 /*
671 * We allocate an fd here for use when talking
672 * to the file itself.
673 */
674
675 /*ARGSUSED*/
676 static int
boot_ufs_open(char * filename,int flags)677 boot_ufs_open(char *filename, int flags)
678 {
679 fileid_t *filep;
680 ino_t inode;
681 static int filedes = 1;
682
683 /* build and link a new file descriptor */
684 filep = (fileid_t *)bkmem_alloc(sizeof (fileid_t));
685 filep->fi_back = head->fi_back;
686 filep->fi_forw = head;
687 head->fi_back->fi_forw = filep;
688 head->fi_back = filep;
689 filep->fi_filedes = filedes++;
690 filep->fi_taken = 1;
691 filep->fi_path = (char *)bkmem_alloc(strlen(filename) + 1);
692 (void) strcpy(filep->fi_path, filename);
693 filep->fi_devp = ufs_devp; /* dev is already "mounted" */
694 filep->fi_inode = NULL;
695 bzero(filep->fi_buf, MAXBSIZE);
696
697 inode = find(filep, filename);
698 if (inode == (ino_t)0) {
699 boot_ufs_close(filep->fi_filedes);
700 return (-1);
701 }
702 if (openi(filep, inode)) {
703 boot_ufs_close(filep->fi_filedes);
704 return (-1);
705 }
706
707 filep->fi_offset = filep->fi_count = 0;
708
709 return (filep->fi_filedes);
710 }
711
712 /*
713 * We don't do any IO here.
714 * We just play games with the device pointers.
715 */
716
717 static off_t
boot_ufs_lseek(int fd,off_t addr,int whence)718 boot_ufs_lseek(int fd, off_t addr, int whence)
719 {
720 fileid_t *filep;
721
722 /* Make sure user knows what file he is talking to */
723 if (!(filep = find_fp(fd)))
724 return (-1);
725
726 switch (whence) {
727 case SEEK_CUR:
728 filep->fi_offset += addr;
729 break;
730 case SEEK_SET:
731 filep->fi_offset = addr;
732 break;
733 default:
734 case SEEK_END:
735 printf("ufs_lseek(): invalid whence value %d\n", whence);
736 break;
737 }
738
739 filep->fi_blocknum = addr / DEV_BSIZE;
740 filep->fi_count = 0;
741
742 return (0);
743 }
744
745 /*
746 * ufs_fstat() only supports size, mode, and times at present time.
747 */
748
749 static int
boot_ufs_fstat(int fd,struct bootstat * stp)750 boot_ufs_fstat(int fd, struct bootstat *stp)
751 {
752 fileid_t *filep;
753 struct inode *ip;
754
755 if (!(filep = find_fp(fd)))
756 return (-1);
757
758 ip = filep->fi_inode;
759
760 stp->st_mode = 0;
761 stp->st_size = 0;
762
763 if (ip == NULL)
764 return (0);
765
766 switch (ip->i_smode & IFMT) {
767 case IFDIR:
768 stp->st_mode = S_IFDIR;
769 break;
770 case IFLNK:
771 stp->st_mode = S_IFLNK;
772 break;
773 case IFREG:
774 stp->st_mode = S_IFREG;
775 break;
776 default:
777 break;
778 }
779 stp->st_size = ip->i_size;
780 stp->st_atim.tv_sec = ip->i_atime.tv_sec;
781 stp->st_atim.tv_nsec = ip->i_atime.tv_usec * 1000;
782 stp->st_mtim.tv_sec = ip->i_mtime.tv_sec;
783 stp->st_mtim.tv_nsec = ip->i_mtime.tv_usec * 1000;
784 stp->st_ctim.tv_sec = ip->i_ctime.tv_sec;
785 stp->st_ctim.tv_nsec = ip->i_ctime.tv_usec * 1000;
786
787 return (0);
788 }
789
790 static int
boot_ufs_close(int fd)791 boot_ufs_close(int fd)
792 {
793 fileid_t *filep;
794
795 /* Make sure user knows what file he is talking to */
796 if (!(filep = find_fp(fd)))
797 return (-1);
798
799 if (filep->fi_taken && (filep != head)) {
800 /* Clear the ranks */
801 bkmem_free(filep->fi_path, strlen(filep->fi_path)+1);
802 filep->fi_blocknum = filep->fi_count = filep->fi_offset = 0;
803 filep->fi_memp = (caddr_t)0;
804 filep->fi_devp = 0;
805 filep->fi_taken = 0;
806
807 /* unlink and deallocate node */
808 filep->fi_forw->fi_back = filep->fi_back;
809 filep->fi_back->fi_forw = filep->fi_forw;
810 bkmem_free((char *)filep, sizeof (fileid_t));
811
812 return (0);
813 } else {
814 /* Big problem */
815 printf("\nFile descrip %d not allocated!", fd);
816 return (-1);
817 }
818 }
819
820 /* closeall is now idempotent */
821 /*ARGSUSED*/
822 static void
boot_ufs_closeall(int flag)823 boot_ufs_closeall(int flag)
824 {
825 fileid_t *filep = head;
826
827 if (ufs_devp == NULL) {
828 if (head)
829 prom_panic("boot_ufs_closeall: head != NULL.\n");
830 return;
831 }
832
833 while ((filep = filep->fi_forw) != head)
834 if (filep->fi_taken)
835 if (boot_ufs_close(filep->fi_filedes))
836 prom_panic("Filesystem may be inconsistent.\n");
837
838
839 release_cache(ufs_devp->di_dcookie);
840 (void) prom_close(ufs_devp->di_dcookie);
841 ufs_devp->di_taken = 0;
842 if (verbosemode & print_cache_stats)
843 print_cache_data();
844 lufs_closeall();
845 bkmem_free((char *)ufs_devp, sizeof (devid_t));
846 bkmem_free((char *)head, sizeof (fileid_t));
847 ufs_devp = (devid_t *)NULL;
848 head = (fileid_t *)NULL;
849 }
850
851 static int
boot_ufs_getdents(int fd,struct dirent * dep,unsigned size)852 boot_ufs_getdents(int fd, struct dirent *dep, unsigned size)
853 {
854 /*
855 * Read directory entries from the file open on "fd" into the
856 * "size"-byte buffer at "dep" until the buffer is exhausted
857 * or we reach EOF on the directory. Returns the number of
858 * entries read.
859 */
860 int n;
861 fileid_t *fp;
862 unsigned long oldoff, oldblok;
863
864 #define SLOP (sizeof (struct dirent) - offsetof(struct dirent, d_name[1]))
865
866 if (fp = find_fp(fd)) {
867 /*
868 * File is open, check type to make sure it's a directory.
869 */
870
871 while ((fp->fi_inode->i_smode & IFMT) == IFLNK) {
872 /*
873 * If file is a symbolic link, we'll follow
874 * it JIC it points to a directory!
875 */
876 fileid_t fx;
877 char pn[MAXPATHLEN];
878 fp->fi_count = DEV_BSIZE;
879 fp->fi_blocknum = fsbtodb(&fp->fi_devp->un_fs.di_fs,
880 fp->fi_inode->i_db[0]);
881
882 /*
883 * Return failure if:
884 * (a) we get an I/O error reading the path name.
885 * (b) the path name points to a non-existant file,
886 * (c) we get an I/O error reading the target inode.
887 */
888 if ((fp->fi_memp = get_bcache(fp)) == NULL) {
889 if (set_bcache(fp))
890 return (-1);
891 lufs_merge_deltas(fp);
892 }
893 if (!(n = find(&fx, strcpy(pn, fp->fi_memp))) ||
894 openi(fp = &fx, n)) {
895 return (-1);
896 }
897 }
898
899 if ((fp->fi_inode->i_smode & IFMT) == IFDIR) {
900 /*
901 * If target file is a directory, go ahead
902 * and read it. This consists of making
903 * repeated calls to readdir() until we reach
904 * end-of-file or run out of buffer space.
905 */
906 int cnt = 0;
907 struct direct *dp;
908 struct dirinfo dir;
909
910 dir.fi = fp;
911 oldblok = fp->fi_blocknum;
912 dir.loc = oldoff = fp->fi_offset;
913
914 for (dp = readdir(&dir); dp; dp = readdir(&dir)) {
915 /*
916 * Read all directory entries in the file ...
917 */
918
919 if (dp->d_ino) {
920 /*
921 * Next entry is valid.
922 * Compute name length and
923 * break loop if there's not
924 * enough space in the output
925 * buffer for the next entry.
926 *
927 * NOTE: "SLOP" is the number
928 * of bytes inserted into the
929 * dirent struct's "d_name"
930 * field by the compiler to
931 * preserve alignment.
932 */
933 dep->d_ino = dp->d_ino;
934 n = strlen(dp->d_name);
935 n = roundup((sizeof (struct dirent) +
936 ((n > SLOP) ? n : 0)),
937 sizeof (off_t));
938
939 if (n > size)
940 break; /* user buffer is full */
941
942 oldblok = fp->fi_blocknum;
943 oldoff = dir.loc;
944 size -= n;
945 cnt += 1;
946
947 (void) strcpy(dep->d_name, dp->d_name);
948 dep->d_off = dir.loc;
949 dep->d_reclen = (ushort_t)n;
950
951 dep = (struct dirent *)
952 ((char *)dep + n);
953 }
954 }
955 /*
956 * Remember where we left off for next time
957 */
958 fp->fi_blocknum = oldblok;
959 fp->fi_offset = oldoff;
960
961 return (cnt);
962 }
963 }
964
965 #undef SLOP
966
967 return (-1);
968 }
969