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