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 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 #include <fcntl.h> 29 #include <stdio.h> 30 #include <stdlib.h> 31 #include <sys/types.h> 32 #include <sys/stat.h> 33 #include <unistd.h> 34 #include <libintl.h> 35 #include <locale.h> 36 #include <string.h> 37 #include <strings.h> 38 #include <errno.h> 39 #include <dirent.h> 40 #include <dlfcn.h> 41 #include <sys/wait.h> 42 #include <sys/fstyp.h> 43 #include <sys/dkio.h> 44 #include <sys/param.h> 45 #include <libfstyp.h> 46 #include <sys/dktp/fdisk.h> 47 #include <sys/fs/pc_label.h> 48 49 #define FSTYP_LIBFS_DIR "/usr/lib/fs" 50 51 static const char *getmodfsname(); 52 static char *getexecpathname(); 53 static void dump_nvlist(nvlist_t *list, int indent); 54 static boolean_t dos_to_dev(char *path, char **devpath, int *num); 55 static boolean_t find_dos_drive(int fd, int num, off_t *offset); 56 static void run_legacy_cmds(int fd, char *device, int vflag); 57 static int run_cmd(char *path, char *arg0, char *arg1, char *arg2); 58 59 60 static void 61 usage(void) 62 { 63 (void) fprintf(stderr, gettext("Usage: fstyp [-av] <device>\n")); 64 exit(1); 65 } 66 67 int 68 main(int argc, char **argv) 69 { 70 int fd = -1; 71 int c; 72 int aflag = 0; 73 int vflag = 0; 74 int indent = 0; 75 char *devpath; 76 boolean_t is_dos; 77 int dos_num; 78 off_t offset = 0; 79 nvlist_t *attr = NULL; 80 fstyp_handle_t h = NULL; 81 const char *modfsname; 82 const char *fsname; 83 int error = FSTYP_ERR_NO_MATCH; 84 85 (void) setlocale(LC_ALL, ""); 86 87 #if !defined(TEXT_DOMAIN) 88 #define TEXT_DOMAIN "SYS_TEST" 89 #endif 90 (void) textdomain(TEXT_DOMAIN); 91 92 while ((c = getopt(argc, argv, "av")) != -1) { 93 switch (c) { 94 case 'a': 95 aflag = 1; 96 break; 97 case 'v': 98 vflag = 1; 99 break; 100 default: 101 usage(); 102 break; 103 } 104 } 105 106 argv += optind; 107 argc -= optind; 108 109 if (argc != 1) { 110 usage(); 111 } 112 113 modfsname = getmodfsname(); 114 115 /* 116 * Open device, find partition offset if requested 117 */ 118 if (!(is_dos = dos_to_dev(argv[0], &devpath, &dos_num))) { 119 devpath = argv[0]; 120 } 121 if ((fd = open(devpath, O_RDONLY)) < 0) { 122 error = FSTYP_ERR_DEV_OPEN; 123 goto out; 124 } 125 if (is_dos) { 126 if (!find_dos_drive(fd, dos_num, &offset)) { 127 error = FSTYP_ERR_NO_PARTITION; 128 goto out; 129 } 130 } 131 132 /* 133 * Use libfstyp to identify filesystem 134 */ 135 if ((error = fstyp_init(fd, offset, NULL, &h)) != 0) { 136 goto out; 137 } 138 if ((error = fstyp_ident(h, modfsname, &fsname)) != 0) { 139 fstyp_fini(h); 140 h = NULL; 141 142 run_legacy_cmds(fd, argv[0], vflag); 143 144 goto out; 145 } 146 147 (void) printf("%s\n", fsname); 148 149 /* 150 * Output additional info if requested 151 */ 152 if (vflag) { 153 error = fstyp_dump(h, stdout, stderr); 154 } 155 if (aflag || (vflag && (error == FSTYP_ERR_NOP))) { 156 if ((error = fstyp_get_attr(h, &attr)) != 0) { 157 goto out; 158 } 159 dump_nvlist(attr, indent); 160 } 161 162 out: 163 if (error != 0) { 164 (void) fprintf(stderr, gettext("unknown_fstyp (%s)\n"), 165 fstyp_strerror(h, error)); 166 } 167 if (h != NULL) { 168 fstyp_fini(h); 169 } 170 if (fd >= 0) { 171 (void) close(fd); 172 } 173 if (devpath != argv[0]) { 174 free(devpath); 175 } 176 return (error); 177 178 } 179 180 #define NVP(elem, type, vtype, ptype, format) { \ 181 vtype value; \ 182 \ 183 (void) nvpair_value_##type(elem, &value); \ 184 (void) printf("%*s%s: " format "\n", indent, "", \ 185 nvpair_name(elem), (ptype)value); \ 186 } 187 188 #define NVPA(elem, type, vtype, ptype, format) { \ 189 uint_t i, count; \ 190 vtype *value; \ 191 \ 192 (void) nvpair_value_##type(elem, &value, &count); \ 193 for (i = 0; i < count; i++) { \ 194 (void) printf("%*s%s[%d]: " format "\n", indent, "", \ 195 nvpair_name(elem), i, (ptype)value[i]); \ 196 } \ 197 } 198 199 static void 200 dump_nvlist(nvlist_t *list, int indent) 201 { 202 nvpair_t *elem = NULL; 203 boolean_t bool_value; 204 nvlist_t *nvlist_value; 205 nvlist_t **nvlist_array_value; 206 uint_t i, count; 207 208 if (list == NULL) { 209 return; 210 } 211 212 while ((elem = nvlist_next_nvpair(list, elem)) != NULL) { 213 switch (nvpair_type(elem)) { 214 case DATA_TYPE_BOOLEAN_VALUE: 215 (void) nvpair_value_boolean_value(elem, &bool_value); 216 (void) printf("%*s%s: %s\n", indent, "", 217 nvpair_name(elem), bool_value ? "true" : "false"); 218 break; 219 220 case DATA_TYPE_BYTE: 221 NVP(elem, byte, uchar_t, int, "%u"); 222 break; 223 224 case DATA_TYPE_INT8: 225 NVP(elem, int8, int8_t, int, "%d"); 226 break; 227 228 case DATA_TYPE_UINT8: 229 NVP(elem, uint8, uint8_t, int, "%u"); 230 break; 231 232 case DATA_TYPE_INT16: 233 NVP(elem, int16, int16_t, int, "%d"); 234 break; 235 236 case DATA_TYPE_UINT16: 237 NVP(elem, uint16, uint16_t, int, "%u"); 238 break; 239 240 case DATA_TYPE_INT32: 241 NVP(elem, int32, int32_t, long, "%ld"); 242 break; 243 244 case DATA_TYPE_UINT32: 245 NVP(elem, uint32, uint32_t, ulong_t, "%lu"); 246 break; 247 248 case DATA_TYPE_INT64: 249 NVP(elem, int64, int64_t, longlong_t, "%lld"); 250 break; 251 252 case DATA_TYPE_UINT64: 253 NVP(elem, uint64, uint64_t, u_longlong_t, "%llu"); 254 break; 255 256 case DATA_TYPE_STRING: 257 NVP(elem, string, char *, char *, "'%s'"); 258 break; 259 260 case DATA_TYPE_BYTE_ARRAY: 261 NVPA(elem, byte_array, uchar_t, int, "%u"); 262 break; 263 264 case DATA_TYPE_INT8_ARRAY: 265 NVPA(elem, int8_array, int8_t, int, "%d"); 266 break; 267 268 case DATA_TYPE_UINT8_ARRAY: 269 NVPA(elem, uint8_array, uint8_t, int, "%u"); 270 break; 271 272 case DATA_TYPE_INT16_ARRAY: 273 NVPA(elem, int16_array, int16_t, int, "%d"); 274 break; 275 276 case DATA_TYPE_UINT16_ARRAY: 277 NVPA(elem, uint16_array, uint16_t, int, "%u"); 278 break; 279 280 case DATA_TYPE_INT32_ARRAY: 281 NVPA(elem, int32_array, int32_t, long, "%ld"); 282 break; 283 284 case DATA_TYPE_UINT32_ARRAY: 285 NVPA(elem, uint32_array, uint32_t, ulong_t, "%lu"); 286 break; 287 288 case DATA_TYPE_INT64_ARRAY: 289 NVPA(elem, int64_array, int64_t, longlong_t, "%lld"); 290 break; 291 292 case DATA_TYPE_UINT64_ARRAY: 293 NVPA(elem, uint64_array, uint64_t, u_longlong_t, 294 "%llu"); 295 break; 296 297 case DATA_TYPE_STRING_ARRAY: 298 NVPA(elem, string_array, char *, char *, "'%s'"); 299 break; 300 301 case DATA_TYPE_NVLIST: 302 (void) nvpair_value_nvlist(elem, &nvlist_value); 303 (void) printf("%*s%s:\n", indent, "", 304 nvpair_name(elem)); 305 dump_nvlist(nvlist_value, indent + 4); 306 break; 307 308 case DATA_TYPE_NVLIST_ARRAY: 309 (void) nvpair_value_nvlist_array(elem, 310 &nvlist_array_value, &count); 311 for (i = 0; i < count; i++) { 312 (void) printf("%*s%s[%u]:\n", indent, "", 313 nvpair_name(elem), i); 314 dump_nvlist(nvlist_array_value[i], indent + 4); 315 } 316 break; 317 318 default: 319 (void) printf(gettext("bad config type %d for %s\n"), 320 nvpair_type(elem), nvpair_name(elem)); 321 } 322 } 323 } 324 325 /* 326 * If the executable is a fs-specific hardlink, /usr/lib/fs/<fsname>/fstyp, 327 * return that fsname; otherwise return NULL. 328 */ 329 static const char * 330 getmodfsname() 331 { 332 static char fsname_buf[FSTYPSZ + 1]; 333 char *fsname = NULL; 334 char *path; 335 char *p; 336 int len; 337 338 if ((path = getexecpathname()) == NULL) { 339 return (NULL); 340 } 341 if ((p = strrchr(path, '/')) != NULL) { 342 *p = '\0'; 343 if ((p = strrchr(path, '/')) != NULL) { 344 *p++ = '\0'; 345 len = strlen(p); 346 if ((strcmp(path, FSTYP_LIBFS_DIR) == 0) && 347 (len > 0) && (len < sizeof (fsname_buf))) { 348 (void) strlcpy(fsname_buf, p, 349 sizeof (fsname_buf)); 350 fsname = fsname_buf; 351 } 352 } 353 } 354 free(path); 355 return (fsname); 356 } 357 358 /* 359 * Return executable's absolute pathname 360 */ 361 static char * 362 getexecpathname() 363 { 364 size_t size; 365 const char *execname; 366 char *cwd; 367 char *path; 368 char *rpath; 369 370 size = pathconf(".", _PC_PATH_MAX) + 1; 371 path = malloc(size); 372 rpath = malloc(size); 373 cwd = getcwd(NULL, size); 374 if ((path == NULL) || (rpath == NULL) || (cwd == NULL)) { 375 goto out; 376 } 377 execname = getexecname(); 378 379 if (execname[0] == '/') { 380 (void) snprintf(path, size, "%s", execname); 381 } else { 382 (void) snprintf(path, size, "%s/%s", cwd, execname); 383 } 384 if (realpath(path, rpath) == NULL) { 385 free(rpath); 386 rpath = NULL; 387 } 388 389 out: 390 if (path != NULL) { 391 free(path); 392 } 393 if (cwd != NULL) { 394 free(cwd); 395 } 396 return (rpath); 397 } 398 399 /* 400 * Separates dos notation device spec into device and drive number 401 */ 402 static boolean_t 403 dos_to_dev(char *path, char **devpath, int *num) 404 { 405 char *p; 406 407 if ((p = strrchr(path, ':')) == NULL) { 408 return (B_FALSE); 409 } 410 if ((*num = atoi(p + 1)) == 0) { 411 return (B_FALSE); 412 } 413 p[0] = '\0'; 414 *devpath = strdup(path); 415 p[0] = ':'; 416 return (*devpath != NULL); 417 } 418 419 static boolean_t 420 is_dos_drive(uchar_t type) 421 { 422 return ((type == DOSOS12) || (type == DOSOS16) || 423 (type == DOSHUGE) || (type == FDISK_WINDOWS) || 424 (type == FDISK_EXT_WIN) || (type == FDISK_FAT95) || 425 (type == DIAGPART)); 426 } 427 428 static boolean_t 429 is_dos_extended(uchar_t id) 430 { 431 return ((id == EXTDOS) || (id == FDISK_EXTLBA)); 432 } 433 434 struct part_find_s { 435 int num; 436 int count; 437 int systid; 438 int r_systid; 439 int r_relsect; 440 int r_numsect; 441 }; 442 443 enum { WALK_CONTINUE, WALK_TERMINATE }; 444 445 /* 446 * Walk partition tables and invoke a callback for each. 447 */ 448 static void 449 walk_partitions(int fd, int startsec, off_t secsz, 450 int (*f)(void *, int, int, int), void *arg) 451 { 452 uint32_t buf[1024/4]; 453 int bufsize = 1024; 454 struct mboot *mboot = (struct mboot *)&buf[0]; 455 struct ipart ipart[FD_NUMPART]; 456 int sec = startsec; 457 int lastsec = sec + 1; 458 int relsect; 459 int ext = 0; 460 int systid; 461 boolean_t valid; 462 int i; 463 464 while (sec != lastsec) { 465 if (pread(fd, buf, bufsize, (off_t)sec * secsz) != bufsize) { 466 break; 467 } 468 lastsec = sec; 469 if (ltohs(mboot->signature) != MBB_MAGIC) { 470 break; 471 } 472 bcopy(mboot->parts, ipart, FD_NUMPART * sizeof (struct ipart)); 473 474 for (i = 0; i < FD_NUMPART; i++) { 475 systid = ipart[i].systid; 476 relsect = sec + ltohi(ipart[i].relsect); 477 if (systid == 0) { 478 continue; 479 } 480 valid = B_TRUE; 481 if (is_dos_extended(systid) && (sec == lastsec)) { 482 sec = startsec + ltohi(ipart[i].relsect); 483 if (ext++ == 0) { 484 relsect = startsec = sec; 485 } else { 486 valid = B_FALSE; 487 } 488 } 489 if (valid && f(arg, ipart[i].systid, relsect, 490 ltohi(ipart[i].numsect)) == WALK_TERMINATE) { 491 return; 492 } 493 } 494 } 495 } 496 497 static int 498 find_dos_drive_cb(void *arg, int systid, int relsect, int numsect) 499 { 500 struct part_find_s *p = arg; 501 502 if (is_dos_drive(systid)) { 503 if (++p->count == p->num) { 504 p->r_relsect = relsect; 505 p->r_numsect = numsect; 506 p->r_systid = systid; 507 return (WALK_TERMINATE); 508 } 509 } 510 511 return (WALK_CONTINUE); 512 } 513 514 /* 515 * Given a dos drive number, return its relative offset in the drive. 516 */ 517 static boolean_t 518 find_dos_drive(int fd, int num, off_t *offset) 519 { 520 struct dk_minfo mi; 521 off_t secsz; 522 struct part_find_s p = { 0, 0, 0, 0, 0, 0 }; 523 524 p.num = num; 525 526 /* 527 * It is possible that the media we are dealing with can have different 528 * sector size than the default 512 bytes. Query the driver and check 529 * whether the media has different sector size. 530 */ 531 if (ioctl(fd, DKIOCGMEDIAINFO, &mi) < 0) 532 secsz = DEV_BSIZE; 533 else 534 secsz = mi.dki_lbsize; 535 536 if (num > 0) { 537 walk_partitions(fd, 0, secsz, find_dos_drive_cb, &p); 538 if (p.count == num) { 539 *offset = secsz * (off_t)p.r_relsect; 540 return (B_TRUE); 541 } 542 } 543 544 return (B_FALSE); 545 } 546 547 /* 548 * libfstyp identification failed: as a last resort, try to 549 * find and run legacy /usr/lib/fs/<fsname>/fstyp commands. 550 */ 551 static void 552 run_legacy_cmds(int fd, char *device, int vflag) 553 { 554 char *lib_dir = FSTYP_LIBFS_DIR; 555 char *path; 556 long name_max; 557 DIR *dirp; 558 struct dirent *dp_mem, *dp; 559 struct stat st; 560 fstyp_handle_t h; 561 int error; 562 char *arg1, *arg2; 563 564 if (vflag) { 565 arg1 = "-v"; 566 arg2 = device; 567 } else { 568 arg1 = device; 569 arg2 = NULL; 570 } 571 572 if ((dirp = opendir(lib_dir)) == NULL) { 573 return; 574 } 575 576 name_max = pathconf(lib_dir, _PC_NAME_MAX); 577 path = calloc(1, name_max + 1); 578 dp = dp_mem = calloc(1, sizeof (struct dirent) + name_max + 1); 579 if ((path == NULL) || (dp_mem == NULL)) { 580 goto out; 581 } 582 583 while ((readdir_r(dirp, dp, &dp) == 0) && (dp != NULL)) { 584 if (dp->d_name[0] == '.') { 585 continue; 586 } 587 (void) snprintf(path, name_max, "%s/%s", lib_dir, dp->d_name); 588 589 /* it's legacy if there's no libfstyp module for it */ 590 error = fstyp_init(fd, 0, path, &h); 591 if (error != FSTYP_ERR_MOD_NOT_FOUND) { 592 if (error == 0) { 593 fstyp_fini(h); 594 } 595 continue; 596 } 597 598 /* file must exist and be executable */ 599 (void) snprintf(path, name_max, 600 "%s/%s/fstyp", lib_dir, dp->d_name); 601 if ((stat(path, &st) < 0) || 602 ((st.st_mode & S_IXUSR) == 0)) { 603 continue; 604 } 605 606 if ((error = run_cmd(path, "fstyp", arg1, arg2)) == 0) { 607 exit(0); 608 } 609 } 610 611 out: 612 if (dp_mem != NULL) { 613 free(dp_mem); 614 } 615 if (path != NULL) { 616 free(path); 617 } 618 (void) closedir(dirp); 619 } 620 621 static int 622 run_cmd(char *path, char *arg0, char *arg1, char *arg2) 623 { 624 pid_t pid; 625 int status = 1; 626 627 pid = fork(); 628 if (pid < 0) { 629 return (1); 630 } else if (pid == 0) { 631 /* child */ 632 (void) execl(path, arg0, arg1, arg2, 0); 633 exit(1); 634 } 635 /* parent */ 636 (void) wait(&status); 637 return (status); 638 } 639