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