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 2007 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 #include <sys/filep.h>
36 #include <sys/salib.h>
37 #include <sys/sacache.h>
38
39 #include <sys/fs/hsfs_spec.h>
40 #include <sys/fs/hsfs_isospec.h>
41 #include <sys/fs/hsfs_node.h>
42 #include <sys/fs/hsfs_susp.h>
43 #include <sys/fs/hsfs_rrip.h>
44
45 #include "hsfs_sig.h"
46
47 #include <sys/stat.h>
48 #include <sys/bootvfs.h>
49 #include <sys/bootconf.h>
50 #include <sys/bootdebug.h>
51
52 #define hdbtodb(n) ((ISO_SECTOR_SIZE / DEV_BSIZE) * (n))
53
54 #define THE_EPOCH 1970
55 #define END_OF_TIME 2099
56
57 /* May not need this... */
58 static uint_t sua_offset = 0;
59
60 /* The root inode on an HSFS filesystem can be anywhere! */
61 static uint_t root_ino = 0; /* This is both a flag and a value */
62
63 static fileid_t *head;
64
65 /* Only got one of these...ergo, only 1 fs open at once */
66 static devid_t *devp;
67
68 struct dirinfo {
69 int loc;
70 fileid_t *fi;
71 };
72
73 struct hs_direct {
74 struct direct hs_ufs_dir;
75 struct hs_direntry hs_dir;
76 };
77
78 /*
79 * Function prototypes
80 */
81
82 static int boot_hsfs_mountroot(char *str);
83 static int boot_hsfs_unmountroot(void);
84 static int boot_hsfs_open(char *filename, int flags);
85 static int boot_hsfs_close(int fd);
86 static ssize_t boot_hsfs_read(int fd, caddr_t buf, size_t size);
87 static off_t boot_hsfs_lseek(int, off_t, int);
88 static int boot_hsfs_fstat(int fd, struct bootstat *stp);
89 static void boot_hsfs_closeall(int flag);
90 static int boot_hsfs_getdents(int fd, struct dirent *dep, unsigned size);
91
92 struct boot_fs_ops boot_hsfs_ops = {
93 "hsfs",
94 boot_hsfs_mountroot,
95 boot_hsfs_unmountroot,
96 boot_hsfs_open,
97 boot_hsfs_close,
98 boot_hsfs_read,
99 boot_hsfs_lseek,
100 boot_hsfs_fstat,
101 boot_hsfs_closeall,
102 boot_hsfs_getdents
103 };
104
105 static ino_t find(fileid_t *, char *);
106 static ino_t dlook(fileid_t *, char *);
107 static int opendir(fileid_t *, ino_t);
108 static struct hs_direct *readdir(struct dirinfo *);
109 static uint_t parse_dir(fileid_t *, int, struct hs_direct *);
110 static uint_t parse_susp(char *, uint_t *, struct hs_direct *);
111 static void hs_seti(fileid_t *, struct hs_direct *, ino_t);
112 static void hs_dodates(enum hs_vol_type, struct hs_direntry *, char *);
113 static time_t hs_date_to_gmtime(int, int, int, int);
114
115 /*
116 * There is only 1 open (mounted) device at any given time.
117 * So we can keep a single, global devp file descriptor to
118 * use to index into the di[] array. This is not true for the
119 * fi[] array. We can have more than one file open at once,
120 * so there is no global fd for the fi[].
121 * The user program must save the fd passed back from open()
122 * and use it to do subsequent read()'s.
123 */
124
125 static int
opendir(fileid_t * filep,ino_t inode)126 opendir(fileid_t *filep, ino_t inode)
127 {
128 struct hs_direct hsdep;
129 int retval;
130
131 /* Set up the saio request */
132 filep->fi_offset = 0;
133 filep->fi_blocknum = hdbtodb(inode);
134 filep->fi_count = ISO_SECTOR_SIZE;
135
136 /* Maybe the block is in the disk block cache */
137 if ((filep->fi_memp = get_bcache(filep)) == NULL) {
138 /* Not in the block cache so read it from disk */
139 if (retval = set_bcache(filep)) {
140 return (retval);
141 }
142 }
143
144 filep->fi_offset = 0;
145 filep->fi_blocknum = hdbtodb(inode);
146
147 if (inode != root_ino)
148 return (0);
149
150 if ((int)(parse_dir(filep, 0, &hsdep)) > 0) {
151 hs_seti(filep, &hsdep, inode);
152 return (0);
153 }
154 return (1);
155 }
156
157 static ino_t
find(fileid_t * filep,char * path)158 find(fileid_t *filep, char *path)
159 {
160 register char *q;
161 char c;
162 ino_t inode;
163
164 if (path == NULL || *path == '\0') {
165 printf("null path\n");
166 return (0);
167 }
168
169 if ((boothowto & RB_DEBUG) && (boothowto & RB_VERBOSE))
170 printf("find(): path=<%s>\n", path);
171
172 /* Read the ROOT directory */
173 if (opendir(filep, inode = root_ino)) {
174 printf("find(): root_ino opendir() failed!\n");
175 return ((ino_t)-1);
176 }
177
178 while (*path) {
179 while (*path == '/')
180 path++;
181 if (*(q = path) == '\0')
182 break;
183 while (*q != '/' && *q != '\0')
184 q++;
185 c = *q;
186 *q = '\0';
187
188 if ((inode = dlook(filep, path)) != 0) {
189 if (c == '\0')
190 break;
191 if (opendir(filep, inode)) {
192 printf("find(): opendir(%d) failed!\n", inode);
193 *q = c;
194 return ((ino_t)-1);
195 }
196 *q = c;
197 path = q;
198 continue;
199 } else {
200 *q = c;
201 return (0);
202 }
203 }
204 return (inode);
205 }
206
207 static fileid_t *
find_fp(int fd)208 find_fp(int fd)
209 {
210 fileid_t *filep = head;
211
212 if (fd >= 0) {
213 while ((filep = filep->fi_forw) != head)
214 if (fd == filep->fi_filedes)
215 return (filep->fi_taken ? filep : 0);
216 }
217
218 return (0);
219 }
220
221 static ino_t
dlook(fileid_t * filep,char * path)222 dlook(fileid_t *filep, char *path)
223 {
224 int dv = filep->fi_devp->di_dcookie;
225 register struct hs_direct *hsdep;
226 register struct direct *udp;
227 register struct inode *ip;
228 struct dirinfo dirp;
229 register int len;
230 ino_t in;
231
232 ip = filep->fi_inode;
233 if (path == NULL || *path == '\0')
234 return (0);
235 if ((ip->i_smode & IFMT) != IFDIR) {
236 return (0);
237 }
238 if (ip->i_size == 0) {
239 return (0);
240 }
241 len = strlen(path);
242 /* first look through the directory entry cache */
243 if (in = get_dcache(dv, path, ip->i_number)) {
244 if ((filep->fi_inode = get_icache(dv, in)) != NULL) {
245 filep->fi_offset = 0;
246 filep->fi_blocknum = hdbtodb(in);
247 return (in);
248 }
249 }
250 dirp.loc = 0;
251 dirp.fi = filep;
252 for (hsdep = readdir(&dirp); hsdep != NULL; hsdep = readdir(&dirp)) {
253 udp = &hsdep->hs_ufs_dir;
254 if (udp->d_namlen == 1 &&
255 udp->d_name[0] == '.' &&
256 udp->d_name[1] == '\0')
257 continue;
258 if (udp->d_namlen == 2 &&
259 udp->d_name[0] == '.' &&
260 udp->d_name[1] == '.' &&
261 udp->d_name[2] == '\0')
262 continue;
263 if (udp->d_namlen == len && (strcmp(path, udp->d_name) == 0)) {
264 set_dcache(dv, path, ip->i_number, udp->d_ino);
265 hs_seti(filep, hsdep, udp->d_ino);
266 filep->fi_offset = 0;
267 filep->fi_blocknum = hdbtodb(udp->d_ino);
268 /* put this entry into the cache */
269 return (udp->d_ino);
270 }
271 /* Allow "*" to print all names at that level, w/out match */
272 if (strcmp(path, "*") == 0)
273 printf("%s\n", udp->d_name);
274 }
275 return (0);
276 }
277
278 /*
279 * get next entry in a directory.
280 */
281 static struct hs_direct *
readdir(struct dirinfo * dirp)282 readdir(struct dirinfo *dirp)
283 {
284 static struct hs_direct hsdep;
285 register struct direct *udp = &hsdep.hs_ufs_dir;
286 register struct inode *ip;
287 register fileid_t *filep;
288 register daddr_t lbn;
289 register int off;
290
291 filep = dirp->fi;
292 ip = filep->fi_inode;
293 for (;;) {
294 if (dirp->loc >= ip->i_size) {
295 return (NULL);
296 }
297 off = dirp->loc & ((1 << ISO_SECTOR_SHIFT) - 1);
298 if (off == 0) {
299 lbn = hdbtodb(dirp->loc >> ISO_SECTOR_SHIFT);
300 filep->fi_blocknum = lbn + hdbtodb(ip->i_number);
301 filep->fi_count = ISO_SECTOR_SIZE;
302 /* check the block cache */
303 if ((filep->fi_memp = get_bcache(filep)) == 0)
304 if (set_bcache(filep))
305 return ((struct hs_direct *)-1);
306 }
307 dirp->loc += parse_dir(filep, off, &hsdep);
308 if (udp->d_reclen == 0 && dirp->loc <= ip->i_size) {
309 dirp->loc = roundup(dirp->loc, ISO_SECTOR_SIZE);
310 continue;
311 }
312 return (&hsdep);
313 }
314 }
315
316 /*
317 * Get the next block of data from the file. If possible, dma right into
318 * user's buffer
319 */
320 static int
getblock(fileid_t * filep,caddr_t buf,int count,int * rcount)321 getblock(fileid_t *filep, caddr_t buf, int count, int *rcount)
322 {
323 register struct inode *ip;
324 register caddr_t p;
325 register int off, size, diff;
326 register daddr_t lbn;
327 static int pos;
328 static char ind[] = "|/-\\"; /* that's entertainment? */
329 static int blks_read;
330
331 ip = filep->fi_inode;
332 p = filep->fi_memp;
333 if ((signed)filep->fi_count <= 0) {
334
335 /* find the amt left to be read in the file */
336 diff = ip->i_size - filep->fi_offset;
337 if (diff <= 0) {
338 printf("Short read\n");
339 return (-1);
340 }
341
342 /* which block (or frag) in the file do we read? */
343 lbn = hdbtodb(filep->fi_offset >> ISO_SECTOR_SHIFT);
344
345 /* which physical block on the device do we read? */
346 filep->fi_blocknum = lbn + hdbtodb(ip->i_number);
347
348 off = filep->fi_offset & ((1 << ISO_SECTOR_SHIFT) - 1);
349
350 size = sizeof (filep->fi_buf);
351 if (size > ISO_SECTOR_SIZE)
352 size = ISO_SECTOR_SIZE;
353
354 filep->fi_count = size;
355 filep->fi_memp = filep->fi_buf;
356
357 /*
358 * optimization if we are reading large blocks of data then
359 * we can go directly to user's buffer
360 */
361 *rcount = 0;
362 if (off == 0 && count >= size) {
363 filep->fi_memp = buf;
364 if (diskread(filep)) {
365 return (-1);
366 }
367 *rcount = size;
368 filep->fi_count = 0;
369 read_opt++;
370 if ((blks_read++ & 0x3) == 0)
371 printf("%c\b", ind[pos++ & 3]);
372 return (0);
373 } else
374 if (diskread(filep))
375 return (-1);
376
377 /*
378 * round and round she goes (though not on every block..
379 * - OBP's take a fair bit of time to actually print stuff)
380 */
381 if ((blks_read++ & 0x3) == 0)
382 printf("%c\b", ind[pos++ & 3]);
383
384 if (filep->fi_offset - off + size >= ip->i_size)
385 filep->fi_count = diff + off;
386 filep->fi_count -= off;
387 p = &filep->fi_memp[off];
388 }
389 filep->fi_memp = p;
390 return (0);
391 }
392
393
394 /*
395 * This is the high-level read function. It works like this.
396 * We assume that our IO device buffers up some amount of
397 * data ant that we can get a ptr to it. Thus we need
398 * to actually call the device func about filesize/blocksize times
399 * and this greatly increases our IO speed. When we already
400 * have data in the buffer, we just return that data (with bcopy() ).
401 */
402
403 static ssize_t
boot_hsfs_read(int fd,caddr_t buf,size_t count)404 boot_hsfs_read(int fd, caddr_t buf, size_t count)
405 {
406 size_t i, j;
407 struct inode *ip;
408 caddr_t n;
409 fileid_t *filep;
410 int rcount;
411
412 if (!(filep = find_fp(fd))) {
413 return (-1);
414 }
415
416 ip = filep->fi_inode;
417
418 if (filep->fi_offset + count > ip->i_size)
419 count = ip->i_size - filep->fi_offset;
420
421 /* that was easy */
422 if ((i = count) == 0)
423 return (0);
424
425 n = buf;
426 while (i > 0) {
427 /* If we need to reload the buffer, do so */
428 if ((j = filep->fi_count) == 0) {
429 getblock(filep, buf, i, &rcount);
430 i -= rcount;
431 buf += rcount;
432 filep->fi_offset += rcount;
433 } else {
434 /* else just bcopy from our buffer */
435 j = MIN(i, j);
436 bcopy(filep->fi_memp, buf, (unsigned)j);
437 buf += j;
438 filep->fi_memp += j;
439 filep->fi_offset += j;
440 filep->fi_count -= j;
441 i -= j;
442 }
443 }
444 return (buf - n);
445 }
446
447 /*
448 * This routine will open a device as it is known by the
449 * V2 OBP.
450 * Interface Defn:
451 * err = mountroot(string);
452 * err: 0 on success
453 * -1 on failure
454 * string: char string describing the properties of the device.
455 * We must not dork with any fi[]'s here. Save that for later.
456 */
457
458 static int
boot_hsfs_mountroot(char * str)459 boot_hsfs_mountroot(char *str)
460 {
461 ihandle_t h;
462 struct hs_volume *fsp;
463 char *bufp;
464
465 if ((boothowto & RB_DEBUG) && (boothowto & RB_VERBOSE))
466 printf("mountroot()\n");
467
468 /*
469 * If already mounted, just return success.
470 */
471 if (root_ino != 0) {
472 return (0);
473 }
474
475 h = prom_open(str);
476
477 if (h == 0) {
478 printf("Cannot open %s\n", str);
479 return (-1);
480 }
481
482 devp = (devid_t *)bkmem_alloc(sizeof (devid_t));
483 devp->di_taken = 1;
484 devp->di_dcookie = h;
485 devp->di_desc = (char *)bkmem_alloc(strlen(str) + 1);
486 (void) strcpy(devp->di_desc, str);
487 bzero(devp->un_fs.dummy, sizeof (devp->un_fs.dummy));
488 head = (fileid_t *)bkmem_alloc(sizeof (fileid_t));
489 head->fi_back = head->fi_forw = head;
490 head->fi_filedes = 0;
491 head->fi_taken = 0;
492
493 /* Setup read of the "superblock" */
494 bzero(head->fi_buf, sizeof (head->fi_buf));
495 head->fi_devp = devp;
496 head->fi_blocknum = hdbtodb(ISO_VOLDESC_SEC);
497 head->fi_count = ISO_SECTOR_SIZE;
498 head->fi_memp = head->fi_buf;
499 head->fi_offset = 0;
500
501 if (diskread(head)) {
502 printf("mountroot(): read super block failed!\n");
503 boot_hsfs_closeall(1);
504 return (-1);
505 }
506
507 bufp = head->fi_memp;
508 fsp = (struct hs_volume *)devp->un_fs.dummy;
509 /* Since RRIP is based on ISO9660, that's where we start */
510
511 if (ISO_DESC_TYPE(bufp) != ISO_VD_PVD ||
512 strncmp((char *)(ISO_std_id(bufp)), (char *)(ISO_ID_STRING),
513 ISO_ID_STRLEN) != 0 || ISO_STD_VER(bufp) != ISO_ID_VER) {
514 boot_hsfs_closeall(1);
515 return (-1);
516 }
517
518 /* Now we fill in the volume descriptor */
519 fsp->vol_size = ISO_VOL_SIZE(bufp);
520 fsp->lbn_size = ISO_BLK_SIZE(bufp);
521 fsp->lbn_shift = ISO_SECTOR_SHIFT;
522 fsp->lbn_secshift = ISO_SECTOR_SHIFT;
523 fsp->vol_set_size = (ushort_t)ISO_SET_SIZE(bufp);
524 fsp->vol_set_seq = (ushort_t)ISO_SET_SEQ(bufp);
525
526 /* Make sure we have a valid logical block size */
527 if (fsp->lbn_size & ~(1 << fsp->lbn_shift)) {
528 printf("%d byte logical block size invalid.\n", fsp->lbn_size);
529 boot_hsfs_closeall(1);
530 return (-1);
531 }
532
533 /* Since an HSFS root could be located anywhere on the media! */
534 root_ino = IDE_EXT_LBN(ISO_root_dir(bufp));
535
536 if ((boothowto & RB_DEBUG) && (boothowto & RB_VERBOSE)) {
537 int i;
538
539 printf("root_ino=%d\n", root_ino);
540 printf("ID=");
541 for (i = 0; i < ISO_ID_STRLEN; i++)
542 printf("%c", *(ISO_std_id(bufp)+i));
543 printf(" VS=%d\n", fsp->vol_size);
544 }
545
546 return (0);
547 }
548
549 /*
550 * Unmount the currently mounted root fs. In practice, this means
551 * closing all open files and releasing resources. All of this
552 * is done by boot_hsfs_closeall().
553 */
554
555 int
boot_hsfs_unmountroot(void)556 boot_hsfs_unmountroot(void)
557 {
558 if (root_ino == 0)
559 return (-1);
560
561 boot_hsfs_closeall(1);
562
563 return (0);
564 }
565
566 /*
567 * We allocate an fd here for use when talking
568 * to the file itself.
569 */
570
571 /*ARGSUSED*/
572 static int
boot_hsfs_open(char * filename,int flags)573 boot_hsfs_open(char *filename, int flags)
574 {
575 fileid_t *filep;
576 ino_t inode;
577 static int filedes = 1;
578
579 /* build and link a new file descriptor */
580 filep = (fileid_t *)bkmem_alloc(sizeof (fileid_t));
581 filep->fi_back = head->fi_back;
582 filep->fi_forw = head;
583 head->fi_back->fi_forw = filep;
584 head->fi_back = filep;
585
586 filep->fi_filedes = filedes++;
587 filep->fi_taken = 1;
588 filep->fi_path = (char *)bkmem_alloc(strlen(filename) + 1);
589 (void) strcpy(filep->fi_path, filename);
590 filep->fi_devp = devp; /* dev is already "mounted" */
591
592 filep->fi_inode = 0;
593
594 inode = find(filep, filename);
595 if (inode == (ino_t)0) {
596 if ((boothowto & RB_DEBUG) && (boothowto & RB_VERBOSE))
597 printf("open(%s) ENOENT\n", filename);
598 (void) boot_hsfs_close(filep->fi_filedes);
599 return (-1);
600 }
601
602 filep->fi_blocknum = hdbtodb(inode);
603 filep->fi_offset = filep->fi_count = 0;
604
605 if ((boothowto & RB_DEBUG) && (boothowto & RB_VERBOSE))
606 printf("open(%s) fd=%d\n", filename, filep->fi_filedes);
607 return (filep->fi_filedes);
608 }
609
610 /*
611 * hsfs_fstat() only supports size, mode and times at present time.
612 */
613
614 static int
boot_hsfs_fstat(int fd,struct bootstat * stp)615 boot_hsfs_fstat(int fd, struct bootstat *stp)
616 {
617 fileid_t *filep;
618 struct inode *ip;
619
620 if (!(filep = find_fp(fd)))
621 return (-1);
622
623 ip = filep->fi_inode;
624
625 stp->st_mode = 0;
626 stp->st_size = 0;
627
628 if (ip == NULL)
629 return (0);
630
631 switch (ip->i_smode & IFMT) {
632 case IFDIR:
633 stp->st_mode = S_IFDIR;
634 break;
635 case IFREG:
636 stp->st_mode = S_IFREG;
637 break;
638 default:
639 break;
640 }
641 stp->st_size = ip->i_size;
642
643 /* file times */
644 stp->st_atim.tv_sec = ip->i_atime.tv_sec;
645 stp->st_atim.tv_nsec = ip->i_atime.tv_usec * 1000;
646 stp->st_mtim.tv_sec = ip->i_mtime.tv_sec;
647 stp->st_mtim.tv_nsec = ip->i_mtime.tv_usec * 1000;
648 stp->st_ctim.tv_sec = ip->i_ctime.tv_sec;
649 stp->st_ctim.tv_nsec = ip->i_ctime.tv_usec * 1000;
650
651 return (0);
652 }
653
654 /*
655 * We don't do any IO here.
656 * We just play games with the device pointers.
657 */
658
659 /*ARGSUSED*/
660 static off_t
boot_hsfs_lseek(int fd,off_t addr,int whence)661 boot_hsfs_lseek(int fd, off_t addr, int whence)
662 {
663 fileid_t *filep;
664
665 if (!(filep = find_fp(fd)))
666 return (-1);
667
668 filep->fi_offset = addr;
669 filep->fi_blocknum = addr / DEV_BSIZE;
670 filep->fi_count = 0;
671
672 return (0);
673 }
674
675 static int
boot_hsfs_close(int fd)676 boot_hsfs_close(int fd)
677 {
678 fileid_t *filep;
679
680 if ((boothowto & RB_DEBUG) && (boothowto & RB_VERBOSE))
681 printf("close(%d)\n", fd);
682
683 if (filep = find_fp(fd)) {
684 /* Clear the ranks */
685 bkmem_free(filep->fi_path, strlen(filep->fi_path)+1);
686 filep->fi_blocknum = filep->fi_count = filep->fi_offset = 0;
687 filep->fi_memp = (caddr_t)0;
688 filep->fi_devp = 0;
689 filep->fi_taken = 0;
690
691 /* unlink and deallocate node */
692 filep->fi_forw->fi_back = filep->fi_back;
693 filep->fi_back->fi_forw = filep->fi_forw;
694 bkmem_free((char *)filep, sizeof (fileid_t));
695
696 return (0);
697 } else {
698 /* Big problem */
699 printf("\nFile descrip %d not allocated!", fd);
700 return (-1);
701 }
702 }
703
704 /* closeall is now idempotent */
705 /*ARGSUSED*/
706 static void
boot_hsfs_closeall(int flag)707 boot_hsfs_closeall(int flag)
708 {
709 fileid_t *filep = head;
710 extern int verbosemode;
711
712 if (devp == NULL) {
713 if (head)
714 prom_panic("boot_hsfs_closeall: head != NULL.\n");
715 return;
716 }
717
718 while ((filep = filep->fi_forw) != head)
719 if (filep->fi_taken)
720 if (boot_hsfs_close(filep->fi_filedes))
721 prom_panic("Filesystem may be inconsistent.\n");
722
723
724 release_cache(devp->di_dcookie);
725 (void) prom_close(devp->di_dcookie);
726 devp->di_taken = 0;
727 if (verbosemode)
728 print_cache_data();
729 bkmem_free((char *)devp, sizeof (devid_t));
730 bkmem_free((char *)head, sizeof (fileid_t));
731 root_ino = 0;
732 devp = NULL;
733 head = NULL;
734 }
735
736 static uint_t
parse_dir(fileid_t * filep,int offset,struct hs_direct * hsdep)737 parse_dir(fileid_t *filep, int offset, struct hs_direct *hsdep)
738 {
739 char *bufp = (char *)(filep->fi_memp + offset);
740 struct direct *udp = &hsdep->hs_ufs_dir;
741 struct hs_direntry *hdp = &hsdep->hs_dir;
742 uint_t ce_lbn;
743 uint_t ce_len;
744 uint_t nmlen;
745 uint_t i;
746 uchar_t c;
747 int ret_code = 0;
748
749 if ((udp->d_reclen = IDE_DIR_LEN(bufp)) == 0)
750 return (0);
751
752 hdp->ext_lbn = IDE_EXT_LBN(bufp);
753 hdp->ext_size = IDE_EXT_SIZE(bufp);
754 hs_dodates(HS_VOL_TYPE_ISO, hdp, bufp);
755 hdp->xar_len = IDE_XAR_LEN(bufp);
756 hdp->intlf_sz = IDE_INTRLV_SIZE(bufp);
757 hdp->intlf_sk = IDE_INTRLV_SKIP(bufp);
758 hdp->sym_link = NULL;
759
760 udp->d_ino = hdp->ext_lbn;
761
762 c = IDE_FLAGS(bufp);
763 if (IDE_REGULAR_FILE(c)) {
764 hdp->type = VREG;
765 hdp->mode = IFREG;
766 hdp->nlink = 1;
767 } else if (IDE_REGULAR_DIR(c)) {
768 hdp->type = VDIR;
769 hdp->mode = IFDIR;
770 hdp->nlink = 2;
771 } else {
772 printf("parse_dir(): file type=0x%x unknown.\n", c);
773 return ((uint_t)-1);
774 }
775
776 /* Some initial conditions */
777 nmlen = IDE_NAME_LEN(bufp);
778 c = *IDE_NAME(bufp);
779 /* Special Case: Current Directory */
780 if (nmlen == 1 && c == '\0') {
781 udp->d_name[0] = '.';
782 udp->d_name[1] = '\0';
783 udp->d_namlen = 1;
784 /* Special Case: Parent Directory */
785 } else if (nmlen == 1 && c == '\001') {
786 udp->d_name[0] = '.';
787 udp->d_name[1] = '.';
788 udp->d_name[2] = '\0';
789 udp->d_namlen = 2;
790 /* Other file name */
791 } else {
792 udp->d_namlen = 0;
793 for (i = 0; i < nmlen; i++) {
794 c = *(IDE_name(bufp)+i);
795 if (c == ';')
796 break;
797 else if (c == ' ')
798 continue;
799 else
800 udp->d_name[udp->d_namlen++] = c;
801 }
802 udp->d_name[udp->d_namlen] = '\0';
803 }
804 /* System Use Fields */
805 ce_len = IDE_SUA_LEN(bufp);
806 ce_lbn = 0;
807 if ((int)(ce_len) > 0) {
808 ce_lbn = parse_susp((char *)IDE_sys_use_area(bufp),
809 &ce_len, hsdep);
810 while (ce_lbn) {
811 daddr_t save_blocknum = filep->fi_blocknum;
812 daddr_t save_offset = filep->fi_offset;
813 caddr_t save_memp = filep->fi_memp;
814 uint_t save_count = filep->fi_count;
815
816 #ifdef noisy
817 print_io_req(filep, "parse_dir(): [I]");
818 #endif /* noisy */
819
820 filep->fi_blocknum = hdbtodb(ce_lbn);
821 filep->fi_offset = 0;
822 filep->fi_count = ISO_SECTOR_SIZE;
823
824 #ifdef noisy
825 print_io_req(filep, "parse_dir(): [0]");
826 #endif /* noisy */
827
828 if ((filep->fi_memp = get_bcache(filep)) == 0)
829 ret_code = set_bcache(filep);
830
831 #ifdef noisy
832 print_io_req(filep, "parse_dir(): [1]");
833 #endif /* noisy */
834
835 if (ret_code) {
836 filep->fi_blocknum = save_blocknum;
837 filep->fi_offset = save_offset;
838 filep->fi_memp = save_memp;
839 filep->fi_count = save_count;
840 printf("parse_dir(): "
841 "set_bcache() failed (%d)\n", ret_code);
842 break;
843 }
844 ce_lbn = parse_susp(filep->fi_memp, &ce_len, hsdep);
845
846 filep->fi_blocknum = save_blocknum;
847 filep->fi_offset = save_offset;
848 filep->fi_memp = save_memp;
849 filep->fi_count = save_count;
850
851 #ifdef noisy
852 print_io_req(filep, "parse_dir(): [2]");
853 #endif /* noisy */
854 }
855 }
856
857 return (udp->d_reclen);
858 }
859
860 static uint_t
parse_susp(char * bufp,uint_t * ce_len,struct hs_direct * hsdep)861 parse_susp(char *bufp, uint_t *ce_len, struct hs_direct *hsdep)
862 {
863 struct direct *udp = &hsdep->hs_ufs_dir;
864 uchar_t *susp;
865 uint_t cur_off = 0;
866 uint_t blk_len = *ce_len;
867 uint_t susp_len = 0;
868 uint_t ce_lbn = 0;
869 uint_t i;
870
871 while (cur_off < blk_len) {
872 susp = (uchar_t *)(bufp + cur_off);
873 if (susp[0] == '\0' || susp[1] == '\0')
874 break;
875 susp_len = SUF_LEN(susp);
876 if (susp_len == 0)
877 break;
878 for (i = 0; i < hsfs_num_sig; i++) {
879 if (strncmp(hsfs_sig_tab[i],
880 (char *)susp, SUF_SIG_LEN) == 0) {
881 #ifdef noisy
882 if ((boothowto & RB_DEBUG) &&
883 (boothowto & RB_VERBOSE))
884 printf(" SUSP_%c%c %d\n",
885 susp[0], susp[1], susp_len);
886 #endif /* noisy */
887 switch (i) {
888 case SUSP_SP_IX:
889 if (CHECK_BYTES_OK(susp)) {
890 sua_offset =
891 SP_SUA_OFFSET(susp);
892 #ifdef lint
893 /* this may not be needed */
894 i = (int)sua_offset;
895 #endif /* lint */
896 }
897 break;
898
899 case SUSP_CE_IX:
900 ce_lbn = CE_BLK_LOC(susp);
901 *ce_len = CE_CONT_LEN(susp);
902 #ifdef noisy
903 if ((boothowto & RB_DEBUG) &&
904 (boothowto & RB_VERBOSE))
905 printf("parse_susp(): "
906 "CE: ce_lbn = %d "
907 "ce_len=%d\n",
908 ce_lbn, *ce_len);
909 #endif /* noisy */
910 break;
911
912 case SUSP_ST_IX:
913 printf("parse_susp(): ST: returning "
914 "%d\n", ce_lbn);
915 return (ce_lbn);
916
917 case RRIP_SL_IX:
918 #ifdef noisy
919 if ((boothowto & RB_DEBUG) &&
920 (boothowto & RB_VERBOSE))
921 printf("parse_susp(): "
922 "******* SL *******\n");
923 #endif /* noisy */
924 break;
925
926 case RRIP_RR_IX:
927 break;
928
929 case RRIP_NM_IX:
930 if (!RRIP_NAME_FLAGS(susp)) {
931 udp->d_namlen =
932 RRIP_NAME_LEN(susp);
933 bcopy((char *)RRIP_name(susp),
934 (char *)udp->d_name,
935 udp->d_namlen);
936 udp->d_name
937 [udp->d_namlen] = '\0';
938 }
939 break;
940 }
941 cur_off += susp_len;
942 break;
943 }
944 }
945 if (i > hsfs_num_sig) {
946 printf("parse_susp(): Bad SUSP\n");
947 cur_off = blk_len;
948 break;
949 }
950 }
951 return (ce_lbn);
952 }
953
954 static void
hs_seti(fileid_t * filep,struct hs_direct * hsdep,ino_t inode)955 hs_seti(fileid_t *filep, struct hs_direct *hsdep, ino_t inode)
956 {
957 register struct inode *ip;
958 int dv = filep->fi_devp->di_dcookie;
959
960 /* Try the inode cache first */
961 if ((filep->fi_inode = get_icache(dv, inode)) != NULL)
962 return;
963
964 filep->fi_inode = (struct inode *)bkmem_alloc(sizeof (struct inode));
965 ip = filep->fi_inode;
966 bzero((char *)ip, sizeof (struct inode));
967 ip->i_size = hsdep->hs_dir.ext_size;
968 ip->i_smode = hsdep->hs_dir.mode;
969 ip->i_number = inode;
970 ip->i_atime.tv_sec = hsdep->hs_dir.adate.tv_sec;
971 ip->i_atime.tv_usec = hsdep->hs_dir.adate.tv_usec;
972 ip->i_ctime.tv_sec = hsdep->hs_dir.cdate.tv_sec;
973 ip->i_ctime.tv_usec = hsdep->hs_dir.cdate.tv_usec;
974 ip->i_mtime.tv_sec = hsdep->hs_dir.mdate.tv_sec;
975 ip->i_mtime.tv_usec = hsdep->hs_dir.mdate.tv_usec;
976 set_icache(dv, inode, ip, sizeof (struct inode));
977 }
978
979 #ifdef noisy
980 static void
print_io_req(fileid_t * filep,char * str)981 print_io_req(fileid_t *filep, char *str)
982 {
983 printf("%s o=%d b=%d c=%d m=%x\n",
984 str,
985 filep->fi_offset,
986 filep->fi_blocknum,
987 filep->fi_count,
988 (uint_t)filep->fi_memp);
989 }
990 #endif /* noisy */
991
992 static int
boot_hsfs_getdents(int fd,struct dirent * dep,unsigned size)993 boot_hsfs_getdents(int fd, struct dirent *dep, unsigned size)
994 {
995 /*
996 * Read directory entries from the file open on "fd" into the
997 * "size"-byte buffer at "dep" until the buffer is exhausted
998 * or we reach EOF on the directory. Returns the number of
999 * entries read.
1000 */
1001 int n;
1002 int cnt = 0;
1003 struct dirinfo dir;
1004 struct hs_direct *hdp;
1005 unsigned long oldoff, oldblok;
1006
1007 #define SLOP (sizeof (struct dirent) - (int)&((struct dirent *)0)->d_name[1])
1008
1009 if (!(dir.fi = find_fp(fd)) ||
1010 ((dir.fi->fi_inode->i_smode & IFMT) != IFDIR)) {
1011 /*
1012 * Bogus file descriptor, bail out now!
1013 */
1014 return (-1);
1015 }
1016
1017 oldoff = dir.loc = dir.fi->fi_offset;
1018 oldblok = dir.fi->fi_blocknum;
1019
1020 for (hdp = readdir(&dir); hdp; hdp = readdir(&dir)) {
1021 /*
1022 * Compute name length and break loop if there's not
1023 * enough space in the output buffer for the next
1024 * entry.
1025 *
1026 * NOTE: "SLOP" is the number of bytes inserted into the dirent
1027 * struct's "d_name" field by the compiler to preserve
1028 * alignment.
1029 */
1030 n = strlen(hdp->hs_ufs_dir.d_name);
1031 n = roundup((sizeof (struct dirent) + ((n > SLOP) ? n : 0)),
1032 sizeof (off_t));
1033
1034 if (n > size) {
1035 dir.fi->fi_blocknum = oldblok;
1036 dir.fi->fi_offset = oldoff;
1037 break;
1038 }
1039
1040 oldblok = dir.fi->fi_blocknum;
1041 oldoff = dir.loc;
1042 size -= n;
1043 cnt += 1;
1044
1045 (void) strcpy(dep->d_name, hdp->hs_ufs_dir.d_name);
1046 dep->d_ino = hdp->hs_ufs_dir.d_ino;
1047 dep->d_off = dir.loc;
1048 dep->d_reclen = (unsigned short)n;
1049
1050 dep = (struct dirent *)((char *)dep + n);
1051 }
1052
1053 #undef SLOP
1054
1055 return (cnt);
1056 }
1057
1058 static void
hs_dodates(enum hs_vol_type type,struct hs_direntry * hdp,char * bufp)1059 hs_dodates(enum hs_vol_type type, struct hs_direntry *hdp, char *bufp)
1060 {
1061 if (type == HS_VOL_TYPE_HS) {
1062 hs_parse_dirdate(HDE_cdate(bufp), &hdp->cdate);
1063 hs_parse_dirdate(HDE_cdate(bufp), &hdp->adate);
1064 hs_parse_dirdate(HDE_cdate(bufp), &hdp->mdate);
1065 } else if (type == HS_VOL_TYPE_ISO) {
1066 hs_parse_dirdate(IDE_cdate(bufp), &hdp->cdate);
1067 hs_parse_dirdate(IDE_cdate(bufp), &hdp->adate);
1068 hs_parse_dirdate(IDE_cdate(bufp), &hdp->mdate);
1069 } else
1070 prom_panic("hs_dodates: bad volume type");
1071 }
1072
1073 /*
1074 * hs_parse_dirdate
1075 *
1076 * Parse the short 'directory-format' date into a Unix timeval.
1077 * This is the date format used in Directory Entries.
1078 *
1079 * If the date is not representable, make something up.
1080 */
1081 void
hs_parse_dirdate(uchar_t * dp,struct timeval * tvp)1082 hs_parse_dirdate(uchar_t *dp, struct timeval *tvp)
1083 {
1084 int year, month, day, hour, minute, sec, gmtoff;
1085
1086 year = HDE_DATE_YEAR(dp);
1087 month = HDE_DATE_MONTH(dp);
1088 day = HDE_DATE_DAY(dp);
1089 hour = HDE_DATE_HOUR(dp);
1090 minute = HDE_DATE_MIN(dp);
1091 sec = HDE_DATE_SEC(dp);
1092 gmtoff = HDE_DATE_GMTOFF(dp);
1093
1094 tvp->tv_usec = 0;
1095 if (year < THE_EPOCH) {
1096 tvp->tv_sec = 0;
1097 } else {
1098 tvp->tv_sec = hs_date_to_gmtime(year, month, day, gmtoff);
1099 if (tvp->tv_sec != -1) {
1100 tvp->tv_sec += ((hour * 60) + minute) * 60 + sec;
1101 }
1102 }
1103
1104 return;
1105
1106 }
1107
1108 /*
1109 * hs_parse_longdate
1110 *
1111 * Parse the long 'user-oriented' date into a Unix timeval.
1112 * This is the date format used in the Volume Descriptor.
1113 *
1114 * If the date is not representable, make something up.
1115 */
1116 void
hs_parse_longdate(uchar_t * dp,struct timeval * tvp)1117 hs_parse_longdate(uchar_t *dp, struct timeval *tvp)
1118 {
1119 int year, month, day, hour, minute, sec, gmtoff;
1120
1121 year = HSV_DATE_YEAR(dp);
1122 month = HSV_DATE_MONTH(dp);
1123 day = HSV_DATE_DAY(dp);
1124 hour = HSV_DATE_HOUR(dp);
1125 minute = HSV_DATE_MIN(dp);
1126 sec = HSV_DATE_SEC(dp);
1127 gmtoff = HSV_DATE_GMTOFF(dp);
1128
1129 tvp->tv_usec = 0;
1130 if (year < THE_EPOCH) {
1131 tvp->tv_sec = 0;
1132 } else {
1133 tvp->tv_sec = hs_date_to_gmtime(year, month, day, gmtoff);
1134 if (tvp->tv_sec != -1) {
1135 tvp->tv_sec += ((hour * 60) + minute) * 60 + sec;
1136 tvp->tv_usec = HSV_DATE_HSEC(dp) * 10000;
1137 }
1138 }
1139
1140 }
1141
1142 /* cumulative number of seconds per month, non-leap and leap-year versions */
1143 static time_t cum_sec[] = {
1144 0x0, 0x28de80, 0x4dc880, 0x76a700, 0x9e3400, 0xc71280,
1145 0xee9f80, 0x1177e00, 0x1405c80, 0x167e980, 0x190c800, 0x1b85500
1146 };
1147 static time_t cum_sec_leap[] = {
1148 0x0, 0x28de80, 0x4f1a00, 0x77f880, 0x9f8580, 0xc86400,
1149 0xeff100, 0x118cf80, 0x141ae00, 0x1693b00, 0x1921980, 0x1b9a680
1150 };
1151 #define SEC_PER_DAY 0x15180
1152 #define SEC_PER_YEAR 0x1e13380
1153
1154 /*
1155 * hs_date_to_gmtime
1156 *
1157 * Convert year(1970-2099)/month(1-12)/day(1-31) to seconds-since-1970/1/1.
1158 *
1159 * Returns -1 if the date is out of range.
1160 */
1161 static time_t
hs_date_to_gmtime(int year,int mon,int day,int gmtoff)1162 hs_date_to_gmtime(int year, int mon, int day, int gmtoff)
1163 {
1164 time_t sum;
1165 time_t *cp;
1166 int y;
1167
1168 if ((year < THE_EPOCH) || (year > END_OF_TIME) ||
1169 (mon < 1) || (mon > 12) ||
1170 (day < 1) || (day > 31))
1171 return (-1);
1172
1173 /*
1174 * Figure seconds until this year and correct for leap years.
1175 * Note: 2000 is a leap year but not 2100.
1176 */
1177 y = year - THE_EPOCH;
1178 sum = y * SEC_PER_YEAR;
1179 sum += ((y + 1) / 4) * SEC_PER_DAY;
1180 /*
1181 * Point to the correct table for this year and
1182 * add in seconds until this month.
1183 */
1184 cp = ((y + 2) % 4) ? cum_sec : cum_sec_leap;
1185 sum += cp[mon - 1];
1186 /*
1187 * Add in seconds until 0:00 of this day.
1188 * (days-per-month validation is not done here)
1189 */
1190 sum += (day - 1) * SEC_PER_DAY;
1191 sum -= (gmtoff * 15 * 60);
1192 return (sum);
1193 }
1194