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