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, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 /* 30 * Basic file system reading code for standalone I/O system. 31 * Simulates a primitive UNIX I/O system (read(), write(), open(), etc). 32 * Does not support writes. 33 */ 34 35 /* 36 * WARNING: 37 * This is currently used by installgrub for creating bootable floppy. 38 * The special part is diskread_callback/fileread_callback for gathering 39 * fileblock list. 40 */ 41 42 #include <sys/param.h> 43 #include <sys/sysmacros.h> 44 #include <sys/vnode.h> 45 #include <sys/fs/pc_label.h> 46 #include <sys/bootvfs.h> 47 #include <sys/filep.h> 48 #include "pcfilep.h" 49 50 #if defined(_BOOT) 51 #include "../common/util.h" 52 #elif defined(_KERNEL) 53 #include <sys/sunddi.h> 54 #else 55 #include <stdio.h> 56 #include <strings.h> 57 #include <ctype.h> 58 #endif 59 60 #if defined(_BOOT) 61 #define dprintf if (bootrd_debug) printf 62 #elif defined(_KERNEL) 63 #define printf kobj_printf 64 #define dprintf if (bootrd_debug) kobj_printf 65 66 /* PRINTLIKE */ 67 extern void kobj_printf(char *, ...); 68 #else 69 #define dprintf if (bootrd_debug) printf 70 #endif 71 72 #define FI_STARTCLUST(fp) (*(ushort_t *)(fp)->fi_buf) 73 #define FI_LENGTH(fp) (*(long *)((fp)->fi_buf + 4)) 74 75 extern int bootrd_debug; 76 extern void *bkmem_alloc(size_t); 77 extern void bkmem_free(void *, size_t); 78 79 /* 80 * NOTE: The fileread_callback is set by the calling program 81 * during a file read. diskread_callback is set to fileread_callback 82 * only if reading a file block. It needs to be NULL while reading 83 * cluster blocks. 84 */ 85 extern int (*diskread_callback)(int, int); 86 extern int (*fileread_callback)(int, int); 87 88 /* 89 * Local prototypes 90 */ 91 static int lookuppn(char *, _dir_entry_p); 92 static fileid_t *find_fp(int); 93 static void *readblock(int, int); 94 static int fat_map(int, int); 95 static int cluster_valid(long, int); 96 static int fat_ctodb(int, int); 97 98 static int bpcfs_mountroot(char *str); 99 static int bpcfs_unmountroot(void); 100 static int bpcfs_open(char *str, int flags); 101 static int bpcfs_close(int fd); 102 static void bpcfs_closeall(void); 103 static ssize_t bpcfs_read(int fdesc, char *buf, size_t count); 104 static off_t bpcfs_lseek(int fdesc, off_t addr, int whence); 105 106 static fileid_t *head; 107 static _fat_controller_p pcfsp; 108 109 /* cache the cluster */ 110 static int nsec_cache; 111 static int nsec_start; 112 static char *cluster_cache; 113 114 /*ARGSUSED*/ 115 static int 116 bpcfs_mountroot(char *str) 117 { 118 int ncluster; 119 if (pcfsp != NULL) 120 return (0); /* already mounted */ 121 122 pcfsp = bkmem_alloc(sizeof (_fat_controller_t)); 123 head = (fileid_t *)bkmem_alloc(sizeof (fileid_t)); 124 head->fi_back = head->fi_forw = head; 125 head->fi_filedes = 0; 126 head->fi_taken = 0; 127 128 /* read of first floppy sector */ 129 head->fi_blocknum = 0; 130 head->fi_count = SECSIZ; 131 head->fi_memp = (caddr_t)pcfsp->f_sector; 132 if (diskread(head)) { 133 printf("failed to read first sector\n"); 134 bkmem_free(pcfsp, sizeof (*pcfsp)); 135 pcfsp = NULL; 136 return (-1); 137 } 138 139 if (pcfsp->f_bpb.bs_spc == 0) { 140 printf("invalid bios paramet block\n"); 141 return (-1); 142 } 143 144 pcfsp->f_rootsec = 145 (pcfsp->f_bpb.bs_num_fats * ltohs(pcfsp->f_bpb.bs_spf)) + 146 ltohs(pcfsp->f_bpb.bs_resv_sectors); 147 pcfsp->f_rootlen = 148 ltohs(pcfsp->f_bpb.bs_num_root_entries) * 149 sizeof (_dir_entry_t) / SECSIZ; 150 pcfsp->f_adjust = 0; 151 pcfsp->f_dclust = CLUSTER_ROOTDIR; 152 pcfsp->f_filesec = pcfsp->f_rootsec + pcfsp->f_rootlen; 153 pcfsp->f_nxtfree = CLUSTER_FIRST; 154 155 /* figure out the number of clusters in this partition */ 156 ncluster = (((ulong_t)ltohs(pcfsp->f_bpb.bs_siv) ? 157 (ulong_t)ltohs(pcfsp->f_bpb.bs_siv) : 158 (ulong_t)ltohi(pcfsp->f_bpb.bs_siv)) - 159 pcfsp->f_filesec) / (ulong_t)pcfsp->f_bpb.bs_spc; 160 pcfsp->f_16bit = ncluster >= CLUSTER_MAX_12; 161 pcfsp->f_ncluster = ncluster; 162 163 /* cache the cluster */ 164 if (pcfsp->f_16bit) 165 nsec_cache = (((ncluster << 1) + 511) >> 9); 166 else 167 nsec_cache = (ncluster + ((ncluster + 1) >> 1) + 511) >> 9; 168 cluster_cache = bkmem_alloc(nsec_cache * SECSIZ); 169 if (cluster_cache == NULL) { 170 printf("bpcfs_mountroot: out of memory\n"); 171 bkmem_free(pcfsp, sizeof (*pcfsp)); 172 pcfsp = NULL; 173 return (-1); 174 } 175 176 head->fi_blocknum = nsec_start = 177 ltohs(pcfsp->f_bpb.bs_resv_sectors) + pcfsp->f_adjust; 178 head->fi_count = nsec_cache * SECSIZ; 179 head->fi_memp = cluster_cache; 180 if (diskread(head)) { 181 printf("bpcfs_mountroot: failed to read cluster\n"); 182 bkmem_free(pcfsp, sizeof (*pcfsp)); 183 pcfsp = NULL; 184 return (-1); 185 } 186 dprintf("read cluster sectors %d starting at %d\n", 187 nsec_cache, nsec_start); 188 return (0); 189 } 190 191 static int 192 bpcfs_unmountroot(void) 193 { 194 if (pcfsp == NULL) 195 return (-1); 196 197 (void) bpcfs_closeall(); 198 199 return (0); 200 } 201 202 /* 203 * Open a file. 204 */ 205 /*ARGSUSED*/ 206 int 207 bpcfs_open(char *str, int flags) 208 { 209 static int filedes = 1; 210 211 fileid_t *filep; 212 _dir_entry_t d; 213 214 dprintf("open %s\n", str); 215 filep = (fileid_t *)bkmem_alloc(sizeof (fileid_t)); 216 filep->fi_back = head->fi_back; 217 filep->fi_forw = head; 218 head->fi_back->fi_forw = filep; 219 head->fi_back = filep; 220 filep->fi_filedes = filedes++; 221 filep->fi_taken = 1; 222 filep->fi_path = (char *)bkmem_alloc(strlen(str) + 1); 223 (void) strcpy(filep->fi_path, str); 224 225 if (lookuppn(str, &d)) { 226 (void) bpcfs_close(filep->fi_filedes); 227 return (-1); 228 } 229 230 filep->fi_offset = 0; 231 FI_STARTCLUST(filep) = d.d_cluster; 232 FI_LENGTH(filep) = d.d_size; 233 dprintf("file %s size = %ld\n", str, d.d_size); 234 return (filep->fi_filedes); 235 } 236 237 int 238 bpcfs_close(int fd) 239 { 240 fileid_t *filep; 241 242 dprintf("close %d\n", fd); 243 if (!(filep = find_fp(fd))) 244 return (-1); 245 246 if (filep->fi_taken == 0 || filep == head) { 247 printf("File descripter %d no allocated!\n", fd); 248 return (-1); 249 } 250 251 /* unlink and deallocate node */ 252 filep->fi_forw->fi_back = filep->fi_back; 253 filep->fi_back->fi_forw = filep->fi_forw; 254 bkmem_free(filep->fi_path, strlen(filep->fi_path) + 1); 255 bkmem_free((char *)filep, sizeof (fileid_t)); 256 dprintf("close done\n"); 257 return (0); 258 } 259 260 static void 261 bpcfs_closeall(void) 262 { 263 fileid_t *filep; 264 265 while ((filep = head->fi_forw) != head) 266 if (filep->fi_taken && bpcfs_close(filep->fi_filedes)) 267 printf("Filesystem may be inconsistent.\n"); 268 269 bkmem_free(pcfsp, sizeof (*pcfsp)); 270 bkmem_free(head, sizeof (fileid_t)); 271 pcfsp = NULL; 272 head = NULL; 273 } 274 275 static ssize_t 276 bpcfs_read(int fd, caddr_t b, size_t c) 277 { 278 ulong_t sector; 279 uint_t count = 0, xfer, i; 280 char *block; 281 ulong_t off, blk; 282 int rd, spc; 283 fileid_t *fp; 284 285 dprintf("bpcfs_read: fd = %d, buf = %p, size = %d\n", 286 fd, (void *)b, c); 287 fp = find_fp(fd); 288 if (fp == NULL) { 289 printf("invalid file descriptor %d\n", fd); 290 return (-1); 291 } 292 293 spc = pcfsp->f_bpb.bs_spc; 294 off = fp->fi_offset; 295 blk = FI_STARTCLUST(fp); 296 rd = blk == CLUSTER_ROOTDIR ? 1 : 0; 297 298 spc = pcfsp->f_bpb.bs_spc; 299 off = fp->fi_offset; 300 blk = FI_STARTCLUST(fp); 301 rd = (blk == CLUSTER_ROOTDIR) ? 1 : 0; 302 303 if ((c = MIN(FI_LENGTH(fp) - off, c)) == 0) 304 return (0); 305 306 while (off >= pcfsp->f_bpb.bs_spc * SECSIZ) { 307 blk = fat_map(blk, rd); 308 off -= pcfsp->f_bpb.bs_spc * SECSIZ; 309 310 if (!cluster_valid(blk, rd)) { 311 printf("bpcfs_read: invalid cluster: %ld, %d\n", 312 blk, rd); 313 return (-1); 314 } 315 } 316 317 while (count < c) { 318 sector = fat_ctodb(blk, rd); 319 diskread_callback = fileread_callback; 320 for (i = ((off / SECSIZ) % pcfsp->f_bpb.bs_spc); i < spc; i++) { 321 xfer = MIN(SECSIZ - (off % SECSIZ), c - count); 322 if (xfer == 0) 323 break; /* last sector done */ 324 325 block = (char *)readblock(sector + i, 1); 326 if (block == NULL) { 327 return (-1); 328 } 329 dprintf("bpcfs_read: read %d bytes\n", xfer); 330 if (diskread_callback == NULL) 331 (void) bcopy(&block[off % SECSIZ], b, xfer); 332 count += xfer; 333 off += xfer; 334 b += xfer; 335 } 336 337 diskread_callback = NULL; 338 if (count < c) { 339 blk = fat_map(blk, rd); 340 if (!cluster_valid(blk, rd)) { 341 printf("bpcfs_read: invalid cluster: %ld, %d\n", 342 blk, rd); 343 break; 344 } 345 } 346 } 347 348 fp->fi_offset += count; 349 return (count); 350 } 351 352 /* 353 * This version of seek() only performs absolute seeks (whence == 0). 354 */ 355 static off_t 356 bpcfs_lseek(int fd, off_t addr, int whence) 357 { 358 fileid_t *filep; 359 360 dprintf("lseek %d, off = %lx\n", fd, addr); 361 if (!(filep = find_fp(fd))) 362 return (-1); 363 364 switch (whence) { 365 case SEEK_CUR: 366 filep->fi_offset += addr; 367 break; 368 case SEEK_SET: 369 filep->fi_offset = addr; 370 break; 371 default: 372 case SEEK_END: 373 printf("lseek(): invalid whence value %d\n", whence); 374 break; 375 } 376 377 filep->fi_blocknum = addr / DEV_BSIZE; 378 filep->fi_count = 0; 379 return (0); 380 } 381 382 static fileid_t * 383 find_fp(int fd) 384 { 385 fileid_t *filep = head; 386 387 if (fd >= 0) { 388 while ((filep = filep->fi_forw) != head) 389 if (fd == filep->fi_filedes) 390 return (filep->fi_taken ? filep : 0); 391 } 392 393 return (0); 394 } 395 396 static int 397 cluster_valid(long c, int rd) 398 { 399 return ((rd && (c == 0)) ? 1 : (c >= CLUSTER_RES_16_0 ? 0 : c)); 400 } 401 402 static int 403 fat_ctodb(int blk, int r) 404 { 405 uint_t s; 406 407 s = r ? blk + pcfsp->f_rootsec + pcfsp->f_adjust : 408 ((blk - 2) * pcfsp->f_bpb.bs_spc) + 409 pcfsp->f_filesec + pcfsp->f_adjust; 410 411 return (s); 412 } 413 414 static int 415 fat_map(int blk, int rootdir) 416 { 417 ulong_t sectn, fat_index; 418 uchar_t *fp; 419 420 if (rootdir) { 421 return (blk > pcfsp->f_rootlen ? CLUSTER_EOF : blk + 1); 422 } 423 424 /* ---- Find out what sector this cluster is in ---- */ 425 fat_index = (pcfsp->f_16bit) ? ((ulong_t)blk << 1) : 426 ((ulong_t)blk + ((uint_t)blk >> 1)); 427 428 sectn = (fat_index / SECSIZ) + ltohs(pcfsp->f_bpb.bs_resv_sectors) 429 + pcfsp->f_adjust; 430 431 /* 432 * Read two sectors so that if our fat_index points at the last byte 433 * byte we'll have the data needed. This is only a problem for fat12 434 * entries. 435 */ 436 if (!(fp = (uchar_t *)readblock(sectn, 2))) { 437 printf("fat_map: bad cluster\n"); 438 return (CLUSTER_BAD_16); 439 } 440 441 fp += (fat_index % SECSIZ); 442 443 if (pcfsp->f_16bit) 444 blk = fp[0] | (fp[1] << 8); 445 else { 446 if (blk & 1) 447 blk = ((fp[0] >> 4) & 0xf) | (fp[1] << 4); 448 else 449 blk = ((fp[1] & 0xf) << 8) | fp[0]; 450 451 /* 452 * This makes compares easier because we can just compare 453 * against one value instead of two. 454 */ 455 if (blk >= CLUSTER_RES_12_0) 456 blk |= CLUSTER_RES_16_0; 457 } 458 return (blk); 459 } 460 461 static int 462 namecmp(char *pn, char *dn, int cs) 463 { 464 dprintf("namecmp %s, %s, len = %d\n", pn, dn, cs); 465 466 /* starting char must match */ 467 while (*pn && *dn) { 468 --cs; 469 if (toupper(*pn++) != toupper(*dn++)) 470 return (1); 471 } 472 473 dprintf("namecmp: cs = %d\n", cs); 474 /* remainder should be either ~# or all spaces */ 475 if (cs > 0 && *dn == '~') 476 return (0); 477 while (cs > 0) { 478 if (*dn++ != ' ') 479 return (1); 480 --cs; 481 } 482 return (0); 483 } 484 485 static int 486 dircmp(char *name, char *d_name, char *d_ext) 487 { 488 int ret; 489 char *sep, *ext; 490 491 sep = (char *)strchr(name, '.'); 492 493 if (sep) { 494 *sep = '\0'; 495 ext = sep + 1; 496 } else 497 ext = " "; 498 499 if (namecmp(name, d_name, NAMESIZ) || namecmp(ext, d_ext, EXTSIZ)) 500 ret = 1; 501 else 502 ret = 0; 503 if (sep) 504 *sep = '.'; 505 return (ret); 506 } 507 508 static int 509 lookup(char *n, _dir_entry_p dp, ulong_t dir_blk) 510 { 511 int spc = pcfsp->f_bpb.bs_spc; 512 int rd = (dir_blk == CLUSTER_ROOTDIR ? 1 : 0); 513 _dir_entry_p dxp; 514 int j, sector; 515 516 dprintf("lookup: name = %s\n", n); 517 518 while (cluster_valid(dir_blk, rd)) { 519 sector = fat_ctodb(dir_blk, rd); 520 dxp = readblock(sector, 1); /* read one sector */ 521 if (dxp == NULL) 522 return (0); 523 for (j = 0; j < DIRENTS * spc; j++, dxp++) { 524 dprintf("lookup: dir entry %s.%s;\n", 525 dxp->d_name, dxp->d_ext); 526 if (dxp->d_name[0] == 0) 527 return (0); 528 if ((uchar_t)dxp->d_name[0] != 0xE5 && 529 (dxp->d_attr & (DE_LABEL|DE_HIDDEN)) == 0 && 530 dircmp(n, dxp->d_name, dxp->d_ext) == 0) { 531 dprintf("lookup: match found\n"); 532 (void) bcopy(dxp, dp, sizeof (*dp)); 533 return (1); 534 } 535 } 536 /* next cluster */ 537 dir_blk = fat_map(dir_blk, rd); 538 } 539 540 return (0); 541 } 542 543 static int 544 lookuppn(char *n, _dir_entry_p dp) 545 { 546 long dir_blk; 547 char name[8 + 1 + 3 + 1]; /* <8>.<3>'\0' */ 548 char *p, *ep; 549 _dir_entry_t dd; 550 551 dprintf("lookuppn: path = %s\n", n); 552 dir_blk = pcfsp->f_dclust; 553 if ((*n == '\\') || (*n == '/')) { 554 dir_blk = CLUSTER_ROOTDIR; 555 while ((*n == '\\') || (*n == '/')) 556 n++; 557 if (*n == '\0') { 558 (void) bzero(dp, sizeof (*dp)); 559 dp->d_cluster = CLUSTER_ROOTDIR; 560 dp->d_attr = DE_DIRECTORY; 561 return (0); 562 } 563 } 564 565 ep = &name[0] + sizeof (name); 566 while (*n) { 567 (void) bzero(name, sizeof (name)); 568 p = &name[0]; 569 while (*n && (*n != '\\') && (*n != '/')) 570 if (p != ep) 571 *p++ = *n++; 572 else { 573 dprintf("return, name %s is too long\n", name); 574 return (-1); /* name is too long */ 575 } 576 while ((*n == '\\') || (*n == '/')) 577 n++; 578 if (lookup(name, &dd, dir_blk) == 0) { 579 dprintf("return, name %s not found\n", name); 580 return (-1); 581 } 582 dprintf("dd = %x:%x:%x attr = %x\n", 583 *(int *)&dd, *(((int *)&dd) + 1), 584 *(((int *)&dd) + 2), dd.d_attr); 585 if (*n && ((dd.d_attr & DE_DIRECTORY) == 0)) { 586 dprintf("return, not a directory\n"); 587 return (-1); 588 } 589 590 dir_blk = dd.d_cluster; 591 } 592 (void) bcopy(&dd, dp, sizeof (dd)); 593 return (0); 594 } 595 596 static void * 597 readblock(int sector, int nsec) 598 { 599 if (sector >= nsec_start && sector + nsec <= nsec_start + nsec_cache) 600 return (cluster_cache + (sector - nsec_start) * SECSIZ); 601 602 /* read disk sectors */ 603 head->fi_blocknum = sector; 604 head->fi_count = nsec * SECSIZ; 605 head->fi_memp = head->fi_buf; 606 if (diskread(head)) { 607 printf("failed to %d sectors at %d\n", nsec, sector); 608 return (NULL); 609 } 610 611 return (head->fi_buf); 612 } 613 614 struct boot_fs_ops bpcfs_ops = { 615 "boot_pcfs", 616 bpcfs_mountroot, 617 bpcfs_unmountroot, 618 bpcfs_open, 619 bpcfs_close, 620 bpcfs_read, 621 bpcfs_lseek, 622 NULL 623 }; 624