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 #include <fcntl.h> 27 #include <stdio.h> 28 #include <stdlib.h> 29 #include <sys/types.h> 30 #include <sys/stat.h> 31 #include <unistd.h> 32 #include <libintl.h> 33 #include <locale.h> 34 #include <string.h> 35 #include <strings.h> 36 #include <errno.h> 37 #include <dirent.h> 38 #include <dlfcn.h> 39 #include <sys/wait.h> 40 #include <sys/fstyp.h> 41 #include <sys/dkio.h> 42 #include <sys/param.h> 43 #include <libfstyp.h> 44 #include <sys/dktp/fdisk.h> 45 #include <sys/fs/pc_label.h> 46 47 #include "libadm.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 = getfullrawname(path); 415 p[0] = ':'; 416 if (*devpath != NULL && **devpath == '\0') { 417 free(*devpath); 418 *devpath = NULL; 419 } 420 return (*devpath != NULL); 421 } 422 423 static boolean_t 424 is_dos_drive(uchar_t type) 425 { 426 return ((type == DOSOS12) || (type == DOSOS16) || 427 (type == DOSHUGE) || (type == FDISK_WINDOWS) || 428 (type == FDISK_EXT_WIN) || (type == FDISK_FAT95) || 429 (type == DIAGPART)); 430 } 431 432 static boolean_t 433 is_dos_extended(uchar_t id) 434 { 435 return ((id == EXTDOS) || (id == FDISK_EXTLBA)); 436 } 437 438 struct part_find_s { 439 int num; 440 int count; 441 int systid; 442 int r_systid; 443 int r_relsect; 444 int r_numsect; 445 }; 446 447 enum { WALK_CONTINUE, WALK_TERMINATE }; 448 449 /* 450 * Walk partition tables and invoke a callback for each. 451 */ 452 static void 453 walk_partitions(int fd, int startsec, off_t secsz, 454 int (*f)(void *, int, int, int), void *arg) 455 { 456 uint32_t buf[1024/4]; 457 int bufsize = 1024; 458 struct mboot *mboot = (struct mboot *)&buf[0]; 459 struct ipart ipart[FD_NUMPART]; 460 int sec = startsec; 461 int lastsec = sec + 1; 462 int relsect; 463 int ext = 0; 464 int systid; 465 boolean_t valid; 466 int i; 467 468 while (sec != lastsec) { 469 if (pread(fd, buf, bufsize, (off_t)sec * secsz) != bufsize) { 470 break; 471 } 472 lastsec = sec; 473 if (ltohs(mboot->signature) != MBB_MAGIC) { 474 break; 475 } 476 bcopy(mboot->parts, ipart, FD_NUMPART * sizeof (struct ipart)); 477 478 for (i = 0; i < FD_NUMPART; i++) { 479 systid = ipart[i].systid; 480 relsect = sec + ltohi(ipart[i].relsect); 481 if (systid == 0) { 482 continue; 483 } 484 valid = B_TRUE; 485 if (is_dos_extended(systid) && (sec == lastsec)) { 486 sec = startsec + ltohi(ipart[i].relsect); 487 if (ext++ == 0) { 488 relsect = startsec = sec; 489 } else { 490 valid = B_FALSE; 491 } 492 } 493 if (valid && f(arg, ipart[i].systid, relsect, 494 ltohi(ipart[i].numsect)) == WALK_TERMINATE) { 495 return; 496 } 497 } 498 } 499 } 500 501 static int 502 find_dos_drive_cb(void *arg, int systid, int relsect, int numsect) 503 { 504 struct part_find_s *p = arg; 505 506 if (is_dos_drive(systid)) { 507 if (++p->count == p->num) { 508 p->r_relsect = relsect; 509 p->r_numsect = numsect; 510 p->r_systid = systid; 511 return (WALK_TERMINATE); 512 } 513 } 514 515 return (WALK_CONTINUE); 516 } 517 518 /* 519 * Given a dos drive number, return its relative offset in the drive. 520 */ 521 static boolean_t 522 find_dos_drive(int fd, int num, off_t *offset) 523 { 524 struct dk_minfo mi; 525 off_t secsz; 526 struct part_find_s p = { 0, 0, 0, 0, 0, 0 }; 527 528 p.num = num; 529 530 /* 531 * It is possible that the media we are dealing with can have different 532 * sector size than the default 512 bytes. Query the driver and check 533 * whether the media has different sector size. 534 */ 535 if (ioctl(fd, DKIOCGMEDIAINFO, &mi) < 0) 536 secsz = DEV_BSIZE; 537 else 538 secsz = mi.dki_lbsize; 539 540 if (num > 0) { 541 walk_partitions(fd, 0, secsz, find_dos_drive_cb, &p); 542 if (p.count == num) { 543 *offset = secsz * (off_t)p.r_relsect; 544 return (B_TRUE); 545 } 546 } 547 548 return (B_FALSE); 549 } 550 551 /* 552 * libfstyp identification failed: as a last resort, try to 553 * find and run legacy /usr/lib/fs/<fsname>/fstyp commands. 554 */ 555 static void 556 run_legacy_cmds(int fd, char *device, int vflag) 557 { 558 char *lib_dir = FSTYP_LIBFS_DIR; 559 char *path; 560 long name_max; 561 DIR *dirp; 562 struct dirent *dp_mem, *dp; 563 struct stat st; 564 fstyp_handle_t h; 565 int error; 566 char *arg1, *arg2; 567 568 if (vflag) { 569 arg1 = "-v"; 570 arg2 = device; 571 } else { 572 arg1 = device; 573 arg2 = NULL; 574 } 575 576 if ((dirp = opendir(lib_dir)) == NULL) { 577 return; 578 } 579 580 name_max = pathconf(lib_dir, _PC_NAME_MAX); 581 path = calloc(1, name_max + 1); 582 dp = dp_mem = calloc(1, sizeof (struct dirent) + name_max + 1); 583 if ((path == NULL) || (dp_mem == NULL)) { 584 goto out; 585 } 586 587 while ((readdir_r(dirp, dp, &dp) == 0) && (dp != NULL)) { 588 if (dp->d_name[0] == '.') { 589 continue; 590 } 591 (void) snprintf(path, name_max, "%s/%s", lib_dir, dp->d_name); 592 593 /* it's legacy if there's no libfstyp module for it */ 594 error = fstyp_init(fd, 0, path, &h); 595 if (error != FSTYP_ERR_MOD_NOT_FOUND) { 596 if (error == 0) { 597 fstyp_fini(h); 598 } 599 continue; 600 } 601 602 /* file must exist and be executable */ 603 (void) snprintf(path, name_max, 604 "%s/%s/fstyp", lib_dir, dp->d_name); 605 if ((stat(path, &st) < 0) || 606 ((st.st_mode & S_IXUSR) == 0)) { 607 continue; 608 } 609 610 if ((error = run_cmd(path, "fstyp", arg1, arg2)) == 0) { 611 exit(0); 612 } 613 } 614 615 out: 616 if (dp_mem != NULL) { 617 free(dp_mem); 618 } 619 if (path != NULL) { 620 free(path); 621 } 622 (void) closedir(dirp); 623 } 624 625 static int 626 run_cmd(char *path, char *arg0, char *arg1, char *arg2) 627 { 628 pid_t pid; 629 int status = 1; 630 631 pid = fork(); 632 if (pid < 0) { 633 return (1); 634 } else if (pid == 0) { 635 /* child */ 636 (void) execl(path, arg0, arg1, arg2, 0); 637 exit(1); 638 } 639 /* parent */ 640 (void) wait(&status); 641 return (status); 642 } 643