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
usage(void)60 usage(void)
61 {
62 (void) fprintf(stderr, gettext("Usage: fstyp [-av] <device>\n"));
63 exit(1);
64 }
65
66 int
main(int argc,char ** argv)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 *
getmodfsname()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 *
getexecpathname()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
dos_to_dev(char * path,char ** devpath,int * num)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
is_dos_drive(uchar_t type)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
is_dos_extended(uchar_t id)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
walk_partitions(int fd,uint32_t startsec,off_t secsz,int (* f)(void *,int,uint32_t,uint32_t),void * arg)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
find_dos_drive_cb(void * arg,int systid,uint32_t relsect,uint32_t numsect)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
find_dos_drive(int fd,int num,off_t * offset)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
run_legacy_cmds(int fd,char * device,int vflag)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
run_cmd(char * path,char * arg0,char * arg1,char * arg2)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