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