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