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