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 (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T 23 * All Rights Reserved 24 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 25 * Use is subject to license terms. 26 * Copyright 2024 MNX Cloud, Inc. 27 */ 28 29 /* 30 * Portions of this source code were derived from Berkeley 4.3 BSD 31 * under license from the Regents of the University of California. 32 */ 33 34 /* 35 * libfstyp module for pcfs 36 */ 37 #include <sys/param.h> 38 #include <sys/types.h> 39 #include <sys/mntent.h> 40 #include <errno.h> 41 #include <sys/fs/pc_fs.h> 42 #include <sys/fs/pc_label.h> 43 #include <sys/fs/pc_dir.h> 44 #include <sys/stat.h> 45 #include <sys/vfs.h> 46 #include <sys/dkio.h> 47 #include <stdio.h> 48 #include <unistd.h> 49 #include <string.h> 50 #include <strings.h> 51 #include <sys/mnttab.h> 52 #include <locale.h> 53 #include <libfstyp_module.h> 54 55 #define PC_LABEL_SIZE 11 56 57 /* for the <sys/fs/pc_dir.h> PCDL_IS_LFN macro */ 58 int enable_long_filenames = 1; 59 60 struct fstyp_fat16_bs { 61 uint8_t f_drvnum; 62 uint8_t f_reserved1; 63 uint8_t f_bootsig; 64 uint8_t f_volid[4]; 65 uint8_t f_label[11]; 66 uint8_t f_typestring[8]; 67 }; 68 69 struct fstyp_fat32_bs { 70 uint32_t f_fatlength; 71 uint16_t f_flags; 72 uint8_t f_major; 73 uint8_t f_minor; 74 uint32_t f_rootcluster; 75 uint16_t f_infosector; 76 uint16_t f_backupboot; 77 uint8_t f_reserved2[12]; 78 uint8_t f_drvnum; 79 uint8_t f_reserved1; 80 uint8_t f_bootsig; 81 uint8_t f_volid[4]; 82 uint8_t f_label[11]; 83 uint8_t f_typestring[8]; 84 }; 85 86 typedef struct fstyp_pcfs { 87 int fd; 88 off_t offset; 89 nvlist_t *attr; 90 struct bootsec bs; 91 struct fstyp_fat16_bs bs16; 92 struct fstyp_fat32_bs bs32; 93 ushort_t bps; 94 int fattype; 95 char volume_label[PC_LABEL_SIZE + 1]; 96 97 /* parameters derived or calculated per FAT spec */ 98 ulong_t FATSz; 99 ulong_t TotSec; 100 ulong_t RootDirSectors; 101 ulong_t FirstDataSector; 102 ulong_t DataSec; 103 ulong_t CountOfClusters; 104 } fstyp_pcfs_t; 105 106 107 /* We should eventually make the structs "packed" so these won't be needed */ 108 #define PC_BPSEC(h) ltohs((h)->bs.bps[0]) 109 #define PC_RESSEC(h) ltohs((h)->bs.res_sec[0]) 110 #define PC_NROOTENT(h) ltohs((h)->bs.rdirents[0]) 111 #define PC_NSEC(h) ltohs((h)->bs.numsect[0]) 112 #define PC_DRVNUM(h) (FSTYP_IS_32(h) ? (h)->bs32.f_drvnum : \ 113 (h)->bs16.f_drvnum) 114 #define PC_VOLID(a) (FSTYP_IS_32(h) ? ltohi((h)->bs32.f_volid[0]) : \ 115 ltohi((h)->bs16.f_volid[0])) 116 #define PC_LABEL_ADDR(a) (FSTYP_IS_32(h) ? \ 117 &((h)->bs32.f_label[0]) : &((h)->bs16.f_label[0])) 118 119 #define FSTYP_IS_32(h) ((h)->fattype == 32) 120 121 #define FSTYP_MAX_CLUSTER_SIZE (64 * 1024) /* though officially 32K */ 122 #define FSTYP_MAX_DIR_SIZE (65536 * 32) 123 124 static int read_bootsec(fstyp_pcfs_t *h); 125 static int valid_media(fstyp_pcfs_t *h); 126 static int well_formed(fstyp_pcfs_t *h); 127 static void calculate_parameters(fstyp_pcfs_t *h); 128 static void determine_fattype(fstyp_pcfs_t *h); 129 static void get_label(fstyp_pcfs_t *h); 130 static void get_label_16(fstyp_pcfs_t *h); 131 static void get_label_32(fstyp_pcfs_t *h); 132 static int next_cluster_32(fstyp_pcfs_t *h, int n); 133 static boolean_t dir_find_label(fstyp_pcfs_t *h, struct pcdir *d, int nent); 134 static int is_pcfs(fstyp_pcfs_t *h); 135 static int dumpfs(fstyp_pcfs_t *h, FILE *fout, FILE *ferr); 136 static int get_attr(fstyp_pcfs_t *h); 137 138 int fstyp_mod_init(int fd, off_t offset, fstyp_mod_handle_t *handle); 139 void fstyp_mod_fini(fstyp_mod_handle_t handle); 140 int fstyp_mod_ident(fstyp_mod_handle_t handle); 141 int fstyp_mod_get_attr(fstyp_mod_handle_t handle, nvlist_t **attrp); 142 int fstyp_mod_dump(fstyp_mod_handle_t handle, FILE *fout, FILE *ferr); 143 144 int 145 fstyp_mod_init(int fd, off_t offset, fstyp_mod_handle_t *handle) 146 { 147 struct fstyp_pcfs *h; 148 149 if ((h = calloc(1, sizeof (struct fstyp_pcfs))) == NULL) { 150 return (FSTYP_ERR_NOMEM); 151 } 152 h->fd = fd; 153 h->offset = offset; 154 155 *handle = (fstyp_mod_handle_t)h; 156 return (0); 157 } 158 159 void 160 fstyp_mod_fini(fstyp_mod_handle_t handle) 161 { 162 struct fstyp_pcfs *h = (struct fstyp_pcfs *)handle; 163 164 if (h->attr == NULL) { 165 nvlist_free(h->attr); 166 h->attr = NULL; 167 } 168 free(h); 169 } 170 171 int 172 fstyp_mod_ident(fstyp_mod_handle_t handle) 173 { 174 struct fstyp_pcfs *h = (struct fstyp_pcfs *)handle; 175 176 return (is_pcfs(h)); 177 } 178 179 int 180 fstyp_mod_get_attr(fstyp_mod_handle_t handle, nvlist_t **attrp) 181 { 182 struct fstyp_pcfs *h = (struct fstyp_pcfs *)handle; 183 int error; 184 185 if (h->attr == NULL) { 186 if (nvlist_alloc(&h->attr, NV_UNIQUE_NAME_TYPE, 0)) { 187 return (FSTYP_ERR_NOMEM); 188 } 189 if ((error = get_attr(h)) != 0) { 190 nvlist_free(h->attr); 191 h->attr = NULL; 192 return (error); 193 } 194 } 195 196 *attrp = h->attr; 197 return (0); 198 } 199 200 int 201 fstyp_mod_dump(fstyp_mod_handle_t handle, FILE *fout, FILE *ferr) 202 { 203 struct fstyp_pcfs *h = (struct fstyp_pcfs *)handle; 204 205 return (dumpfs(h, fout, ferr)); 206 } 207 208 209 /* 210 * Read in boot sector. Convert into host endianness where possible. 211 */ 212 static int 213 read_bootsec(fstyp_pcfs_t *h) 214 { 215 struct dk_minfo dkminfo; 216 char *buf; 217 size_t size = PC_SECSIZE; 218 219 if (ioctl(h->fd, DKIOCGMEDIAINFO, &dkminfo) != -1) { 220 if (dkminfo.dki_lbsize != 0) 221 size = dkminfo.dki_lbsize; 222 } 223 224 buf = malloc(size); 225 if (buf == NULL) 226 return (FSTYP_ERR_NOMEM); 227 228 (void) lseek(h->fd, h->offset, SEEK_SET); 229 if (read(h->fd, buf, size) != (ssize_t)size) { 230 free(buf); 231 return (FSTYP_ERR_IO); 232 } 233 234 bcopy(buf, &h->bs, sizeof (h->bs)); 235 bcopy(buf + sizeof (struct bootsec), &h->bs16, sizeof (h->bs16)); 236 bcopy(buf + sizeof (struct bootsec), &h->bs32, sizeof (h->bs32)); 237 free(buf); 238 239 h->bs.fatsec = ltohs(h->bs.fatsec); 240 h->bs.spt = ltohs(h->bs.spt); 241 h->bs.nhead = ltohs(h->bs.nhead); 242 h->bs.hiddensec = ltohi(h->bs.hiddensec); 243 h->bs.totalsec = ltohi(h->bs.totalsec); 244 245 h->bs32.f_fatlength = ltohi(h->bs32.f_fatlength); 246 h->bs32.f_flags = ltohs(h->bs32.f_flags); 247 h->bs32.f_rootcluster = ltohi(h->bs32.f_rootcluster); 248 h->bs32.f_infosector = ltohs(h->bs32.f_infosector); 249 h->bs32.f_backupboot = ltohs(h->bs32.f_backupboot); 250 251 h->bps = PC_BPSEC(h); 252 253 return (0); 254 } 255 256 static int 257 valid_media(fstyp_pcfs_t *h) 258 { 259 switch (h->bs.mediadesriptor) { 260 case MD_FIXED: 261 case SS8SPT: 262 case DS8SPT: 263 case SS9SPT: 264 case DS9SPT: 265 case DS18SPT: 266 case DS9_15SPT: 267 return (1); 268 default: 269 return (0); 270 } 271 } 272 273 static int 274 well_formed(fstyp_pcfs_t *h) 275 { 276 int fatmatch; 277 278 if (h->bs16.f_bootsig == 0x29) { 279 fatmatch = ((h->bs16.f_typestring[0] == 'F' && 280 h->bs16.f_typestring[1] == 'A' && 281 h->bs16.f_typestring[2] == 'T') && 282 (h->bs.fatsec > 0) && 283 ((PC_NSEC(h) == 0 && h->bs.totalsec > 0) || 284 PC_NSEC(h) > 0)); 285 } else if (h->bs32.f_bootsig == 0x29) { 286 fatmatch = ((h->bs32.f_typestring[0] == 'F' && 287 h->bs32.f_typestring[1] == 'A' && 288 h->bs32.f_typestring[2] == 'T') && 289 (h->bs.fatsec == 0 && h->bs32.f_fatlength > 0) && 290 ((PC_NSEC(h) == 0 && h->bs.totalsec > 0) || 291 PC_NSEC(h) > 0)); 292 } else { 293 fatmatch = (PC_NSEC(h) > 0 && h->bs.fatsec > 0); 294 } 295 296 return (fatmatch && h->bps > 0 && h->bps % 512 == 0 && 297 h->bs.spcl > 0 && PC_RESSEC(h) >= 1 && h->bs.nfat > 0); 298 } 299 300 static void 301 calculate_parameters(fstyp_pcfs_t *h) 302 { 303 if (PC_NSEC(h) != 0) { 304 h->TotSec = PC_NSEC(h); 305 } else { 306 h->TotSec = h->bs.totalsec; 307 } 308 if (h->bs.fatsec != 0) { 309 h->FATSz = h->bs.fatsec; 310 } else { 311 h->FATSz = h->bs32.f_fatlength; 312 } 313 if ((h->bps == 0) || (h->bs.spcl == 0)) { 314 return; 315 } 316 h->RootDirSectors = 317 ((PC_NROOTENT(h) * 32) + (h->bps - 1)) / h->bps; 318 h->FirstDataSector = 319 PC_RESSEC(h) + h->bs.nfat * h->FATSz + h->RootDirSectors; 320 h->DataSec = h->TotSec - h->FirstDataSector; 321 h->CountOfClusters = h->DataSec / h->bs.spcl; 322 } 323 324 static void 325 determine_fattype(fstyp_pcfs_t *h) 326 { 327 if (h->CountOfClusters == 0) { 328 h->fattype = 0; 329 return; 330 } 331 332 if (h->CountOfClusters < 4085) { 333 h->fattype = 12; 334 } else if (h->CountOfClusters < 65525) { 335 h->fattype = 16; 336 } else { 337 h->fattype = 32; 338 } 339 } 340 341 static void 342 get_label(fstyp_pcfs_t *h) 343 { 344 /* 345 * Use label from the boot sector by default. 346 * Can overwrite later with the one from root directory. 347 */ 348 (void) memcpy(h->volume_label, PC_LABEL_ADDR(h), PC_LABEL_SIZE); 349 h->volume_label[PC_LABEL_SIZE] = '\0'; 350 351 if (h->fattype == 0) { 352 return; 353 } else if (FSTYP_IS_32(h)) { 354 get_label_32(h); 355 } else { 356 get_label_16(h); 357 } 358 } 359 360 /* 361 * Get volume label from the root directory entry. 362 * In FAT12/16 the root directory is of fixed size. 363 * It immediately follows the FATs 364 */ 365 static void 366 get_label_16(fstyp_pcfs_t *h) 367 { 368 ulong_t FirstRootDirSecNum; 369 int secsize; 370 off_t offset; 371 uint8_t buf[PC_SECSIZE * 4]; 372 int i; 373 int nent, resid; 374 375 if ((secsize = h->bps) > sizeof (buf)) { 376 return; 377 } 378 379 FirstRootDirSecNum = PC_RESSEC(h) + h->bs.nfat * h->bs.fatsec; 380 offset = h->offset + FirstRootDirSecNum * secsize; 381 resid = PC_NROOTENT(h); 382 383 for (i = 0; i < h->RootDirSectors; i++) { 384 (void) lseek(h->fd, offset, SEEK_SET); 385 if (read(h->fd, buf, secsize) != secsize) { 386 return; 387 } 388 389 nent = secsize / sizeof (struct pcdir); 390 if (nent > resid) { 391 nent = resid; 392 } 393 if (dir_find_label(h, (struct pcdir *)buf, nent)) { 394 return; 395 } 396 397 resid -= nent; 398 offset += PC_SECSIZE; 399 } 400 } 401 402 /* 403 * Get volume label from the root directory entry. 404 * In FAT32 root is a usual directory, a cluster chain. 405 * It starts at BPB_RootClus. 406 */ 407 static void 408 get_label_32(fstyp_pcfs_t *h) 409 { 410 off_t offset; 411 int clustersize; 412 int n; 413 ulong_t FirstSectorofCluster; 414 uint8_t *buf; 415 int nent; 416 int cnt = 0; 417 418 clustersize = h->bs.spcl * h->bps; 419 if ((clustersize == 0) || (clustersize > FSTYP_MAX_CLUSTER_SIZE) || 420 ((buf = calloc(1, clustersize)) == NULL)) { 421 return; 422 } 423 424 for (n = h->bs32.f_rootcluster; n != 0; n = next_cluster_32(h, n)) { 425 FirstSectorofCluster = 426 (n - 2) * h->bs.spcl + h->FirstDataSector; 427 offset = h->offset + FirstSectorofCluster * h->bps; 428 (void) lseek(h->fd, offset, SEEK_SET); 429 if (read(h->fd, buf, clustersize) != clustersize) { 430 break; 431 } 432 433 nent = clustersize / sizeof (struct pcdir); 434 if (dir_find_label(h, (struct pcdir *)buf, nent)) { 435 break; 436 } 437 438 if (++cnt > FSTYP_MAX_DIR_SIZE / clustersize) { 439 break; 440 } 441 } 442 443 free(buf); 444 } 445 446 /* 447 * Get a FAT entry pointing to the next file cluster 448 */ 449 int 450 next_cluster_32(fstyp_pcfs_t *h, int n) 451 { 452 uint8_t buf[PC_SECSIZE]; 453 ulong_t ThisFATSecNum; 454 ulong_t ThisFATEntOffset; 455 off_t offset; 456 uint32_t val; 457 int next = 0; 458 459 ThisFATSecNum = PC_RESSEC(h) + (n * 4) / h->bps; 460 ThisFATEntOffset = (n * 4) % h->bps; 461 offset = h->offset + ThisFATSecNum * h->bps; 462 463 (void) lseek(h->fd, offset, SEEK_SET); 464 if (read(h->fd, buf, sizeof (buf)) == sizeof (buf)) { 465 val = buf[ThisFATEntOffset] & 0x0fffffff; 466 next = ltohi(val); 467 } 468 469 return (next); 470 } 471 472 /* 473 * Given an array of pcdir structs, find one containing volume label. 474 */ 475 static boolean_t 476 dir_find_label(fstyp_pcfs_t *h, struct pcdir *d, int nent) 477 { 478 int i; 479 480 for (i = 0; i < nent; i++, d++) { 481 if (PCDL_IS_LFN(d)) 482 continue; 483 if ((d->pcd_filename[0] != PCD_UNUSED) && 484 (d->pcd_filename[0] != PCD_ERASED) && 485 ((d->pcd_attr & (PCA_LABEL | PCA_DIR)) == PCA_LABEL) && 486 (d->un.pcd_scluster_hi == 0) && 487 (d->pcd_scluster_lo == 0)) { 488 (void) memcpy(h->volume_label, d->pcd_filename, 489 PC_LABEL_SIZE); 490 h->volume_label[PC_LABEL_SIZE] = '\0'; 491 return (B_TRUE); 492 } 493 } 494 return (B_FALSE); 495 } 496 497 static int 498 is_pcfs(fstyp_pcfs_t *h) 499 { 500 int error; 501 502 if ((error = read_bootsec(h)) != 0) { 503 return (error); 504 } 505 if (!valid_media(h)) { 506 return (FSTYP_ERR_NO_MATCH); 507 } 508 if (!well_formed(h)) { 509 return (FSTYP_ERR_NO_MATCH); 510 } 511 512 calculate_parameters(h); 513 determine_fattype(h); 514 get_label(h); 515 516 return (0); 517 } 518 519 static int 520 dumpfs(fstyp_pcfs_t *h, FILE *fout, FILE *ferr __unused) 521 { 522 /* 523 * If fat type was not detected, then the other data is 524 * likely bogus. 525 */ 526 if (h->fattype == 0) 527 return (FSTYP_ERR_NO_MATCH); 528 529 (void) fprintf(fout, "Filesystem type: FAT%d\n", h->fattype); 530 (void) fprintf(fout, 531 "Bytes Per Sector %d\t\tSectors Per Cluster %d\n", 532 h->bps, h->bs.spcl); 533 (void) fprintf(fout, 534 "Reserved Sectors %d\t\tNumber of FATs %d\n", 535 (unsigned short)PC_RESSEC(h), h->bs.nfat); 536 (void) fprintf(fout, 537 "Root Dir Entries %d\t\tNumber of Sectors %d\n", 538 (unsigned short)PC_NROOTENT(h), h->TotSec); 539 (void) fprintf(fout, 540 "Sectors Per FAT %d\t\tSectors Per Track %d\n", 541 h->FATSz, h->bs.spt); 542 (void) fprintf(fout, 543 "Number of Heads %d\t\tNumber Hidden Sectors %d\n", 544 h->bs.nhead, h->bs.hiddensec); 545 (void) fprintf(fout, "Volume ID: 0x%x\n", PC_VOLID(h)); 546 (void) fprintf(fout, "Volume Label: %s\n", h->volume_label); 547 (void) fprintf(fout, "Drive Number: 0x%x\n", PC_DRVNUM(h)); 548 (void) fprintf(fout, "Media Type: 0x%x ", h->bs.mediadesriptor); 549 550 switch (h->bs.mediadesriptor) { 551 case MD_FIXED: 552 (void) fprintf(fout, "\"Fixed\" Disk\n"); 553 break; 554 case SS8SPT: 555 (void) fprintf(fout, "Single Sided, 8 Sectors Per Track\n"); 556 break; 557 case DS8SPT: 558 (void) fprintf(fout, "Double Sided, 8 Sectors Per Track\n"); 559 break; 560 case SS9SPT: 561 (void) fprintf(fout, "Single Sided, 9 Sectors Per Track\n"); 562 break; 563 case DS9SPT: 564 (void) fprintf(fout, "Double Sided, 9 Sectors Per Track\n"); 565 break; 566 case DS18SPT: 567 (void) fprintf(fout, "Double Sided, 18 Sectors Per Track\n"); 568 break; 569 case DS9_15SPT: 570 (void) fprintf(fout, "Double Sided, 9-15 Sectors Per Track\n"); 571 break; 572 default: 573 (void) fprintf(fout, "Unknown Media Type\n"); 574 } 575 576 return (0); 577 } 578 579 #define ADD_STRING(h, name, value) \ 580 if (nvlist_add_string(h->attr, name, value) != 0) { \ 581 return (FSTYP_ERR_NOMEM); \ 582 } 583 584 #define ADD_UINT32(h, name, value) \ 585 if (nvlist_add_uint32(h->attr, name, value) != 0) { \ 586 return (FSTYP_ERR_NOMEM); \ 587 } 588 589 #define ADD_UINT64(h, name, value) \ 590 if (nvlist_add_uint64(h->attr, name, value) != 0) { \ 591 return (FSTYP_ERR_NOMEM); \ 592 } 593 594 #define ADD_BOOL(h, name, value) \ 595 if (nvlist_add_boolean_value(h->attr, name, value) != 0) { \ 596 return (FSTYP_ERR_NOMEM); \ 597 } 598 599 static int 600 get_attr(fstyp_pcfs_t *h) 601 { 602 char s[64]; 603 604 ADD_UINT32(h, "bytes_per_sector", h->bps); 605 ADD_UINT32(h, "sectors_per_cluster", h->bs.spcl); 606 ADD_UINT32(h, "reserved_sectors", PC_RESSEC(h)); 607 ADD_UINT32(h, "fats", h->bs.nfat); 608 ADD_UINT32(h, "root_entry_count", PC_NROOTENT(h)); 609 ADD_UINT32(h, "total_sectors_16", PC_NSEC(h)); 610 ADD_UINT32(h, "media", h->bs.mediadesriptor); 611 ADD_UINT32(h, "fat_size_16", h->bs.fatsec); 612 ADD_UINT32(h, "sectors_per_track", h->bs.spt); 613 ADD_UINT32(h, "heads", h->bs.nhead); 614 ADD_UINT32(h, "hidden_sectors", h->bs.hiddensec); 615 ADD_UINT32(h, "total_sectors_32", h->bs.totalsec); 616 ADD_UINT32(h, "drive_number", PC_DRVNUM(h)); 617 ADD_UINT32(h, "volume_id", PC_VOLID(h)); 618 ADD_STRING(h, "volume_label", h->volume_label); 619 if (FSTYP_IS_32(h)) { 620 ADD_UINT32(h, "fat_size_32", h->bs32.f_fatlength); 621 } 622 ADD_UINT32(h, "total_sectors", h->TotSec); 623 ADD_UINT32(h, "fat_size", h->FATSz); 624 ADD_UINT32(h, "count_of_clusters", h->CountOfClusters); 625 ADD_UINT32(h, "fat_entry_size", h->fattype); 626 627 ADD_BOOL(h, "gen_clean", B_TRUE); 628 if (PC_VOLID(a) != 0) { 629 (void) snprintf(s, sizeof (s), "%08x", PC_VOLID(a)); 630 ADD_STRING(h, "gen_guid", s); 631 } 632 (void) snprintf(s, sizeof (s), "%d", h->fattype); 633 ADD_STRING(h, "gen_version", s); 634 ADD_STRING(h, "gen_volume_label", h->volume_label); 635 636 return (0); 637 } 638