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 #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 == 1) || (type == 4) || (type == 5) || (type == 6) || 424 ((type >= 8) && (type <= 0xf))); 425 } 426 427 static boolean_t 428 is_dos_extended(uchar_t id) 429 { 430 return ((id == EXTDOS) || (id == FDISK_EXTLBA)); 431 } 432 433 struct part_find_s { 434 int num; 435 int count; 436 int systid; 437 int r_systid; 438 int r_relsect; 439 int r_numsect; 440 }; 441 442 enum { WALK_CONTINUE, WALK_TERMINATE }; 443 444 /* 445 * Walk partition tables and invoke a callback for each. 446 */ 447 static void 448 walk_partitions(int fd, int startsec, int (*f)(void *, int, int, int), 449 void *arg) 450 { 451 uint32_t buf[1024/4]; 452 int bufsize = 1024; 453 struct mboot *mboot = (struct mboot *)&buf[0]; 454 struct ipart ipart[FD_NUMPART]; 455 int sec = startsec; 456 int lastsec = sec + 1; 457 int relsect; 458 int ext = 0; 459 int systid; 460 boolean_t valid; 461 int i; 462 463 while (sec != lastsec) { 464 if (pread(fd, buf, bufsize, (off_t)sec * 512) != bufsize) { 465 break; 466 } 467 lastsec = sec; 468 if (ltohs(mboot->signature) != MBB_MAGIC) { 469 break; 470 } 471 bcopy(mboot->parts, ipart, FD_NUMPART * sizeof (struct ipart)); 472 473 for (i = 0; i < FD_NUMPART; i++) { 474 systid = ipart[i].systid; 475 relsect = sec + ltohi(ipart[i].relsect); 476 if (systid == 0) { 477 continue; 478 } 479 valid = B_TRUE; 480 if (is_dos_extended(systid) && (sec == lastsec)) { 481 sec = startsec + ltohi(ipart[i].relsect); 482 if (ext++ == 0) { 483 relsect = startsec = sec; 484 } else { 485 valid = B_FALSE; 486 } 487 } 488 if (valid && f(arg, ipart[i].systid, relsect, 489 ltohi(ipart[i].numsect)) == WALK_TERMINATE) { 490 return; 491 } 492 } 493 } 494 } 495 496 static int 497 find_dos_drive_cb(void *arg, int systid, int relsect, int numsect) 498 { 499 struct part_find_s *p = arg; 500 501 if (is_dos_drive(systid)) { 502 if (++p->count == p->num) { 503 p->r_relsect = relsect; 504 p->r_numsect = numsect; 505 p->r_systid = systid; 506 return (WALK_TERMINATE); 507 } 508 } 509 510 return (WALK_CONTINUE); 511 } 512 513 /* 514 * Given a dos drive number, return its relative sector number, 515 * number of sectors in partition and the system id. 516 */ 517 static boolean_t 518 find_dos_drive(int fd, int num, int *relsect, int *numsect, int *systid) 519 { 520 struct part_find_s p = { 0, 0, 0, 0, 0, 0 }; 521 522 p.num = num; 523 524 if (num > 0) { 525 walk_partitions(fd, 0, find_dos_drive_cb, &p); 526 if (p.count == num) { 527 *relsect = p.r_relsect; 528 *numsect = p.r_numsect; 529 *systid = p.r_systid; 530 return (B_TRUE); 531 } 532 } 533 534 return (B_FALSE); 535 } 536 537 /* 538 * libfstyp identification failed: as a last resort, try to 539 * find and run legacy /usr/lib/fs/<fsname>/fstyp commands. 540 */ 541 static void 542 run_legacy_cmds(int fd, char *device, int vflag) 543 { 544 char *lib_dir = FSTYP_LIBFS_DIR; 545 char *path; 546 long name_max; 547 DIR *dirp; 548 struct dirent *dp_mem, *dp; 549 struct stat st; 550 fstyp_handle_t h; 551 int error; 552 char *arg1, *arg2; 553 554 if (vflag) { 555 arg1 = "-v"; 556 arg2 = device; 557 } else { 558 arg1 = device; 559 arg2 = NULL; 560 } 561 562 if ((dirp = opendir(lib_dir)) == NULL) { 563 return; 564 } 565 566 name_max = pathconf(lib_dir, _PC_NAME_MAX); 567 path = calloc(1, name_max + 1); 568 dp = dp_mem = calloc(1, sizeof (struct dirent) + name_max + 1); 569 if ((path == NULL) || (dp_mem == NULL)) { 570 goto out; 571 } 572 573 while ((readdir_r(dirp, dp, &dp) == 0) && (dp != NULL)) { 574 if (dp->d_name[0] == '.') { 575 continue; 576 } 577 (void) snprintf(path, name_max, "%s/%s", lib_dir, dp->d_name); 578 579 /* it's legacy if there's no libfstyp module for it */ 580 error = fstyp_init(fd, 0, path, &h); 581 if (error != FSTYP_ERR_MOD_NOT_FOUND) { 582 if (error == 0) { 583 fstyp_fini(h); 584 } 585 continue; 586 } 587 588 /* file must exist and be executable */ 589 (void) snprintf(path, name_max, 590 "%s/%s/fstyp", lib_dir, dp->d_name); 591 if ((stat(path, &st) < 0) || 592 ((st.st_mode & S_IXUSR) == 0)) { 593 continue; 594 } 595 596 if ((error = run_cmd(path, "fstyp", arg1, arg2)) == 0) { 597 exit(0); 598 } 599 } 600 601 out: 602 if (dp_mem != NULL) { 603 free(dp_mem); 604 } 605 if (path != NULL) { 606 free(path); 607 } 608 (void) closedir(dirp); 609 } 610 611 static int 612 run_cmd(char *path, char *arg0, char *arg1, char *arg2) 613 { 614 pid_t pid; 615 int status = 1; 616 617 pid = fork(); 618 if (pid < 0) { 619 return (1); 620 } else if (pid == 0) { 621 /* child */ 622 (void) execl(path, arg0, arg1, arg2, 0); 623 exit(1); 624 } 625 /* parent */ 626 (void) wait(&status); 627 return (status); 628 } 629