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 2009 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 boolean_t dos_to_dev(char *path, char **devpath, int *num); 54 static boolean_t find_dos_drive(int fd, int num, off_t *offset); 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; 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, &offset)) { 126 error = FSTYP_ERR_NO_PARTITION; 127 goto out; 128 } 129 } 130 131 /* 132 * Use libfstyp to identify filesystem 133 */ 134 if ((error = fstyp_init(fd, offset, NULL, &h)) != 0) { 135 goto out; 136 } 137 if ((error = fstyp_ident(h, modfsname, &fsname)) != 0) { 138 fstyp_fini(h); 139 h = NULL; 140 141 run_legacy_cmds(fd, argv[0], vflag); 142 143 goto out; 144 } 145 146 (void) printf("%s\n", fsname); 147 148 /* 149 * Output additional info if requested 150 */ 151 if (vflag) { 152 error = fstyp_dump(h, stdout, stderr); 153 } 154 if (aflag || (vflag && (error == FSTYP_ERR_NOP))) { 155 if ((error = fstyp_get_attr(h, &attr)) != 0) { 156 goto out; 157 } 158 dump_nvlist(attr, indent); 159 } 160 161 out: 162 if (error != 0) { 163 (void) fprintf(stderr, gettext("unknown_fstyp (%s)\n"), 164 fstyp_strerror(h, error)); 165 } 166 if (h != NULL) { 167 fstyp_fini(h); 168 } 169 if (fd >= 0) { 170 (void) close(fd); 171 } 172 if (devpath != argv[0]) { 173 free(devpath); 174 } 175 return (error); 176 177 } 178 179 /* 180 * If the executable is a fs-specific hardlink, /usr/lib/fs/<fsname>/fstyp, 181 * return that fsname; otherwise return NULL. 182 */ 183 static const char * 184 getmodfsname() 185 { 186 static char fsname_buf[FSTYPSZ + 1]; 187 char *fsname = NULL; 188 char *path; 189 char *p; 190 int len; 191 192 if ((path = getexecpathname()) == NULL) { 193 return (NULL); 194 } 195 if ((p = strrchr(path, '/')) != NULL) { 196 *p = '\0'; 197 if ((p = strrchr(path, '/')) != NULL) { 198 *p++ = '\0'; 199 len = strlen(p); 200 if ((strcmp(path, FSTYP_LIBFS_DIR) == 0) && 201 (len > 0) && (len < sizeof (fsname_buf))) { 202 (void) strlcpy(fsname_buf, p, 203 sizeof (fsname_buf)); 204 fsname = fsname_buf; 205 } 206 } 207 } 208 free(path); 209 return (fsname); 210 } 211 212 /* 213 * Return executable's absolute pathname 214 */ 215 static char * 216 getexecpathname() 217 { 218 size_t size; 219 const char *execname; 220 char *cwd; 221 char *path; 222 char *rpath; 223 224 size = pathconf(".", _PC_PATH_MAX) + 1; 225 path = malloc(size); 226 rpath = malloc(size); 227 cwd = getcwd(NULL, size); 228 if ((path == NULL) || (rpath == NULL) || (cwd == NULL)) { 229 goto out; 230 } 231 execname = getexecname(); 232 233 if (execname[0] == '/') { 234 (void) snprintf(path, size, "%s", execname); 235 } else { 236 (void) snprintf(path, size, "%s/%s", cwd, execname); 237 } 238 if (realpath(path, rpath) == NULL) { 239 free(rpath); 240 rpath = NULL; 241 } 242 243 out: 244 if (path != NULL) { 245 free(path); 246 } 247 if (cwd != NULL) { 248 free(cwd); 249 } 250 return (rpath); 251 } 252 253 /* 254 * Separates dos notation device spec into device and drive number 255 */ 256 static boolean_t 257 dos_to_dev(char *path, char **devpath, int *num) 258 { 259 char *p; 260 261 if ((p = strrchr(path, ':')) == NULL) { 262 return (B_FALSE); 263 } 264 if ((*num = atoi(p + 1)) == 0) { 265 return (B_FALSE); 266 } 267 p[0] = '\0'; 268 *devpath = getfullrawname(path); 269 p[0] = ':'; 270 if (*devpath != NULL && **devpath == '\0') { 271 free(*devpath); 272 *devpath = NULL; 273 } 274 return (*devpath != NULL); 275 } 276 277 static boolean_t 278 is_dos_drive(uchar_t type) 279 { 280 return ((type == DOSOS12) || (type == DOSOS16) || 281 (type == DOSHUGE) || (type == FDISK_WINDOWS) || 282 (type == FDISK_EXT_WIN) || (type == FDISK_FAT95) || 283 (type == DIAGPART)); 284 } 285 286 static boolean_t 287 is_dos_extended(uchar_t id) 288 { 289 return ((id == EXTDOS) || (id == FDISK_EXTLBA)); 290 } 291 292 struct part_find_s { 293 int num; 294 int count; 295 int systid; 296 int r_systid; 297 uint32_t r_relsect; 298 uint32_t r_numsect; 299 }; 300 301 enum { WALK_CONTINUE, WALK_TERMINATE }; 302 303 /* 304 * Walk partition tables and invoke a callback for each. 305 */ 306 static void 307 walk_partitions(int fd, uint32_t startsec, off_t secsz, 308 int (*f)(void *, int, uint32_t, uint32_t), void *arg) 309 { 310 uint32_t buf[1024/4]; 311 int bufsize = 1024; 312 struct mboot *mboot = (struct mboot *)&buf[0]; 313 struct ipart ipart[FD_NUMPART]; 314 uint32_t sec = startsec; 315 uint32_t lastsec = sec + 1; 316 uint32_t relsect; 317 int ext = 0; 318 int systid; 319 boolean_t valid; 320 int i; 321 322 while (sec != lastsec) { 323 if (pread(fd, buf, bufsize, (off_t)sec * secsz) != bufsize) { 324 break; 325 } 326 lastsec = sec; 327 if (ltohs(mboot->signature) != MBB_MAGIC) { 328 break; 329 } 330 bcopy(mboot->parts, ipart, FD_NUMPART * sizeof (struct ipart)); 331 332 for (i = 0; i < FD_NUMPART; i++) { 333 systid = ipart[i].systid; 334 relsect = sec + ltohi(ipart[i].relsect); 335 if (systid == 0) { 336 continue; 337 } 338 valid = B_TRUE; 339 if (is_dos_extended(systid) && (sec == lastsec)) { 340 sec = startsec + ltohi(ipart[i].relsect); 341 if (ext++ == 0) { 342 relsect = startsec = sec; 343 } else { 344 valid = B_FALSE; 345 } 346 } 347 if (valid && f(arg, ipart[i].systid, relsect, 348 ltohi(ipart[i].numsect)) == WALK_TERMINATE) { 349 return; 350 } 351 } 352 } 353 } 354 355 static int 356 find_dos_drive_cb(void *arg, int systid, uint32_t relsect, uint32_t numsect) 357 { 358 struct part_find_s *p = arg; 359 360 if (is_dos_drive(systid)) { 361 if (++p->count == p->num) { 362 p->r_relsect = relsect; 363 p->r_numsect = numsect; 364 p->r_systid = systid; 365 return (WALK_TERMINATE); 366 } 367 } 368 369 return (WALK_CONTINUE); 370 } 371 372 /* 373 * Given a dos drive number, return its relative offset in the drive. 374 */ 375 static boolean_t 376 find_dos_drive(int fd, int num, off_t *offset) 377 { 378 struct dk_minfo mi; 379 off_t secsz; 380 struct part_find_s p = { 0, 0, 0, 0, 0, 0 }; 381 382 p.num = num; 383 384 /* 385 * It is possible that the media we are dealing with can have different 386 * sector size than the default 512 bytes. Query the driver and check 387 * whether the media has different sector size. 388 */ 389 if (ioctl(fd, DKIOCGMEDIAINFO, &mi) < 0) 390 secsz = DEV_BSIZE; 391 else 392 secsz = mi.dki_lbsize; 393 394 if (num > 0) { 395 walk_partitions(fd, 0, secsz, find_dos_drive_cb, &p); 396 if (p.count == num) { 397 *offset = secsz * (off_t)p.r_relsect; 398 return (B_TRUE); 399 } 400 } 401 402 return (B_FALSE); 403 } 404 405 /* 406 * libfstyp identification failed: as a last resort, try to 407 * find and run legacy /usr/lib/fs/<fsname>/fstyp commands. 408 */ 409 static void 410 run_legacy_cmds(int fd, char *device, int vflag) 411 { 412 char *lib_dir = FSTYP_LIBFS_DIR; 413 char *path; 414 long name_max; 415 DIR *dirp; 416 struct dirent *dp_mem, *dp; 417 struct stat st; 418 fstyp_handle_t h; 419 int error; 420 char *arg1, *arg2; 421 422 if (vflag) { 423 arg1 = "-v"; 424 arg2 = device; 425 } else { 426 arg1 = device; 427 arg2 = NULL; 428 } 429 430 if ((dirp = opendir(lib_dir)) == NULL) { 431 return; 432 } 433 434 name_max = pathconf(lib_dir, _PC_NAME_MAX); 435 path = calloc(1, name_max + 1); 436 dp = dp_mem = calloc(1, sizeof (struct dirent) + name_max + 1); 437 if ((path == NULL) || (dp_mem == NULL)) { 438 goto out; 439 } 440 441 while ((readdir_r(dirp, dp, &dp) == 0) && (dp != NULL)) { 442 if (dp->d_name[0] == '.') { 443 continue; 444 } 445 (void) snprintf(path, name_max, "%s/%s", lib_dir, dp->d_name); 446 447 /* it's legacy if there's no libfstyp module for it */ 448 error = fstyp_init(fd, 0, path, &h); 449 if (error != FSTYP_ERR_MOD_NOT_FOUND) { 450 if (error == 0) { 451 fstyp_fini(h); 452 } 453 continue; 454 } 455 456 /* file must exist and be executable */ 457 (void) snprintf(path, name_max, 458 "%s/%s/fstyp", lib_dir, dp->d_name); 459 if ((stat(path, &st) < 0) || 460 ((st.st_mode & S_IXUSR) == 0)) { 461 continue; 462 } 463 464 if ((error = run_cmd(path, "fstyp", arg1, arg2)) == 0) { 465 exit(0); 466 } 467 } 468 469 out: 470 if (dp_mem != NULL) { 471 free(dp_mem); 472 } 473 if (path != NULL) { 474 free(path); 475 } 476 (void) closedir(dirp); 477 } 478 479 static int 480 run_cmd(char *path, char *arg0, char *arg1, char *arg2) 481 { 482 pid_t pid; 483 int status = 1; 484 485 pid = fork(); 486 if (pid < 0) { 487 return (1); 488 } else if (pid == 0) { 489 /* child */ 490 (void) execl(path, arg0, arg1, arg2, 0); 491 exit(1); 492 } 493 /* parent */ 494 (void) wait(&status); 495 return (status); 496 } 497