fstyp.c revision 88ecc943b4eb72f7c4fbbd8435997b85ef171fc3
1898N/A/*
1898N/A * CDDL HEADER START
1898N/A *
1898N/A * The contents of this file are subject to the terms of the
1898N/A * Common Development and Distribution License (the "License").
1898N/A * You may not use this file except in compliance with the License.
1898N/A *
1898N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
1898N/A * or http://www.opensolaris.org/os/licensing.
1898N/A * See the License for the specific language governing permissions
1898N/A * and limitations under the License.
1898N/A *
1898N/A * When distributing Covered Code, include this CDDL HEADER in each
1898N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1898N/A * If applicable, add the following below this CDDL HEADER, with the
1898N/A * fields enclosed by brackets "[]" replaced with your own identifying
1898N/A * information: Portions Copyright [yyyy] [name of copyright owner]
1898N/A *
1898N/A * CDDL HEADER END
1898N/A */
1898N/A/*
1898N/A * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
1898N/A * Use is subject to license terms.
1898N/A */
1898N/A
1898N/A#include <fcntl.h>
1898N/A#include <stdio.h>
1898N/A#include <stdlib.h>
1898N/A#include <sys/types.h>
1898N/A#include <sys/stat.h>
1898N/A#include <unistd.h>
1898N/A#include <libintl.h>
1928N/A#include <locale.h>
1898N/A#include <string.h>
1898N/A#include <strings.h>
1898N/A#include <errno.h>
1898N/A#include <dirent.h>
1898N/A#include <dlfcn.h>
1898N/A#include <sys/wait.h>
1898N/A#include <sys/fstyp.h>
1898N/A#include <sys/dkio.h>
1898N/A#include <sys/param.h>
1898N/A#include <libfstyp.h>
1898N/A#include <sys/dktp/fdisk.h>
1898N/A#include <sys/fs/pc_label.h>
1928N/A
1898N/A#include "libadm.h"
1898N/A
1898N/A#define FSTYP_LIBFS_DIR "/usr/lib/fs"
1898N/A
1898N/Astatic const char *getmodfsname();
1898N/Astatic char *getexecpathname();
1898N/Astatic boolean_t dos_to_dev(char *path, char **devpath, int *num);
1898N/Astatic boolean_t find_dos_drive(int fd, int num, off_t *offset);
1898N/Astatic void run_legacy_cmds(int fd, char *device, int vflag);
1898N/Astatic int run_cmd(char *path, char *arg0, char *arg1, char *arg2);
1898N/A
1928N/A
1928N/Astatic void
1898N/Ausage(void)
1898N/A{
1898N/A (void) fprintf(stderr, gettext("Usage: fstyp [-av] <device>\n"));
1898N/A exit(1);
1898N/A}
1898N/A
1898N/Aint
1898N/Amain(int argc, char **argv)
1898N/A{
1898N/A int fd = -1;
1898N/A int c;
1898N/A int aflag = 0;
1928N/A int vflag = 0;
1928N/A int indent = 0;
1898N/A char *devpath;
1898N/A boolean_t is_dos;
1898N/A int dos_num;
1928N/A off_t offset = 0;
1928N/A nvlist_t *attr = NULL;
1928N/A fstyp_handle_t h = NULL;
1928N/A const char *modfsname;
1928N/A const char *fsname;
1928N/A int error = FSTYP_ERR_NO_MATCH;
1928N/A
1928N/A (void) setlocale(LC_ALL, "");
1898N/A
1898N/A#if !defined(TEXT_DOMAIN)
1898N/A#define TEXT_DOMAIN "SYS_TEST"
1898N/A#endif
1898N/A (void) textdomain(TEXT_DOMAIN);
1898N/A
1898N/A while ((c = getopt(argc, argv, "av")) != -1) {
1898N/A switch (c) {
1898N/A case 'a':
1898N/A aflag = 1;
1898N/A break;
1898N/A case 'v':
1898N/A vflag = 1;
1898N/A break;
1898N/A default:
1898N/A usage();
1898N/A break;
1898N/A }
1898N/A }
1898N/A
1898N/A argv += optind;
1898N/A argc -= optind;
1898N/A
1898N/A if (argc != 1) {
1898N/A usage();
1898N/A }
1898N/A
1898N/A modfsname = getmodfsname();
1898N/A
1898N/A /*
1898N/A * Open device, find partition offset if requested
1898N/A */
1898N/A if (!(is_dos = dos_to_dev(argv[0], &devpath, &dos_num))) {
1898N/A devpath = argv[0];
1898N/A }
1898N/A if ((fd = open(devpath, O_RDONLY)) < 0) {
1898N/A error = FSTYP_ERR_DEV_OPEN;
1898N/A goto out;
1898N/A }
1898N/A if (is_dos) {
1898N/A if (!find_dos_drive(fd, dos_num, &offset)) {
1898N/A error = FSTYP_ERR_NO_PARTITION;
1898N/A goto out;
1898N/A }
1898N/A }
1898N/A
1898N/A /*
1898N/A * Use libfstyp to identify filesystem
1898N/A */
1898N/A if ((error = fstyp_init(fd, offset, NULL, &h)) != 0) {
1898N/A goto out;
1898N/A }
1898N/A if ((error = fstyp_ident(h, modfsname, &fsname)) != 0) {
1898N/A fstyp_fini(h);
1898N/A h = NULL;
1898N/A
1898N/A run_legacy_cmds(fd, argv[0], vflag);
1898N/A
1898N/A goto out;
1898N/A }
1898N/A
1898N/A (void) printf("%s\n", fsname);
1898N/A
1898N/A /*
1898N/A * Output additional info if requested
1898N/A */
1898N/A if (vflag) {
1898N/A error = fstyp_dump(h, stdout, stderr);
1898N/A }
1898N/A if (aflag || (vflag && (error == FSTYP_ERR_NOP))) {
1898N/A if ((error = fstyp_get_attr(h, &attr)) != 0) {
1898N/A goto out;
1898N/A }
1898N/A dump_nvlist(attr, indent);
1898N/A }
1898N/A
1898N/Aout:
1898N/A if (error != 0) {
1898N/A (void) fprintf(stderr, gettext("unknown_fstyp (%s)\n"),
1898N/A fstyp_strerror(h, error));
1898N/A }
1898N/A if (h != NULL) {
1898N/A fstyp_fini(h);
1898N/A }
1898N/A if (fd >= 0) {
1898N/A (void) close(fd);
1898N/A }
1898N/A if (devpath != argv[0]) {
1898N/A free(devpath);
1898N/A }
1898N/A return (error);
1898N/A
1898N/A}
1928N/A
1928N/A/*
1928N/A * If the executable is a fs-specific hardlink, /usr/lib/fs/<fsname>/fstyp,
1928N/A * return that fsname; otherwise return NULL.
1928N/A */
1928N/Astatic const char *
1928N/Agetmodfsname()
1928N/A{
1928N/A static char fsname_buf[FSTYPSZ + 1];
1928N/A char *fsname = NULL;
1928N/A char *path;
1928N/A char *p;
1928N/A int len;
1928N/A
1928N/A if ((path = getexecpathname()) == NULL) {
1898N/A return (NULL);
1898N/A }
1898N/A if ((p = strrchr(path, '/')) != NULL) {
1898N/A *p = '\0';
1898N/A if ((p = strrchr(path, '/')) != NULL) {
1898N/A *p++ = '\0';
1898N/A len = strlen(p);
1898N/A if ((strcmp(path, FSTYP_LIBFS_DIR) == 0) &&
1898N/A (len > 0) && (len < sizeof (fsname_buf))) {
1898N/A (void) strlcpy(fsname_buf, p,
1898N/A sizeof (fsname_buf));
1898N/A fsname = fsname_buf;
1898N/A }
1898N/A }
1898N/A }
1898N/A free(path);
1898N/A return (fsname);
1898N/A}
1898N/A
1898N/A/*
1898N/A * Return executable's absolute pathname
1898N/A */
1898N/Astatic char *
1898N/Agetexecpathname()
1898N/A{
1898N/A size_t size;
1898N/A const char *execname;
1898N/A char *cwd;
1898N/A char *path;
1898N/A char *rpath;
1898N/A
1898N/A size = pathconf(".", _PC_PATH_MAX) + 1;
1898N/A path = malloc(size);
1898N/A rpath = malloc(size);
1898N/A cwd = getcwd(NULL, size);
1898N/A if ((path == NULL) || (rpath == NULL) || (cwd == NULL)) {
1928N/A goto out;
1928N/A }
1928N/A execname = getexecname();
1928N/A
1928N/A if (execname[0] == '/') {
1928N/A (void) snprintf(path, size, "%s", execname);
1928N/A } else {
1928N/A (void) snprintf(path, size, "%s/%s", cwd, execname);
1928N/A }
1928N/A if (realpath(path, rpath) == NULL) {
1928N/A free(rpath);
1928N/A rpath = NULL;
1928N/A }
1928N/A
1928N/Aout:
1928N/A if (path != NULL) {
1928N/A free(path);
1928N/A }
1928N/A if (cwd != NULL) {
1928N/A free(cwd);
1898N/A }
1898N/A return (rpath);
1898N/A}
1898N/A
1928N/A/*
1898N/A * Separates dos notation device spec into device and drive number
1898N/A */
1928N/Astatic boolean_t
1898N/Ados_to_dev(char *path, char **devpath, int *num)
1898N/A{
1898N/A char *p;
1898N/A
1898N/A if ((p = strrchr(path, ':')) == NULL) {
1898N/A return (B_FALSE);
1898N/A }
1898N/A if ((*num = atoi(p + 1)) == 0) {
1898N/A return (B_FALSE);
1898N/A }
1898N/A p[0] = '\0';
1898N/A *devpath = getfullrawname(path);
1898N/A p[0] = ':';
1898N/A if (*devpath != NULL && **devpath == '\0') {
1898N/A free(*devpath);
1898N/A *devpath = NULL;
1898N/A }
1898N/A return (*devpath != NULL);
1928N/A}
1898N/A
1898N/Astatic boolean_t
1898N/Ais_dos_drive(uchar_t type)
1898N/A{
1898N/A return ((type == DOSOS12) || (type == DOSOS16) ||
1898N/A (type == DOSHUGE) || (type == FDISK_WINDOWS) ||
1898N/A (type == FDISK_EXT_WIN) || (type == FDISK_FAT95) ||
1898N/A (type == DIAGPART));
1898N/A}
1898N/A
1898N/Astatic boolean_t
1898N/Ais_dos_extended(uchar_t id)
1898N/A{
1898N/A return ((id == EXTDOS) || (id == FDISK_EXTLBA));
1898N/A}
1898N/A
1898N/Astruct part_find_s {
1898N/A int num;
1898N/A int count;
1898N/A int systid;
1898N/A int r_systid;
1898N/A uint32_t r_relsect;
1898N/A uint32_t r_numsect;
1898N/A};
1898N/A
1898N/Aenum { WALK_CONTINUE, WALK_TERMINATE };
1898N/A
1898N/A/*
1898N/A * Walk partition tables and invoke a callback for each.
1898N/A */
1898N/Astatic void
1898N/Awalk_partitions(int fd, uint32_t startsec, off_t secsz,
1898N/A int (*f)(void *, int, uint32_t, uint32_t), void *arg)
1898N/A{
1898N/A uint32_t buf[1024/4];
1898N/A int bufsize = 1024;
1898N/A struct mboot *mboot = (struct mboot *)&buf[0];
1898N/A struct ipart ipart[FD_NUMPART];
1898N/A uint32_t sec = startsec;
1898N/A uint32_t lastsec = sec + 1;
1898N/A uint32_t relsect;
1898N/A int ext = 0;
1898N/A int systid;
1898N/A boolean_t valid;
1898N/A int i;
1898N/A
1898N/A while (sec != lastsec) {
1898N/A if (pread(fd, buf, bufsize, (off_t)sec * secsz) != bufsize) {
1898N/A break;
1898N/A }
1898N/A lastsec = sec;
1898N/A if (ltohs(mboot->signature) != MBB_MAGIC) {
1898N/A break;
1898N/A }
1898N/A bcopy(mboot->parts, ipart, FD_NUMPART * sizeof (struct ipart));
1898N/A
1898N/A for (i = 0; i < FD_NUMPART; i++) {
1898N/A systid = ipart[i].systid;
1898N/A relsect = sec + ltohi(ipart[i].relsect);
1898N/A if (systid == 0) {
1898N/A continue;
1898N/A }
1898N/A valid = B_TRUE;
1898N/A if (is_dos_extended(systid) && (sec == lastsec)) {
1898N/A sec = startsec + ltohi(ipart[i].relsect);
1898N/A if (ext++ == 0) {
1898N/A relsect = startsec = sec;
1898N/A } else {
1898N/A valid = B_FALSE;
1898N/A }
1898N/A }
1898N/A if (valid && f(arg, ipart[i].systid, relsect,
1898N/A ltohi(ipart[i].numsect)) == WALK_TERMINATE) {
1898N/A return;
1898N/A }
1898N/A }
1898N/A }
1898N/A}
1898N/A
1898N/Astatic int
1898N/Afind_dos_drive_cb(void *arg, int systid, uint32_t relsect, uint32_t numsect)
1898N/A{
1898N/A struct part_find_s *p = arg;
1898N/A
1898N/A if (is_dos_drive(systid)) {
1898N/A if (++p->count == p->num) {
1898N/A p->r_relsect = relsect;
1898N/A p->r_numsect = numsect;
1898N/A p->r_systid = systid;
1898N/A return (WALK_TERMINATE);
1898N/A }
1898N/A }
1898N/A
1898N/A return (WALK_CONTINUE);
1898N/A}
1898N/A
1898N/A/*
1898N/A * Given a dos drive number, return its relative offset in the drive.
1898N/A */
1898N/Astatic boolean_t
1898N/Afind_dos_drive(int fd, int num, off_t *offset)
1898N/A{
1898N/A struct dk_minfo mi;
1898N/A off_t secsz;
1898N/A struct part_find_s p = { 0, 0, 0, 0, 0, 0 };
1898N/A
1898N/A p.num = num;
1898N/A
1898N/A /*
1898N/A * It is possible that the media we are dealing with can have different
* sector size than the default 512 bytes. Query the driver and check
* whether the media has different sector size.
*/
if (ioctl(fd, DKIOCGMEDIAINFO, &mi) < 0)
secsz = DEV_BSIZE;
else
secsz = mi.dki_lbsize;
if (num > 0) {
walk_partitions(fd, 0, secsz, find_dos_drive_cb, &p);
if (p.count == num) {
*offset = secsz * (off_t)p.r_relsect;
return (B_TRUE);
}
}
return (B_FALSE);
}
/*
* libfstyp identification failed: as a last resort, try to
* find and run legacy /usr/lib/fs/<fsname>/fstyp commands.
*/
static void
run_legacy_cmds(int fd, char *device, int vflag)
{
char *lib_dir = FSTYP_LIBFS_DIR;
char *path;
long name_max;
DIR *dirp;
struct dirent *dp_mem, *dp;
struct stat st;
fstyp_handle_t h;
int error;
char *arg1, *arg2;
if (vflag) {
arg1 = "-v";
arg2 = device;
} else {
arg1 = device;
arg2 = NULL;
}
if ((dirp = opendir(lib_dir)) == NULL) {
return;
}
name_max = pathconf(lib_dir, _PC_NAME_MAX);
path = calloc(1, name_max + 1);
dp = dp_mem = calloc(1, sizeof (struct dirent) + name_max + 1);
if ((path == NULL) || (dp_mem == NULL)) {
goto out;
}
while ((readdir_r(dirp, dp, &dp) == 0) && (dp != NULL)) {
if (dp->d_name[0] == '.') {
continue;
}
(void) snprintf(path, name_max, "%s/%s", lib_dir, dp->d_name);
/* it's legacy if there's no libfstyp module for it */
error = fstyp_init(fd, 0, path, &h);
if (error != FSTYP_ERR_MOD_NOT_FOUND) {
if (error == 0) {
fstyp_fini(h);
}
continue;
}
/* file must exist and be executable */
(void) snprintf(path, name_max,
"%s/%s/fstyp", lib_dir, dp->d_name);
if ((stat(path, &st) < 0) ||
((st.st_mode & S_IXUSR) == 0)) {
continue;
}
if ((error = run_cmd(path, "fstyp", arg1, arg2)) == 0) {
exit(0);
}
}
out:
if (dp_mem != NULL) {
free(dp_mem);
}
if (path != NULL) {
free(path);
}
(void) closedir(dirp);
}
static int
run_cmd(char *path, char *arg0, char *arg1, char *arg2)
{
pid_t pid;
int status = 1;
pid = fork();
if (pid < 0) {
return (1);
} else if (pid == 0) {
/* child */
(void) execl(path, arg0, arg1, arg2, 0);
exit(1);
}
/* parent */
(void) wait(&status);
return (status);
}