diff options
author | stevel@tonic-gate <none@none> | 2005-06-14 00:00:00 -0700 |
---|---|---|
committer | stevel@tonic-gate <none@none> | 2005-06-14 00:00:00 -0700 |
commit | 7c478bd95313f5f23a4c958a745db2134aa03244 (patch) | |
tree | c871e58545497667cbb4b0a4f2daf204743e1fe7 /usr/src/cmd/rmformat/rmf_misc.c | |
download | illumos-joyent-7c478bd95313f5f23a4c958a745db2134aa03244.tar.gz |
OpenSolaris Launch
Diffstat (limited to 'usr/src/cmd/rmformat/rmf_misc.c')
-rw-r--r-- | usr/src/cmd/rmformat/rmf_misc.c | 2159 |
1 files changed, 2159 insertions, 0 deletions
diff --git a/usr/src/cmd/rmformat/rmf_misc.c b/usr/src/cmd/rmformat/rmf_misc.c new file mode 100644 index 0000000000..77269cc028 --- /dev/null +++ b/usr/src/cmd/rmformat/rmf_misc.c @@ -0,0 +1,2159 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 2005 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + + +/* + * 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.h> +#include <stdarg.h> +#include "rmformat.h" +/* + * These defines are from the PCMCIA memory driver driver + * header files (pcramio.h/pcramvar.h) and they are in + * the Platform Specific (PS) train. + */ +#ifndef PCRAM_PROBESIZE +#define PCRAMIOC ('P' << 8) +#define PCRAM_PROBESIZE (PCRAMIOC|22) /* Probe memory card size */ +#endif + +/* + * 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. + */ + + /* need the file_dac_read privilege */ + (void) priv_set(PRIV_ON, PRIV_EFFECTIVE, PRIV_FILE_DAC_READ, + (char *)NULL); + + mfd = open(raw_device, O_RDWR | O_NDELAY); + + /* drop the file_dac_read privilege */ + (void) priv_set(PRIV_OFF, PRIV_EFFECTIVE, PRIV_FILE_DAC_READ, + (char *)NULL); + + 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 (stat_buf.st_mode & S_IFDIR) { + + /* + * 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); + } + + /* need the file_dac_read privilege */ + (void) priv_set(PRIV_ON, PRIV_EFFECTIVE, PRIV_FILE_DAC_READ, + (char *)NULL); + + dirp = opendir(tmp_path_name); + + /* drop the file_dac_read privilege */ + (void) priv_set(PRIV_OFF, PRIV_EFFECTIVE, PRIV_FILE_DAC_READ, + (char *)NULL); + + if (dirp == NULL) { + return (-1); + } + + /* need the file_dac_read privilege */ + (void) priv_set(PRIV_ON, PRIV_EFFECTIVE, PRIV_FILE_DAC_READ, + (char *)NULL); + have_read_priv = 1; + + while ((dp = readdir(dirp)) != NULL) { + + /* drop the file_dac_read privilege */ + (void) priv_set(PRIV_OFF, PRIV_EFFECTIVE, + PRIV_FILE_DAC_READ, (char *)NULL); + 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; + } + + /* need the file_dac_read privilege */ + (void) priv_set(PRIV_ON, PRIV_EFFECTIVE, + PRIV_FILE_DAC_READ, (char *)NULL); + have_read_priv = 1; + } + + if (have_read_priv) { + /* drop the file_dac_read privilege */ + (void) priv_set(PRIV_OFF, PRIV_EFFECTIVE, + PRIV_FILE_DAC_READ, (char *)NULL); + have_read_priv = 0; + } + + (void) closedir(dirp); + } + + + if (volmgt_running() == 0) { + /* need the file_dac_read privilege */ + (void) priv_set(PRIV_ON, PRIV_EFFECTIVE, PRIV_FILE_DAC_READ, + (char *)NULL); + have_read_priv = 1; + } + + fd = open(tmp_path_name, flags); + + if (have_read_priv) { + /* drop the file_dac_read privilege */ + (void) priv_set(PRIV_OFF, PRIV_EFFECTIVE, PRIV_FILE_DAC_READ, + (char *)NULL); + have_read_priv = 0; + } + + DPRINTF1("path opened %s\n", tmp_path_name); + + return (fd); +} + +int64_t +my_atoll(char *ptr) +{ + char *tmp_ptr = ptr; + int32_t base = 10; + int64_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 = strtoll(ptr, (char **)NULL, 0); + return (ret_val); +} + +int32_t +write_sunos_label(int32_t fd, int32_t media_type) +{ + + struct vtoc v_toc; + int32_t ret; + + (void) memset(&v_toc, 0, sizeof (struct vtoc)); + + /* 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_PCMCIA_MEM) { + + static struct dk_geom dkg; + + /* Get card cyl/head/secptrack info */ + if (ioctl(fd, DKIOCGGEOM, &dkg) < 0) { + /* + * Card doesn't have a CIS. So, ask driver + * to probe card size info + */ + if (ioctl(fd, PCRAM_PROBESIZE, &dkg) < 0) { + (void) fprintf(stderr, + gettext( + "Could not get card size information\n")); + (void) close(fd); + exit(3); + } + } + + + v_toc.v_part[2].p_start = 0; + v_toc.v_part[2].p_size = dkg.dkg_ncyl * dkg.dkg_nhead * + dkg.dkg_nsect; + + /* v_nparts was 1 in fdformat. But write vtoc failes */ + v_toc.v_nparts = 3; + + } 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 = (dkgeom.dkg_ncyl - 1) * + dkgeom.dkg_nhead * dkgeom.dkg_nsect; + + v_toc.v_part[1].p_start = (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 = 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. */ + + + /* need the file_dac_write privilege */ + (void) priv_set(PRIV_ON, PRIV_EFFECTIVE, PRIV_FILE_DAC_WRITE, + (char *)NULL); + + ret = write_vtoc(fd, &v_toc); + + /* drop the file_dac_write privilege */ + (void) priv_set(PRIV_OFF, PRIV_EFFECTIVE, PRIV_FILE_DAC_WRITE, + (char *)NULL); + + 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, uint32_t start_sector, + uint32_t nblocks, char *buf, + int32_t flag, int32_t blocksize, int32_t no_raw_rw) +{ + int32_t ret; + + DPRINTF("ANALYSE MEDIA \n"); + + + if ((flag == VERIFY_READ) && (!no_raw_rw)) { + + /* need the file_dac_read privilege */ + (void) priv_set(PRIV_ON, PRIV_EFFECTIVE, PRIV_FILE_DAC_READ, + (char *)NULL); + + ret = smedia_raw_read(handle, start_sector, buf, nblocks * + blocksize); + + /* drop the file_dac_read privilege */ + (void) priv_set(PRIV_OFF, PRIV_EFFECTIVE, PRIV_FILE_DAC_READ, + (char *)NULL); + + if ((ret < 0) || (ret != (nblocks * blocksize))) + return (-1); + return (0); + + } else if ((flag == VERIFY_WRITE) && (!no_raw_rw)) { + + /* need the file_dac_write privilege */ + (void) priv_set(PRIV_ON, PRIV_EFFECTIVE, PRIV_FILE_DAC_WRITE, + (char *)NULL); + + ret = smedia_raw_write(handle, start_sector, buf, nblocks * + blocksize); + + /* drop the file_dac_write privilege */ + (void) priv_set(PRIV_OFF, PRIV_EFFECTIVE, PRIV_FILE_DAC_WRITE, + (char *)NULL); + + if ((ret < 0) || (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); + } + + /* need the file_dac_read privilege */ + (void) priv_set(PRIV_ON, PRIV_EFFECTIVE, PRIV_FILE_DAC_READ, + (char *)NULL); + + ret = read(fd, buf, nblocks * blocksize); + + /* drop the file_dac_read privilege */ + (void) priv_set(PRIV_OFF, PRIV_EFFECTIVE, PRIV_FILE_DAC_READ, + (char *)NULL); + + 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); + } + + /* need the file_dac_write privilege */ + (void) priv_set(PRIV_ON, PRIV_EFFECTIVE, PRIV_FILE_DAC_WRITE, + (char *)NULL); + + ret = write(fd, buf, nblocks * blocksize); + + /* drop the file_dac_write privilege */ + (void) priv_set(PRIV_OFF, PRIV_EFFECTIVE, PRIV_FILE_DAC_WRITE, + (char *)NULL); + + 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 */ + + /* need the proc_fork privilege */ + (void) priv_set(PRIV_ON, PRIV_EFFECTIVE, PRIV_PROC_FORK, (char *)NULL); + + pid = fork(); + + /* drop the proc_fork privilege */ + (void) priv_set(PRIV_OFF, PRIV_EFFECTIVE, PRIV_PROC_FORK, (char *)NULL); + + 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); + + /* need the proc_exec privilege */ + (void) priv_set(PRIV_ON, PRIV_EFFECTIVE, PRIV_PROC_EXEC, + (char *)NULL); + + /* umount needs the sys_mount privilege */ + (void) priv_set(PRIV_ON, PRIV_INHERITABLE, PRIV_SYS_MOUNT, + (char *)NULL); + + /* Become root again for the exec */ + if (seteuid(0) < 0) { + PERROR("Can't set effective user id to root"); + } + if (execl("/usr/sbin/umount", "/usr/sbin/umount", mountp, + NULL) < 0) { + perror("exec failed"); + 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; + + /* need the proc_fork privilege */ + (void) priv_set(PRIV_ON, PRIV_EFFECTIVE, PRIV_PROC_FORK, (char *)NULL); + + pid = fork(); + + /* drop the proc_fork privilege */ + (void) priv_set(PRIV_OFF, PRIV_EFFECTIVE, PRIV_PROC_FORK, (char *)NULL); + + /* 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"); + + /* need the proc_exec privilege */ + (void) priv_set(PRIV_ON, PRIV_EFFECTIVE, PRIV_PROC_EXEC, + (char *)NULL); + + /* volrmmount needs the sys_mount privilege */ + (void) priv_set(PRIV_ON, PRIV_INHERITABLE, PRIV_SYS_MOUNT, + (char *)NULL); + + /* Become root again for the exec */ + if (seteuid(0) < 0) { + PERROR("Can't set effective user id to root"); + } + if (execl("/usr/bin/volrmmount", "/usr/bin/volrmmount", "-e", + real_name, NULL) < 0) { + PERROR("volrmmount exec failed"); + 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, device_type; + 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)) { + if (removable) { + 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); + + /* need the file_dac_read privilege */ + (void) priv_set(PRIV_ON, PRIV_EFFECTIVE, PRIV_FILE_DAC_READ, + (char *)NULL); + + /* + * 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); + + /* drop the file_dac_read privilege */ + (void) priv_set(PRIV_OFF, PRIV_EFFECTIVE, PRIV_FILE_DAC_READ, + (char *)NULL); + + 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); + + 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); + return (NULL); + } + + 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]; + + /* need the file_dac_read privilege */ + (void) priv_set(PRIV_ON, PRIV_EFFECTIVE, PRIV_FILE_DAC_READ, + (char *)NULL); + + /* If everything is fine and proper, no need to analyze */ + if ((stat(supplied, &statbuf) == 0) && (statbuf.st_mode & S_IFCHR) && + ((fd = open(supplied, O_RDONLY|O_NDELAY)) >= 0)) { + (void) close(fd); + (void) strlcpy(found, supplied, PATH_MAX); + /* drop the file_dac_read privilege */ + (void) priv_set(PRIV_OFF, PRIV_EFFECTIVE, PRIV_FILE_DAC_READ, + (char *)NULL); + return (1); + } + + /* drop the file_dac_read privilege */ + (void) priv_set(PRIV_OFF, PRIV_EFFECTIVE, PRIV_FILE_DAC_READ, + (char *)NULL); + + 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 (statbuf.st_mode & S_IFDIR) { + 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 vtoc 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 && (!ioctl(t_dev->d_fd, DKIOCGVTOC, &vtocinfo))) { + 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(stderr, 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; + } + } +} |