/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* rmf_misc.c :
* Miscelleneous routines for rmformat.
*/
#include <sys/types.h>
#include <stdio.h>
#include <sys/mnttab.h>
#include <volmgt.h>
#include <sys/dkio.h>
#include <sys/fdio.h>
#include <sys/vtoc.h>
#include <sys/termios.h>
#include <sys/mount.h>
#include <ctype.h>
#include <signal.h>
#include <sys/wait.h>
#include <dirent.h>
#include <priv_utils.h>
#include <stdarg.h>
#include "rmformat.h"
/*
* Definitions.
*/
#define SENSE_KEY(rqbuf) (rqbuf[2] & 0xf) /* scsi error category */
#define ASC(rqbuf) (rqbuf[12]) /* additional sense code */
#define ASCQ(rqbuf) (rqbuf[13]) /* ASC qualifier */
#define DEFAULT_SCSI_TIMEOUT 60
#define INQUIRY_CMD 0x12
#define RQBUFLEN 32
#define CD_RW 1 /* CD_RW/CD-R */
#define WRITE_10_CMD 0x2A
#define READ_INFO_CMD 0x51
#define SYNC_CACHE_CMD 0x35
#define CLOSE_TRACK_CMD 0x5B
#define MODE_SENSE_10_CMD 0x5A
#define DEVFS_PREFIX "/devices"
int uscsi_error; /* used for debugging failed uscsi */
char rqbuf[RQBUFLEN];
static uint_t total_retries;
static struct uscsi_cmd uscmd;
static char ucdb[16];
uchar_t uscsi_status, rqstatus, rqresid;
int total_devices_found = 0;
int removable_found = 0;
extern char *global_intr_msg;
extern int vol_running;
extern char *dev_name;
extern int32_t m_flag;
/*
* ON-private functions from libvolmgt
*/
int _dev_mounted(char *path);
/*
* Function prototypes.
*/
static int my_umount(char *mountp);
static int my_volrmmount(char *real_name);
static int vol_name_to_dev_node(char *vname, char *found);
static int vol_lookup(char *supplied, char *found);
static device_t *get_device(char *user_supplied, char *node);
static char *get_physical_name(char *path);
static int lookup_device(char *supplied, char *found);
static void fini_device(device_t *dev);
static int is_cd(char *node);
void *my_zalloc(size_t size);
void err_msg(char *fmt, ...);
int inquiry(int fd, uchar_t *inq);
struct uscsi_cmd *get_uscsi_cmd(void);
int uscsi(int fd, struct uscsi_cmd *scmd);
int get_mode_page(int fd, int page_no, int pc, int buf_len,
uchar_t *buffer);
int mode_sense(int fd, uchar_t pc, int dbd, int page_len,
uchar_t *buffer);
uint16_t read_scsi16(void *addr);
int check_device(device_t *dev, int cond);
static void get_media_info(device_t *t_dev, char *sdev,
char *pname, char *sn);
extern void process_p_flag(smedia_handle_t handle, int32_t fd);
void
my_perror(char *err_string)
{
int error_no;
if (errno == 0)
return;
error_no = errno;
(void) fprintf(stderr, "%s", err_string);
(void) fprintf(stderr, gettext(" : "));
errno = error_no;
perror("");
}
int32_t
get_confirmation()
{
char c;
(void) fprintf(stderr, gettext("Do you want to continue? (y/n)"));
c = getchar();
if (c == 'y' || c == 'Y')
return (1);
else if (c == 'n' || c == 'N')
return (0);
else {
(void) fprintf(stderr, gettext("Invalid choice\n"));
return (0);
}
}
void
get_passwd(struct smwp_state *wp, int32_t confirm)
{
char passwd[256], re_passwd[256];
int32_t len;
struct termios tio;
int32_t echo_off = 0;
FILE *in, *out;
char *buf;
in = fopen("/dev/tty", "r+");
if (in == NULL) {
in = stdin;
out = stderr;
} else {
out = in;
}
/* Turn echoing off if it is on now. */
if (tcgetattr(fileno(in), &tio) < 0) {
PERROR("Echo off ioctl failed");
exit(1);
}
if (tio.c_lflag & ECHO) {
tio.c_lflag &= ~ECHO;
/* echo_off = tcsetattr(fileno(in), TCSAFLUSH, &tio) == 0; */
echo_off = tcsetattr(fileno(in), TCSAFLUSH, &tio) == 0;
tio.c_lflag |= ECHO;
}
/* CONSTCOND */
while (1) {
(void) fputs(
gettext("Please enter password (32 chars maximum):"),
out);
(void) fflush(out);
buf = fgets(passwd, (size_t)256, in);
rewind(in);
if (buf == NULL) {
PERROR("Error reading password");
continue;
}
len = strlen(passwd);
(void) fputc('\n', out);
len--; /* To offset the \n */
if ((len <= 0) || (len > 32)) {
(void) fprintf(stderr,
gettext("Invalid length of password \n"));
(void) fputs("Try again\n", out);
continue;
}
if (!confirm)
break;
(void) fputs("Please reenter password:", out);
(void) fflush(out);
buf = fgets(re_passwd, (size_t)256, in);
rewind(in);
(void) fputc('\n', out);
if ((buf == NULL) || strcmp(passwd, re_passwd)) {
(void) fputs("passwords did not match\n", out);
(void) fputs("Try again\n", out);
} else {
break;
}
}
wp->sm_passwd_len = len;
(void) strncpy(wp->sm_passwd, passwd, wp->sm_passwd_len);
wp->sm_version = SMWP_STATE_V_1;
/* Restore echoing. */
if (echo_off)
(void) tcsetattr(fileno(in), TCSAFLUSH, &tio);
}
int32_t
check_and_unmount_vold(char *device_name, int32_t flag)
{
char *real_name;
char *nm;
char tmp_path_name[PATH_MAX];
struct stat stat_buf;
int32_t ret_val = 0;
struct mnttab *mntp;
FILE *fp;
int nl;
DPRINTF1("Device name %s\n", device_name);
if (volmgt_running() == 0) {
DPRINTF("Vold not running\n");
return (0);
}
if ((nm = volmgt_symname(device_name)) == NULL) {
DPRINTF("path not managed\n");
real_name = media_findname(device_name);
} else {
DPRINTF1("path managed as %s\n", nm);
real_name = media_findname(nm);
DPRINTF1("real name %s\n", real_name);
}
if (real_name == NULL)
return (-1);
/*
* To find out whether the device has been mounted by
* volume manager...
*
* Convert the real name to a block device address.
* Do a partial match with the mnttab entries.
* Make sure the match is in the beginning to avoid if
* anybody puts a label similiar to volume manager path names.
* Then use "volrmmount -e <dev_name>" if -U flag is set.
*/
nl = strlen("/vol/dev/");
if (strncmp(real_name, "/vol/dev/", nl) != 0)
return (0);
if (real_name[nl] == 'r') {
(void) snprintf(tmp_path_name, PATH_MAX, "%s%s", "/vol/dev/",
&real_name[nl + 1]);
} else {
(void) snprintf(tmp_path_name, PATH_MAX, "%s", real_name);
}
DPRINTF1("%s \n", tmp_path_name);
ret_val = stat(tmp_path_name, &stat_buf);
if (ret_val < 0) {
PERROR("Could not stat");
return (-1);
}
fp = fopen("/etc/mnttab", "r");
if (fp == NULL) {
PERROR("Could not open /etc/mnttab");
return (-1);
}
mntp = (struct mnttab *)malloc(sizeof (struct mnttab));
if (mntp == NULL) {
PERROR("malloc failed");
(void) fclose(fp);
return (-1);
}
errno = 0;
while (getmntent(fp, mntp) == 0) {
if (errno != 0) {
PERROR("Error with mnttab");
(void) fclose(fp);
return (-1);
}
/* Is it a probable entry? */
DPRINTF1(" %s \n", mntp->mnt_special);
if (strstr(mntp->mnt_special, tmp_path_name) !=
mntp->mnt_special) {
/* Skip to next entry */
continue;
} else {
DPRINTF1("Found!! %s\n", mntp->mnt_special);
ret_val = 1;
break;
}
}
if (ret_val == 1) {
if (flag) {
if (my_volrmmount(real_name) < 0) {
ret_val = -1;
}
} else {
ret_val = -1;
}
}
(void) fclose(fp);
free(mntp);
return (ret_val);
}
/*
* This routine checks if a device has mounted partitions. The
* device name is assumed to be /dev/rdsk/cNtNdNsN. So, this can
* be used for SCSI and PCMCIA cards.
* Returns
* 0 : if not mounted
* 1 : if successfully unmounted
* -1 : Any error or umount failed
*/
int32_t
check_and_unmount_scsi(char *device_name, int32_t flag)
{
struct mnttab *mntrefp;
struct mnttab *mntp;
FILE *fp;
char block_dev_name[PATH_MAX];
char tmp_name[PATH_MAX];
int32_t i, j;
int32_t unmounted = 0;
/*
* If the device name is not a character special, anyway we
* can not progress further
*/
if (strncmp(device_name, "/dev/rdsk/c", strlen("/dev/rdsk/c")) != 0)
return (0);
(void) snprintf(block_dev_name, PATH_MAX, "/dev/%s",
&device_name[strlen("/dev/r")]);
fp = fopen("/etc/mnttab", "r");
if (fp == NULL) {
PERROR("Could not open /etc/mnttab");
return (-1);
}
mntrefp = (struct mnttab *)malloc(sizeof (struct mnttab));
if (mntrefp == NULL) {
PERROR("malloc failed");
(void) fclose(fp);
return (-1);
}
mntp = (struct mnttab *)malloc(sizeof (struct mnttab));
if (mntp == NULL) {
PERROR("malloc failed");
(void) fclose(fp);
free(mntrefp);
return (-1);
}
/* Try all the partitions */
(void) snprintf(tmp_name, PATH_MAX, "/dev/%s",
&device_name[strlen("/dev/r")]);
tmp_name[strlen("/dev/dsk/c0t0d0s")] = '\0';
errno = 0;
while (getmntent(fp, mntp) == 0) {
if (errno != 0) {
PERROR("Error with mnttab");
(void) fclose(fp);
return (-1);
}
/* Is it a probable entry? */
if (strncmp(mntp->mnt_special, tmp_name, strlen(tmp_name))) {
/* Skip to next entry */
continue;
}
for (i = 0; i < NDKMAP; i++) {
/* Check for ufs style mount devices */
(void) snprintf(block_dev_name, PATH_MAX,
"%s%d", tmp_name, i);
if (strcmp(mntp->mnt_special, block_dev_name) == 0) {
if (flag) {
if (my_umount(mntp->mnt_mountp) < 0) {
(void) fclose(fp);
return (-1);
}
unmounted = 1;
} else {
(void) fclose(fp);
return (-1);
}
/* Skip to next entry */
continue;
}
/* Try for :1 -> :24 for pcfs */
for (j = 1; j < 24; j++) {
(void) snprintf(block_dev_name, PATH_MAX,
"%s%d:%d", tmp_name, i, j);
if (strcmp(mntp->mnt_special,
block_dev_name) == 0) {
if (flag) {
if (my_umount(mntp->mnt_mountp)
< 0) {
(void) fclose(fp);
return (-1);
}
unmounted = 1;
} else {
(void) fclose(fp);
return (-1);
}
/* Skip to next entry */
continue;
}
(void) snprintf(block_dev_name, PATH_MAX,
"%s%d:%c", tmp_name, i, 'b' + j);
if (strcmp(mntp->mnt_special,
block_dev_name) == 0) {
if (flag) {
if (my_umount(mntp->mnt_mountp)
< 0) {
(void) fclose(fp);
return (-1);
}
unmounted = 1;
} else {
(void) fclose(fp);
return (-1);
}
/* Skip to next entry */
continue;
}
}
}
}
if (unmounted)
return (1);
return (0);
}
/*
* This routine checks if a device has mounted partitions. The
* device name is assumed to be /dev/rdiskette. So, this can
* be used for Floppy controllers
* Returns
* 0 : if not mounted
* 1 : if successfully unmounted
* -1 : Any error or unmount failed
*/
int32_t
check_and_unmount_floppy(int32_t fd, int32_t flag)
{
FILE *fp = NULL;
int32_t mfd;
struct dk_cinfo dkinfo, dkinfo_tmp;
struct mnttab mnt_record;
struct mnttab *mp = &mnt_record;
struct stat stbuf;
char raw_device[PATH_MAX];
int32_t found = 0;
if (ioctl(fd, DKIOCINFO, &dkinfo) < 0) {
return (-1);
}
if ((fp = fopen(MNTTAB, "r")) == NULL) {
PERROR("Could not open /etc/mnttab");
(void) close(fd);
exit(3);
}
while (getmntent(fp, mp) == 0) {
if (strstr(mp->mnt_special, "/dev/fd") == NULL &&
strstr(mp->mnt_special, "/dev/disket") == NULL &&
strstr(mp->mnt_special, "/dev/c") == NULL) {
continue;
}
(void) strcpy(raw_device, "/dev/r");
(void) strcat(raw_device, mp->mnt_special + strlen("/dev/"));
/*
* Attempt to open the device. If it fails, skip it.
*/
/* Turn on the privileges. */
(void) __priv_bracket(PRIV_ON);
mfd = open(raw_device, O_RDWR | O_NDELAY);
/* Turn off the privileges. */
(void) __priv_bracket(PRIV_OFF);
if (mfd < 0) {
continue;
}
/*
* Must be a character device
*/
if (fstat(mfd, &stbuf) < 0 || !S_ISCHR(stbuf.st_mode)) {
(void) close(mfd);
continue;
}
/*
* Attempt to read the configuration info on the disk.
*/
if (ioctl(mfd, DKIOCINFO, &dkinfo_tmp) < 0) {
(void) close(mfd);
continue;
}
/*
* Finished with the opened device
*/
(void) close(mfd);
/*
* If it's not the disk we're interested in, it doesn't apply.
*/
if (dkinfo.dki_ctype != dkinfo_tmp.dki_ctype ||
dkinfo.dki_cnum != dkinfo_tmp.dki_cnum ||
dkinfo.dki_unit != dkinfo_tmp.dki_unit) {
continue;
}
/*
* It's a mount on the disk we're checking. If we are
* checking whole disk, then we found trouble. We can
* quit searching.
*/
if (flag) {
if (my_umount(mp->mnt_mountp) < 0) {
return (-1);
}
found = 1;
} else {
return (-1);
}
}
return (found);
}
int32_t
my_open(char *device_name, int32_t flags)
{
char *real_name;
char *nm;
char tmp_path_name[PATH_MAX];
struct stat stat_buf;
int32_t ret_val;
int32_t fd;
int32_t have_read_priv = 0;
DIR *dirp;
struct dirent *dp;
DPRINTF1("Device name %s\n", device_name);
if ((nm = volmgt_symname(device_name)) == NULL) {
DPRINTF("path not managed\n");
real_name = media_findname(device_name);
} else {
DPRINTF1("path managed as %s\n", nm);
real_name = media_findname(nm);
DPRINTF1("real name %s\n", real_name);
}
if (real_name == NULL)
return (-1);
(void) strcpy(tmp_path_name, real_name);
ret_val = stat(tmp_path_name, &stat_buf);
if (ret_val < 0) {
PERROR("Could not stat");
return (-1);
}
if (S_ISDIR(stat_buf.st_mode)) {
/*
* Open the directory and look for the
* first non '.' entry.
* Since raw_read and raw_writes are used, we don't
* need to access the backup slice.
* For PCMCIA Memory cards, raw_read and raw_writes are
* not supported, but that is not a problem as, only slice2
* is allowed on PCMCIA memory cards.
*/
/*
* First make sure we are operating with a /vol/....
* Otherwise it can dangerous,
* e.g. rmformat -s /dev/rdsk
* We should not look into the directory contents here.
*/
if (strncmp(tmp_path_name, "/vol/dev/", strlen("/vol/dev/"))
!= 0) {
(void) fprintf(stderr, gettext("The specified device \
is not a raw device.\n"));
exit(1);
}
/* Turn on the privileges. */
(void) __priv_bracket(PRIV_ON);
dirp = opendir(tmp_path_name);
/* Turn off the privileges. */
(void) __priv_bracket(PRIV_OFF);
if (dirp == NULL) {
return (-1);
}
/* Turn on the privileges. */
(void) __priv_bracket(PRIV_ON);
have_read_priv = 1;
while ((dp = readdir(dirp)) != NULL) {
/* Turn off the privileges. */
(void) __priv_bracket(PRIV_OFF);
have_read_priv = 0;
DPRINTF1("Found %s\n", dp->d_name);
if ((strcmp(dp->d_name, ".") != 0) &&
(strcmp(dp->d_name, "..") != 0)) {
(void) snprintf(tmp_path_name, PATH_MAX,
"%s/%s", tmp_path_name, dp->d_name);
DPRINTF1("tmp_pathname is %s\n", tmp_path_name);
break;
}
/* Turn on the privileges. */
(void) __priv_bracket(PRIV_ON);
have_read_priv = 1;
}
if (have_read_priv) {
/* drop the file_dac_read privilege */
(void) __priv_bracket(PRIV_OFF);
have_read_priv = 0;
}
(void) closedir(dirp);
}
if (volmgt_running() == 0) {
/* Turn on privileges. */
(void) __priv_bracket(PRIV_ON);
have_read_priv = 1;
}
fd = open(tmp_path_name, flags);
if (have_read_priv) {
/* Turn off privileges. */
(void) __priv_bracket(PRIV_OFF);
have_read_priv = 0;
}
DPRINTF1("path opened %s\n", tmp_path_name);
return (fd);
}
uint64_t
my_atoll(char *ptr)
{
char *tmp_ptr = ptr;
int32_t base = 10;
uint64_t ret_val;
while (*tmp_ptr) {
if (isdigit(*tmp_ptr))
tmp_ptr++;
else {
base = 16;
break;
}
}
tmp_ptr = ptr;
if (base == 16) {
if (strlen(tmp_ptr) < 3) {
return (-1);
}
if (*tmp_ptr++ != '0' || (*tmp_ptr != 'x' && *tmp_ptr != 'X')) {
return (-1);
}
tmp_ptr++;
while (*tmp_ptr) {
if (isxdigit(*tmp_ptr))
tmp_ptr++;
else {
return (-1);
}
}
}
ret_val = (uint64_t)strtoull(ptr, (char **)NULL, 0);
return (ret_val);
}
int32_t
write_sunos_label(int32_t fd, int32_t media_type)
{
struct extvtoc v_toc;
int32_t ret;
(void) memset(&v_toc, 0, sizeof (struct extvtoc));
/* Initialize the vtoc information */
if (media_type == SM_FLOPPY) {
struct fd_char fdchar;
int32_t mult_factor;
if (ioctl(fd, FDIOGCHAR, &fdchar) < 0) {
PERROR("FDIOGCHAR failed");
return (-1);
}
/* SPARC and x86 fd drivers use fdc_medium differently */
#if defined(__sparc)
mult_factor = (fdchar.fdc_medium) ? 2 : 1;
#elif defined(__x86)
mult_factor = (fdchar.fdc_medium == 5) ? 2 : 1;
#else
#error No Platform defined
#endif /* defined(__sparc) */
/* initialize the vtoc structure */
v_toc.v_nparts = 3;
v_toc.v_part[0].p_start = 0;
v_toc.v_part[0].p_size = (fdchar.fdc_ncyl - 1) * 2 *
fdchar.fdc_secptrack * mult_factor;
v_toc.v_part[1].p_start = (fdchar.fdc_ncyl - 1) * 2 *
fdchar.fdc_secptrack * mult_factor;
v_toc.v_part[1].p_size = 2 * fdchar.fdc_secptrack * mult_factor;
v_toc.v_part[2].p_start = 0;
v_toc.v_part[2].p_size = fdchar.fdc_ncyl * 2 *
fdchar.fdc_secptrack * mult_factor;
} else if (media_type == SM_SCSI_FLOPPY) {
smedia_handle_t handle;
smmedium_prop_t med_info;
struct dk_geom dkgeom;
/*
* call smedia_get_medium_property to get the
* correct media information, since DKIOCGMEDIAINFO
* may fail for unformatted media.
*/
handle = smedia_get_handle(fd);
if (handle == NULL) {
(void) fprintf(stderr,
gettext("Failed to get libsmedia handle.\n"));
(void) close(fd);
return (-1);
}
if (smedia_get_medium_property(handle, &med_info) < 0) {
(void) fprintf(stderr,
gettext("Get medium property failed \n"));
(void) smedia_release_handle(handle);
(void) close(fd);
return (-1);
}
/* Fill in our own geometry information */
dkgeom.dkg_pcyl = med_info.sm_pcyl;
dkgeom.dkg_ncyl = med_info.sm_pcyl;
dkgeom.dkg_nhead = med_info.sm_nhead;
dkgeom.dkg_nsect = med_info.sm_nsect;
dkgeom.dkg_acyl = 0;
dkgeom.dkg_bcyl = 0;
dkgeom.dkg_intrlv = 0;
dkgeom.dkg_apc = 0;
/*
* Try to set vtoc, if not successful we will
* continue to use the faked geometry information.
*/
(void) ioctl(fd, DKIOCSGEOM, &dkgeom);
(void) smedia_release_handle(handle);
/* we want the same partitioning as used for normal floppies */
v_toc.v_part[0].p_start = 0;
v_toc.v_part[0].p_size = (diskaddr_t)(dkgeom.dkg_ncyl - 1) *
dkgeom.dkg_nhead * dkgeom.dkg_nsect;
v_toc.v_part[1].p_start = (diskaddr_t)(dkgeom.dkg_ncyl - 1) *
dkgeom.dkg_nhead * dkgeom.dkg_nsect;
v_toc.v_part[1].p_size = dkgeom.dkg_nhead * dkgeom.dkg_nsect;
v_toc.v_part[2].p_start = 0;
v_toc.v_part[2].p_size = (diskaddr_t)dkgeom.dkg_ncyl *
dkgeom.dkg_nhead * dkgeom.dkg_nsect;
/* both write_vtoc and DKIOCSVTOC require V_NUMPAR partitions */
v_toc.v_nparts = V_NUMPAR;
} else {
return (0);
}
v_toc.v_sanity = VTOC_SANE;
v_toc.v_version = V_VERSION;
/*
* The label structure is set up for DEV_BSIZE(512 byte) blocks,
* even though a medium density diskette has 1024 byte blocks
* See dklabel.h for more details.
*/
v_toc.v_sectorsz = DEV_BSIZE;
/* let the fd driver finish constructing the label and writing it. */
/* Turn on the privileges. */
(void) __priv_bracket(PRIV_ON);
ret = write_extvtoc(fd, &v_toc);
/* Turn off the privileges. */
(void) __priv_bracket(PRIV_OFF);
if (ret < 0) {
PERROR("Write vtoc");
DPRINTF1("Write vtoc failed errno:%d\n", errno);
return (-1);
}
return (0);
}
static void
intr_sig_handler()
{
char c;
(void) fprintf(stderr, gettext(global_intr_msg));
(void) fprintf(stderr,
gettext("\nDo you want to stop formatting?(y/n)"));
(void) fflush(stdout);
rewind(stdin);
while ((c = getchar()) == -1)
;
if (c == 'y' || c == 'Y') {
(void) fprintf(stderr, gettext("Format interrupted\n"));
exit(1);
} else if (c == 'n' || c == 'N')
return;
else {
(void) fprintf(stderr, gettext("Did not interrupt\n"));
return;
}
}
static struct sigaction act, oact;
void
trap_SIGINT()
{
act.sa_handler = intr_sig_handler;
(void) memset(&act.sa_mask, 0, sizeof (sigset_t));
act.sa_flags = SA_RESTART; /* | SA_NODEFER; */
if (sigaction(SIGINT, &act, &oact) < 0) {
DPRINTF("sigset failed\n");
return;
}
}
void
release_SIGINT()
{
if (sigaction(SIGINT, &oact, (struct sigaction *)NULL) < 0) {
DPRINTF("sigunset failed\n");
return;
}
}
int32_t
verify(smedia_handle_t handle, int32_t fd, diskaddr_t start_sector,
uint32_t nblocks, char *buf,
int32_t flag, int32_t blocksize, int32_t no_raw_rw)
{
uint64_t ret;
DPRINTF("ANALYSE MEDIA \n");
if ((flag == VERIFY_READ) && (!no_raw_rw)) {
/* Turn on the privileges. */
(void) __priv_bracket(PRIV_ON);
ret = smedia_raw_read(handle, start_sector, buf, nblocks *
blocksize);
/* Turn off the privileges. */
(void) __priv_bracket(PRIV_OFF);
if (ret != (nblocks * blocksize))
return (-1);
return (0);
} else if ((flag == VERIFY_WRITE) && (!no_raw_rw)) {
/* Turn on privileges. */
(void) __priv_bracket(PRIV_ON);
ret = smedia_raw_write(handle, start_sector, buf, nblocks *
blocksize);
/* Turn off the privileges. */
(void) __priv_bracket(PRIV_OFF);
if (ret != (blocksize * nblocks))
return (-1);
return (0);
} else if ((flag == VERIFY_READ) && (no_raw_rw)) {
ret = llseek(fd, start_sector * blocksize, SEEK_SET);
if (ret != start_sector * blocksize) {
(void) fprintf(stderr, gettext("Seek failed\n"));
return (-2);
}
/* Turn on the privileges. */
(void) __priv_bracket(PRIV_ON);
ret = read(fd, buf, nblocks * blocksize);
/* Turn off the privileges. */
(void) __priv_bracket(PRIV_OFF);
if (ret != nblocks * blocksize) {
return (-1);
}
return (0);
} else if ((flag == VERIFY_WRITE) && (no_raw_rw)) {
ret = llseek(fd, start_sector * blocksize, SEEK_SET);
if (ret != start_sector * blocksize) {
(void) fprintf(stderr, gettext("Seek failed\n"));
return (-2);
}
/* Turn on the privileges. */
(void) __priv_bracket(PRIV_ON);
ret = write(fd, buf, nblocks * blocksize);
/* Turn off the privileges. */
(void) __priv_bracket(PRIV_OFF);
if (ret != nblocks * blocksize) {
return (-1);
}
return (0);
} else {
DPRINTF("Illegal parameter to verify_analysis!\n");
return (-1);
}
}
static int
my_umount(char *mountp)
{
pid_t pid; /* forked proc's pid */
int rval; /* proc's return value */
/* create a child to unmount the path */
/* Turn on the privileges */
(void) __priv_bracket(PRIV_ON);
pid = fork();
/* Turn off the privileges. */
(void) __priv_bracket(PRIV_OFF);
if (pid < 0) {
PERROR("fork failed");
exit(0);
}
if (pid == 0) {
/* the child */
/* get rid of those nasty err messages */
DPRINTF1("call_unmount_prog: calling %s \n", mountp);
/* Turn on the priviliges. */
(void) __priv_bracket(PRIV_ON);
if (execl("/usr/sbin/umount", "/usr/sbin/umount", mountp,
NULL) < 0) {
perror("exec failed");
/* Turn off the privileges */
(void) __priv_bracket(PRIV_OFF);
exit(-1);
}
}
/* wait for the umount command to exit */
rval = 0;
if (waitpid(pid, &rval, 0) == pid) {
if (WIFEXITED(rval)) {
if (WEXITSTATUS(rval) == 0) {
DPRINTF("umount : Success\n");
return (1);
}
}
}
return (-1);
}
static int
my_volrmmount(char *real_name)
{
int pid, rval;
/* Turn on the privileges. */
(void) __priv_bracket(PRIV_ON);
pid = fork();
/* Turn off the privileges. */
(void) __priv_bracket(PRIV_OFF);
/* create a child to unmount the path */
if (pid < 0) {
PERROR("fork failed");
exit(0);
}
if (pid == 0) {
/* the child */
/* get rid of those nasty err messages */
DPRINTF1("call_unmount_prog: calling %s \n",
"/usr/bin/volrmmount");
/* Turn on the privileges. */
(void) __priv_bracket(PRIV_ON);
if (execl("/usr/bin/volrmmount", "/usr/bin/volrmmount", "-e",
real_name, NULL) < 0) {
PERROR("volrmmount exec failed");
/* Turn off the privileges */
(void) __priv_bracket(PRIV_OFF);
exit(-1);
}
} else if (waitpid(pid, &rval, 0) == pid) {
if (WIFEXITED(rval)) {
if (WEXITSTATUS(rval) == 0) {
DPRINTF("volrmmount: Success\n");
return (1);
}
}
}
return (-1);
}
int
find_device(int defer, char *tmpstr)
{
DIR *dir;
struct dirent *dirent;
char sdev[PATH_MAX], dev[PATH_MAX], *pname;
device_t *t_dev;
int removable = 0;
int device_type = 0;
int hotpluggable = 0;
struct dk_minfo mediainfo;
static int found = 0;
dir = opendir("/dev/rdsk");
if (dir == NULL)
return (-1);
total_devices_found = 0;
while ((dirent = readdir(dir)) != NULL) {
if (dirent->d_name[0] == '.') {
continue;
}
(void) snprintf(sdev, PATH_MAX, "/dev/rdsk/%s",
dirent->d_name);
#ifdef sparc
if (!strstr(sdev, "s2")) {
continue;
}
#else /* x86 */
if (vol_running) {
if (!(strstr(sdev, "s2") || strstr(sdev, "p0"))) {
continue;
}
} else {
if (!strstr(sdev, "p0")) {
continue;
}
}
#endif
if (!lookup_device(sdev, dev)) {
continue;
}
if ((t_dev = get_device(NULL, dev)) == NULL) {
continue;
}
total_devices_found++;
if ((!defer) && !found) {
char *sn, *tmpbuf;
/*
* dev_name is an optional command line input.
*/
if (dev_name) {
if (strstr(dirent->d_name, tmpstr)) {
found = 1;
} else if (!vol_running) {
continue;
}
}
/*
* volmgt_symname() returns NULL if the device
* is not managed by volmgt.
*/
sn = volmgt_symname(sdev);
if (vol_running && (sn != NULL)) {
if (strstr(sn, "dev") == NULL) {
tmpbuf = (char *)my_zalloc(PATH_MAX);
(void) strcpy(tmpbuf,
"/vol/dev/aliases/");
(void) strcat(tmpbuf, sn);
free(sn);
sn = tmpbuf;
}
if (dev_name && !found) {
if (!strstr(tmpbuf, tmpstr)) {
continue;
} else {
found = 1;
}
}
}
/*
* Get device type information for CD/DVD devices.
*/
if (is_cd(dev)) {
if (check_device(t_dev,
CHECK_DEVICE_IS_DVD_WRITABLE)) {
device_type = DK_DVDR;
} else if (check_device(t_dev,
CHECK_DEVICE_IS_DVD_READABLE)) {
device_type = DK_DVDROM;
} else if (check_device(t_dev,
CHECK_DEVICE_IS_CD_WRITABLE)) {
device_type = DK_CDR;
} else {
device_type = DK_CDROM;
}
} else {
device_type = ioctl(t_dev->d_fd,
DKIOCGMEDIAINFO, &mediainfo);
if (device_type < 0)
device_type = 0;
else
device_type = mediainfo.dki_media_type;
}
if (!ioctl(t_dev->d_fd, DKIOCREMOVABLE, &removable) &&
!ioctl(t_dev->d_fd, DKIOCHOTPLUGGABLE,
&hotpluggable)) {
if (removable || hotpluggable) {
removable_found++;
pname = get_physical_name(sdev);
if (sn) {
(void) printf(" %4d. "
"Volmgt Node: %s\n",
removable_found, sn);
(void) printf(" "
"Logical Node: %s\n", sdev);
(void) printf(" "
"Physical Node: %s\n",
pname);
} else {
(void) printf(" %4d. "
"Logical Node: %s\n",
removable_found, sdev);
(void) printf(" "
"Physical Node: %s\n",
pname);
}
(void) printf(" Connected "
"Device: %-8.8s %-16.16s "
"%-4.4s\n",
&t_dev->d_inq[8],
&t_dev->d_inq[16],
&t_dev->d_inq[32]);
(void) printf(" Device "
"Type: ");
} else
continue;
} else
continue;
switch (device_type) {
case DK_CDROM:
(void) printf("CD Reader\n");
break;
case DK_CDR:
case DK_CDRW:
(void) printf("CD Reader/Writer\n");
break;
case DK_DVDROM:
(void) printf("DVD Reader\n");
break;
case DK_DVDR:
case DK_DVDRAM:
(void) printf("DVD Reader/Writer\n");
break;
case DK_FIXED_DISK:
if (strstr((const char *)
&t_dev->d_inq[16], "FD") ||
strstr((const char *)
&t_dev->d_inq[16], "LS-120"))
(void) printf("Floppy "
"drive\n");
else
(void) printf("Removable\n");
break;
case DK_FLOPPY:
(void) printf("Floppy drive\n");
break;
case DK_ZIP:
(void) printf("Zip drive\n");
break;
case DK_JAZ:
(void) printf("Jaz drive\n");
break;
default:
(void) printf("<Unknown>\n");
DPRINTF1("\t %d\n", device_type);
break;
}
get_media_info(t_dev, sdev, pname, sn);
}
fini_device(t_dev);
}
(void) closedir(dir);
return (removable_found);
}
/*
* Returns a device_t handle for a node returned by lookup_device()
* and takes the user supplied name and stores it inside the node.
*/
static device_t *
get_device(char *user_supplied, char *node)
{
device_t *dev;
int fd;
char devnode[PATH_MAX];
int size;
/*
* we need to resolve any link paths to avoid fake files
* such as /dev/rdsk/../../export/file.
*/
size = resolvepath(node, devnode, PATH_MAX);
if ((size <= 0) || (size >= (PATH_MAX - 1)))
return (NULL);
/* resolvepath may not return a null terminated string */
devnode[size] = '\0';
/* the device node must be in /devices/ or /vol/dev/rdsk */
if ((strncmp(devnode, "/devices/", 9) != 0) &&
(strncmp(devnode, "/vol/dev/rdsk", 13) != 0))
return (NULL);
/* Turn on the privileges. */
(void) __priv_bracket(PRIV_ON);
/*
* Since we are currently running with the user euid it is
* safe to try to open the file without checking access.
*/
fd = open(devnode, O_RDONLY|O_NDELAY);
/* Turn off the privileges. */
(void) __priv_bracket(PRIV_OFF);
if (fd < 0) {
return (NULL);
}
dev = (device_t *)my_zalloc(sizeof (device_t));
dev->d_node = (char *)my_zalloc(strlen(devnode) + 1);
(void) strcpy(dev->d_node, devnode);
dev->d_fd = fd;
dev->d_inq = (uchar_t *)my_zalloc(INQUIRY_DATA_LENGTH);
/* Turn on privileges. */
(void) __priv_bracket(PRIV_ON);
if (!inquiry(fd, dev->d_inq)) {
DPRINTF1("USCSI ioctl failed %d\n",
uscsi_error);
free(dev->d_inq);
free(dev->d_node);
(void) close(dev->d_fd);
free(dev);
/* Turn off privileges. */
(void) __priv_bracket(PRIV_OFF);
return (NULL);
}
/* Turn off privileges. */
(void) __priv_bracket(PRIV_OFF);
if (user_supplied) {
dev->d_name = (char *)my_zalloc(strlen(user_supplied) + 1);
(void) strcpy(dev->d_name, user_supplied);
}
return (dev);
}
/*
* Check for device specific characteristics.
*/
int
check_device(device_t *dev, int cond)
{
uchar_t page_code[4];
/* Look at the capabilities page for this information */
if (cond & CHECK_DEVICE_IS_CD_WRITABLE) {
if (get_mode_page(dev->d_fd, 0x2a, 0, 4, page_code) &&
(page_code[3] & 1)) {
return (1);
}
}
if (cond & CHECK_DEVICE_IS_DVD_WRITABLE) {
if (get_mode_page(dev->d_fd, 0x2a, 0, 4, page_code) &&
(page_code[3] & 0x10)) {
return (1);
}
}
if (cond & CHECK_DEVICE_IS_DVD_READABLE) {
if (get_mode_page(dev->d_fd, 0x2a, 0, 4, page_code) &&
(page_code[2] & 0x8)) {
return (1);
}
}
return (0);
}
/*
* Builds an open()able device path from a user supplied node which can be
* of the * form of /dev/[r]dsk/cxtxdx[sx] or cxtxdx[sx] or volmgt-name like
* cdrom[n].
* Returns the path found in 'found' and returns 1. Otherwise returns 0.
*/
int
lookup_device(char *supplied, char *found)
{
struct stat statbuf;
int fd;
char tmpstr[PATH_MAX];
/* Turn on privileges */
(void) __priv_bracket(PRIV_ON);
/* If everything is fine and proper, no need to analyze */
if ((stat(supplied, &statbuf) == 0) && S_ISCHR(statbuf.st_mode) &&
((fd = open(supplied, O_RDONLY|O_NDELAY)) >= 0)) {
(void) close(fd);
(void) strlcpy(found, supplied, PATH_MAX);
/* Turn off privilege */
(void) __priv_bracket(PRIV_OFF);
return (1);
}
/* Turn off privileges. */
(void) __priv_bracket(PRIV_OFF);
if (strncmp(supplied, "/dev/rdsk/", 10) == 0)
return (vol_lookup(supplied, found));
if (strncmp(supplied, "/dev/dsk/", 9) == 0) {
(void) snprintf(tmpstr, PATH_MAX, "/dev/rdsk/%s",
(char *)strrchr(supplied, '/'));
if ((fd = open(tmpstr, O_RDONLY|O_NDELAY)) >= 0) {
(void) close(fd);
(void) strlcpy(found, supplied, PATH_MAX);
return (1);
}
if ((access(tmpstr, F_OK) == 0) && vol_running)
return (vol_lookup(tmpstr, found));
else
return (0);
}
if ((strncmp(supplied, "cdrom", 5) != 0) &&
(strlen(supplied) < 32)) {
(void) snprintf(tmpstr, sizeof (tmpstr), "/dev/rdsk/%s",
supplied);
if (access(tmpstr, F_OK) < 0) {
(void) strcat(tmpstr, "s2");
}
if ((fd = open(tmpstr, O_RDONLY|O_NDELAY)) >= 0) {
(void) close(fd);
(void) strlcpy(found, tmpstr, PATH_MAX);
return (1);
}
if ((access(tmpstr, F_OK) == 0) && vol_running)
return (vol_lookup(tmpstr, found));
}
return (vol_name_to_dev_node(supplied, found));
}
int
is_cd(char *node)
{
int fd;
struct dk_cinfo cinfo;
fd = open(node, O_RDONLY|O_NDELAY);
if (fd < 0)
return (0);
if (ioctl(fd, DKIOCINFO, &cinfo) < 0) {
(void) close(fd);
return (0);
}
if (cinfo.dki_ctype != DKC_CDROM)
return (0);
return (1);
}
void
print_header(void)
{
/* l10n_NOTE : Column spacing should be kept same */
(void) printf(gettext(" Node "
"Connected Device"));
/* l10n_NOTE : Column spacing should be kept same */
(void) printf(gettext(" Device type\n"));
(void) printf(
"---------------------------+---------------------------");
(void) printf("-----+----------------\n");
}
void
print_divider(void)
{
(void) printf(
"---------------------------+---------------------------");
(void) printf("-----+----------------\n");
}
static void
fini_device(device_t *dev)
{
free(dev->d_inq);
free(dev->d_node);
(void) close(dev->d_fd);
if (dev->d_name)
free(dev->d_name);
free(dev);
}
void *
my_zalloc(size_t size)
{
void *ret;
ret = malloc(size);
if (ret == NULL) {
/* Lets wait a sec. and try again */
if (errno == EAGAIN) {
(void) sleep(1);
ret = malloc(size);
}
if (ret == NULL) {
(void) err_msg("%s\n", gettext(strerror(errno)));
(void) err_msg(gettext(
"Memory allocation failure, Exiting...\n"));
exit(1);
}
}
(void) memset(ret, 0, size);
return (ret);
}
static int
vol_name_to_dev_node(char *vname, char *found)
{
struct stat statbuf;
char *p1;
int i;
if (vname == NULL)
return (0);
if (vol_running)
(void) volmgt_check(vname);
p1 = media_findname(vname);
if (p1 == NULL)
return (0);
if (stat(p1, &statbuf) < 0) {
free(p1);
return (0);
}
if (S_ISDIR(statbuf.st_mode)) {
for (i = 0; i < 16; i++) {
(void) snprintf(found, PATH_MAX, "%s/s%d", p1, i);
if (access(found, F_OK) >= 0)
break;
}
if (i == 16) {
free(p1);
return (0);
}
} else {
(void) strlcpy(found, p1, PATH_MAX);
}
free(p1);
return (1);
}
/*
* Searches for volume manager's equivalent char device for the
* supplied pathname which is of the form of /dev/rdsk/cxtxdxsx
*/
static int
vol_lookup(char *supplied, char *found)
{
char tmpstr[PATH_MAX], tmpstr1[PATH_MAX], *p;
int i, ret;
(void) strlcpy(tmpstr, supplied, PATH_MAX);
if ((p = volmgt_symname(tmpstr)) == NULL) {
if (strstr(tmpstr, "s2") != NULL) {
*((char *)(strrchr(tmpstr, 's') + 1)) = 0;
for (i = 0; i < 16; i++) {
(void) snprintf(tmpstr1, PATH_MAX, "%s%d",
tmpstr, i);
if ((p = volmgt_symname(tmpstr1)) != NULL)
break;
}
} else if (strstr(tmpstr, "p0") != NULL) {
*((char *)(strrchr(tmpstr, 'p') + 1)) = 0;
for (i = 0; i < 5; i++) {
(void) snprintf(tmpstr1, PATH_MAX, "%s%d",
tmpstr, i);
if ((p = volmgt_symname(tmpstr1)) != NULL)
break;
}
} else
return (0);
if (p == NULL)
return (0);
}
ret = vol_name_to_dev_node(p, found);
free(p);
return (ret);
}
/*PRINTFLIKE1*/
void
err_msg(char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
(void) vfprintf(stderr, fmt, ap);
va_end(ap);
}
int
inquiry(int fd, uchar_t *inq)
{
struct uscsi_cmd *scmd;
scmd = get_uscsi_cmd();
scmd->uscsi_flags = USCSI_READ|USCSI_SILENT;
scmd->uscsi_timeout = DEFAULT_SCSI_TIMEOUT;
scmd->uscsi_cdb[0] = INQUIRY_CMD;
scmd->uscsi_cdb[4] = INQUIRY_DATA_LENGTH;
scmd->uscsi_cdblen = 6;
scmd->uscsi_bufaddr = (char *)inq;
scmd->uscsi_buflen = INQUIRY_DATA_LENGTH;
if ((uscsi_error = uscsi(fd, scmd)) < 0)
return (0);
return (1);
}
struct uscsi_cmd *
get_uscsi_cmd(void)
{
(void) memset(&uscmd, 0, sizeof (uscmd));
(void) memset(ucdb, 0, 16);
uscmd.uscsi_cdb = ucdb;
return (&uscmd);
}
int
uscsi(int fd, struct uscsi_cmd *scmd)
{
int ret, global_rqsense;
int retries, max_retries = 5;
int i;
/* set up for request sense extensions */
if (!(scmd->uscsi_flags & USCSI_RQENABLE)) {
scmd->uscsi_flags |= USCSI_RQENABLE;
scmd->uscsi_rqlen = RQBUFLEN;
scmd->uscsi_rqbuf = rqbuf;
global_rqsense = 1;
} else {
global_rqsense = 0;
}
/*
* The device may be busy or slow and fail with a not ready status.
* we'll allow a limited number of retries to give the drive time
* to recover.
*/
for (retries = 0; retries < max_retries; retries++) {
scmd->uscsi_status = 0;
if (global_rqsense)
(void) memset(rqbuf, 0, RQBUFLEN);
DPRINTF("cmd:[");
for (i = 0; i < scmd->uscsi_cdblen; i++)
DPRINTF1("0x%02x ",
(uchar_t)scmd->uscsi_cdb[i]);
DPRINTF("]\n");
/*
* We need to have root privledges in order to use
* uscsi commands on the device.
*/
ret = ioctl(fd, USCSICMD, scmd);
/* maintain consistency in case of sgen */
if ((ret == 0) && (scmd->uscsi_status == 2)) {
ret = -1;
errno = EIO;
}
/* if error and extended request sense, retrieve errors */
if (global_rqsense && (ret < 0) && (scmd->uscsi_status == 2)) {
/*
* The drive is not ready to recieve commands but
* may be in the process of becoming ready.
* sleep for a short time then retry command.
* SENSE/ASC = 2/4 : not ready
* ASCQ = 0 Not Reportable.
* ASCQ = 1 Becoming ready.
*/
if ((SENSE_KEY(rqbuf) == 2) && (ASC(rqbuf) == 4) &&
((ASCQ(rqbuf) == 0) || (ASCQ(rqbuf) == 1))) {
total_retries++;
(void) sleep(3);
continue;
}
/*
* Device is not ready to transmit or a device reset
* has occurred. wait for a short period of time then
* retry the command.
*/
if ((SENSE_KEY(rqbuf) == 6) && ((ASC(rqbuf) == 0x28) ||
(ASC(rqbuf) == 0x29))) {
(void) sleep(3);
total_retries++;
continue;
}
DPRINTF3("cmd: 0x%02x ret:%i status:%02x ",
(uchar_t)scmd->uscsi_cdb[0], ret,
scmd->uscsi_status);
DPRINTF3(" sense: %02x ASC: %02x ASCQ:%02x\n",
(uchar_t)SENSE_KEY(rqbuf),
(uchar_t)ASC(rqbuf), (uchar_t)ASCQ(rqbuf));
}
/* no errors we'll return */
break;
}
/* store the error status for later debug printing */
if ((ret < 0) && (global_rqsense)) {
uscsi_status = scmd->uscsi_status;
rqstatus = scmd->uscsi_rqstatus;
rqresid = scmd->uscsi_rqresid;
}
DPRINTF1("total retries: %d\n", total_retries);
return (ret);
}
/*
* will get the mode page only i.e. will strip off the header.
*/
int
get_mode_page(int fd, int page_no, int pc, int buf_len, uchar_t *buffer)
{
int ret;
uchar_t byte2, *buf;
uint_t header_len, page_len, copy_cnt;
byte2 = (uchar_t)(((pc << 6) & 0xC0) | (page_no & 0x3f));
buf = (uchar_t *)my_zalloc(256);
/* Ask 254 bytes only to make our IDE driver happy */
ret = mode_sense(fd, byte2, 1, 254, buf);
if (ret == 0) {
free(buf);
return (0);
}
header_len = 8 + read_scsi16(&buf[6]);
page_len = buf[header_len + 1] + 2;
copy_cnt = (page_len > buf_len) ? buf_len : page_len;
(void) memcpy(buffer, &buf[header_len], copy_cnt);
free(buf);
return (1);
}
int
mode_sense(int fd, uchar_t pc, int dbd, int page_len, uchar_t *buffer)
{
struct uscsi_cmd *scmd;
scmd = get_uscsi_cmd();
scmd->uscsi_flags = USCSI_READ|USCSI_SILENT;
scmd->uscsi_buflen = page_len;
scmd->uscsi_bufaddr = (char *)buffer;
scmd->uscsi_timeout = DEFAULT_SCSI_TIMEOUT;
scmd->uscsi_cdblen = 0xa;
scmd->uscsi_cdb[0] = MODE_SENSE_10_CMD;
if (dbd) {
/* don't return any block descriptors */
scmd->uscsi_cdb[1] = 0x8;
}
/* the page code we want */
scmd->uscsi_cdb[2] = pc;
/* allocation length */
scmd->uscsi_cdb[7] = (page_len >> 8) & 0xff;
scmd->uscsi_cdb[8] = page_len & 0xff;
if ((uscsi_error = uscsi(fd, scmd)) < 0)
return (0);
return (1);
}
uint16_t
read_scsi16(void *addr)
{
uchar_t *ad = (uchar_t *)addr;
uint16_t ret;
ret = ((((uint16_t)ad[0]) << 8) | ad[1]);
return (ret);
}
/*
* Allocate space for and return a pointer to a string
* on the stack. If the string is null, create
* an empty string.
* Use destroy_data() to free when no longer used.
*/
char *
alloc_string(s)
char *s;
{
char *ns;
if (s == (char *)NULL) {
ns = (char *)my_zalloc(1);
} else {
ns = (char *)my_zalloc(strlen(s) + 1);
(void) strcpy(ns, s);
}
return (ns);
}
/*
* Follow symbolic links from the logical device name to
* the /devfs physical device name. To be complete, we
* handle the case of multiple links. This function
* either returns NULL (no links, or some other error),
* or the physical device name, alloc'ed on the heap.
*
* Note that the standard /devices prefix is stripped from
* the final pathname, if present. The trailing options
* are also removed (":c, raw").
*/
static char *
get_physical_name(char *path)
{
struct stat stbuf;
int i;
int level;
char *p;
char s[MAXPATHLEN];
char buf[MAXPATHLEN];
char dir[MAXPATHLEN];
char savedir[MAXPATHLEN];
char *result = NULL;
if (getcwd(savedir, sizeof (savedir)) == NULL) {
DPRINTF1("getcwd() failed - %s\n", strerror(errno));
return (NULL);
}
(void) strcpy(s, path);
if ((p = strrchr(s, '/')) != NULL) {
*p = 0;
}
if (s[0] == 0) {
(void) strcpy(s, "/");
}
if (chdir(s) == -1) {
DPRINTF2("cannot chdir() to %s - %s\n",
s, strerror(errno));
goto exit;
}
level = 0;
(void) strcpy(s, path);
for (;;) {
/*
* See if there's a real file out there. If not,
* we have a dangling link and we ignore it.
*/
if (stat(s, &stbuf) == -1) {
goto exit;
}
if (lstat(s, &stbuf) == -1) {
DPRINTF2("%s: lstat() failed - %s\n",
s, strerror(errno));
goto exit;
}
/*
* If the file is not a link, we're done one
* way or the other. If there were links,
* return the full pathname of the resulting
* file.
*/
if (!S_ISLNK(stbuf.st_mode)) {
if (level > 0) {
/*
* Strip trailing options from the
* physical device name
*/
if ((p = strrchr(s, ':')) != NULL) {
*p = 0;
}
/*
* Get the current directory, and
* glue the pieces together.
*/
if (getcwd(dir, sizeof (dir)) == NULL) {
DPRINTF1("getcwd() failed - %s\n",
strerror(errno));
goto exit;
}
(void) strcat(dir, "/");
(void) strcat(dir, s);
/*
* If we have the standard fixed
* /devices prefix, remove it.
*/
p = (strstr(dir, DEVFS_PREFIX) == dir) ?
dir+strlen(DEVFS_PREFIX) : dir;
result = alloc_string(p);
}
goto exit;
}
i = readlink(s, buf, sizeof (buf));
if (i == -1) {
DPRINTF2("%s: readlink() failed - %s\n",
s, strerror(errno));
goto exit;
}
level++;
buf[i] = 0;
/*
* Break up the pathname into the directory
* reference, if applicable and simple filename.
* chdir()'ing to the directory allows us to
* handle links with relative pathnames correctly.
*/
(void) strcpy(dir, buf);
if ((p = strrchr(dir, '/')) != NULL) {
*p = 0;
if (chdir(dir) == -1) {
DPRINTF2("cannot chdir() to %s - %s\n",
dir, strerror(errno));
goto exit;
}
(void) strcpy(s, p+1);
} else {
(void) strcpy(s, buf);
}
}
exit:
if (chdir(savedir) == -1) {
(void) printf("cannot chdir() to %s - %s\n",
savedir, strerror(errno));
}
return (result);
}
static void
get_media_info(device_t *t_dev, char *sdev, char *pname, char *sn)
{
struct dk_cinfo cinfo;
struct extvtoc vtocinfo;
float size;
int32_t fd;
smedia_handle_t handle;
struct dk_minfo mediainfo;
int device_type;
device_type = ioctl(t_dev->d_fd, DKIOCGMEDIAINFO, &mediainfo);
/*
* Determine bus type.
*/
if (!ioctl(t_dev->d_fd, DKIOCINFO, &cinfo)) {
if (strstr(cinfo.dki_cname, "usb") || strstr(pname, "usb")) {
(void) printf("\tBus: USB\n");
} else if (strstr(cinfo.dki_cname, "firewire") ||
strstr(pname, "firewire")) {
(void) printf("\tBus: Firewire\n");
} else if (strstr(cinfo.dki_cname, "ide") ||
strstr(pname, "ide")) {
(void) printf("\tBus: IDE\n");
} else if (strstr(cinfo.dki_cname, "scsi") ||
strstr(pname, "scsi")) {
(void) printf("\tBus: SCSI\n");
} else {
(void) printf("\tBus: <Unknown>\n");
}
} else {
(void) printf("\tBus: <Unknown>\n");
}
/*
* Calculate size of media.
*/
if (!device_type &&
(!ioctl(t_dev->d_fd, DKIOCGMEDIAINFO, &mediainfo))) {
size = (mediainfo.dki_lbsize*
mediainfo.dki_capacity)/(1024.0*1024.0);
if (size < 1000) {
(void) printf("\tSize: %.1f MB\n", size);
} else {
size = size/1000;
(void) printf("\tSize: %.1f GB\n", size);
}
} else {
(void) printf("\tSize: <Unknown>\n");
}
/*
* Print label.
*/
if (!device_type && (read_extvtoc(t_dev->d_fd, &vtocinfo) >= 0)) {
if (*vtocinfo.v_volume) {
(void) printf("\tLabel: %s\n", vtocinfo.v_volume);
} else {
(void) printf("\tLabel: <None>\n");
}
} else {
(void) printf("\tLabel: <Unknown>\n");
}
/*
* Acess permissions.
*/
if (device_type) {
(void) printf("\tAccess permissions: <Unknown>\n");
return;
}
(void) fprintf(stdout, gettext("\tAccess permissions: "));
if (sn) {
/*
* Set dev_name for process_p_flag().
*/
dev_name = sn;
fd = my_open(sn, O_RDONLY|O_NDELAY);
} else {
dev_name = sdev;
fd = my_open(sdev, O_RDONLY|O_NDELAY);
}
if (fd < 0) {
(void) printf("<Unknown>\n");
DPRINTF("Could not open device.\n");
(void) close(fd);
} else {
/* register the fd with the libsmedia */
handle = smedia_get_handle(fd);
if (handle == NULL) {
(void) printf("<Unknown>\n");
DPRINTF("Failed to get libsmedia handle.\n");
(void) close(fd);
} else {
process_p_flag(handle, fd);
}
}
/* Clear dev_name */
dev_name = NULL;
}