# # 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. # # Copyright 2015 Nexenta Systems, Inc. All rights reserved. # Copyright (c) 2018, Joyent, Inc. PROG= format COBJS= add_definition.o analyze.o checkdev.o ctlr_scsi.o \ defect.o init_menus.o io.o label.o main.o \ menu.o menu_analyze.o menu_cache.o menu_command.o menu_defect.o \ menu_partition.o misc.o modify_partition.o partition.o \ prompts.o startup.o menu_scsi.o auto_sense.o disk_generic.o \ menu_developer.o menu_fdisk.o OBJS_sparc= init_csparc.o ctlr_ata.o OBJS_i386= init_ci386.o ctlr_ata.o ix_altsctr.o OBJS_= non-existent OBJS= $(OBJS_$(MACH)) $(COBJS) DATA= format.dat include ../Makefile.cmd CERRWARN += $(CNOWARN_UNINIT) CERRWARN += -Wno-parentheses CERRWARN += -Wno-clobbered # Hammerhead: Suppress pointer/int cast and format warnings in legacy format code CERRWARN += -Wno-pointer-to-int-cast CERRWARN += -Wno-incompatible-pointer-types CERRWARN += -Wno-format CERRWARN += -Wno-return-type # not linted SMATCH=off ROOTSYMLINK= $(ROOTETCPROG) ROOTETCDATA= $(ROOTETC)/$(DATA) $(ROOTETCDATA) : FILEMODE = 0644 LDLIBS_i386= -lfdisk LDLIBS_sparc= LDLIBS += -ladm -lefi -ldiskmgt -lnvpair -ldevid -ldevinfo $(LDLIBS_$(MACH)) # Hammerhead: Define i386 for amd64 builds to satisfy platform checks in legacy code # The code checks "defined(i386)" for x86-specific code paths that also apply to amd64 CPPFLAGS += -D_EXTVTOC -Di386 .KEEP_STATE: all: $(PROG) $(DATA) .PARALLEL: $(OBJS) $(PROG): $(OBJS) $(LINK.c) -o $(PROG) $(OBJS) $(LDLIBS) $(POST_PROCESS) # # Install format as /usr/sbin/format, with link from /etc/format # install: $(ROOTUSRSBINPROG) $(ROOTSYMLINK) $(ROOTETCDATA) $(ROOTSYMLINK): $(RM) $@; $(SYMLINK) ../usr/sbin/$(PROG) $@ clean: $(RM) $(OBJS) include ../Makefile.targ /* * 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. */ /* * This file contains the code to add new disk_type and partition * definitions to a format data file. */ #include "global.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "add_definition.h" #include "misc.h" #include "partition.h" #include "menu_command.h" #include "startup.h" extern struct ctlr_type ctlr_types[]; extern int nctypes; extern int errno; /* Function prototypes */ #ifdef __STDC__ static void add_disktype(FILE *fd, struct disk_info *disk_info); static void add_partition(FILE *fd, struct disk_info *, struct partition_info *); static int add_entry(int col, FILE *fd, char *format, ...); #else /* __STDC__ */ static void add_disktype(); static void add_partition(); static int add_entry(); #endif /* __STDC__ */ /* * Add new definitions for the current disk/partition to a format data file. */ int add_definition(void) { FILE *fd; char *filename; time_t clock; char *prompt; union { int xfoo; char deflt_str[MAXPATHLEN]; } x; /* * There must be a current disk and partition table */ if (cur_disk == NULL) { err_print("No Current Disk.\n"); return (0); } if (cur_dtype == NULL) { err_print("Current disk type is not set.\n"); return (-1); } if (cur_parts == NULL) { err_print("Current partition is not set.\n"); return (-1); } /* * If neither the disk definition nor the partition * information has been changed, there's nothing to save. */ if (cur_dtype->dtype_filename != NULL && cur_parts->pinfo_filename != NULL) { err_print("\ Neither the disk type nor the partitioning has been changed.\n"); return (-1); } /* * If saving the partition, and it's unnamed, the user should name * it first. */ if (cur_parts->pinfo_name == NULL) { assert(cur_parts->pinfo_filename == NULL); err_print("Please name this partition type before saving it\n"); return (-1); } /* * Let the user know what we're doing */ if (cur_dtype->dtype_filename == NULL && cur_parts->pinfo_filename == NULL) { fmt_print("Saving new disk and partition definitions\n"); } else if (cur_dtype->dtype_filename == NULL) { fmt_print("Saving new disk definition\n"); } else { assert(cur_parts->pinfo_filename == NULL); fmt_print("Saving new partition definition\n"); } /* * Ask for the file to which to append the new definitions */ prompt = "Enter file name"; (void) strcpy(x.deflt_str, "./format.dat"); filename = (char *)(uintptr_t)input(FIO_OSTR, prompt, ':', NULL, &x.xfoo, DATA_INPUT); assert(filename != NULL); /* * Open the file in append mode, or create it, if necessary */ if ((fd = fopen(filename, "a")) == NULL) { err_print("Cannot open `%s' - %s\n", filename, strerror(errno)); destroy_data(filename); return (-1); } /* * Write a header for the new definitions */ if ((cur_dtype->dtype_filename == NULL) && (cur_parts->pinfo_filename == NULL)) { (void) fprintf(fd, "#\n# New disk/partition type "); } else if (cur_dtype->dtype_filename == NULL) { (void) fprintf(fd, "#\n# New disk type "); } else { (void) fprintf(fd, "#\n# New partition type "); } (void) time(&clock); (void) fprintf(fd, " saved on %s#\n", ctime(&clock)); /* * Save the new definitions */ if (cur_dtype->dtype_filename == NULL) { add_disktype(fd, cur_disk); } if (cur_parts->pinfo_filename == NULL) { add_partition(fd, cur_disk, cur_parts); } /* * We're finished. Clean up */ (void) fclose(fd); destroy_data(filename); return (0); } /* * Add a disk_type definition to the file fd */ static void add_disktype(FILE *fd, struct disk_info *disk_info) { int col; struct disk_type *disk_type; disk_type = disk_info->disk_type; (void) fprintf(fd, "disk_type = \"%s\" \\\n", disk_type->dtype_asciilabel); col = add_entry(0, fd, " : ctlr = %s", ((disk_info->disk_ctlr)->ctlr_ctype)->ctype_name); col = add_entry(col, fd, " : ncyl = %d", disk_type->dtype_ncyl); col = add_entry(col, fd, " : acyl = %d", disk_type->dtype_acyl); col = add_entry(col, fd, " : pcyl = %d", disk_type->dtype_pcyl); col = add_entry(col, fd, " : nhead = %d", disk_type->dtype_nhead); if (disk_type->dtype_options & SUP_PHEAD) { col = add_entry(col, fd, " : phead = %d", disk_type->dtype_phead); } col = add_entry(col, fd, " : nsect = %d", disk_type->dtype_nsect); if (disk_type->dtype_options & SUP_PSECT) { col = add_entry(col, fd, " : psect = %d", disk_type->dtype_psect); } if (disk_type->dtype_options & SUP_BPT) { col = add_entry(col, fd, " : bpt = %d", disk_type->dtype_bpt); } col = add_entry(col, fd, " : rpm = %d", disk_type->dtype_rpm); if (disk_type->dtype_options & SUP_FMTTIME) { col = add_entry(col, fd, " : fmt_time = %d", disk_type->dtype_fmt_time); } if (disk_type->dtype_options & SUP_CYLSKEW) { col = add_entry(col, fd, " : cyl_skew = %d", disk_type->dtype_cyl_skew); } if (disk_type->dtype_options & SUP_TRKSKEW) { col = add_entry(col, fd, " : trk_skew = %d", disk_type->dtype_trk_skew); } if (disk_type->dtype_options & SUP_TRKS_ZONE) { col = add_entry(col, fd, " : trks_zone = %d", disk_type->dtype_trks_zone); } if (disk_type->dtype_options & SUP_ATRKS) { col = add_entry(col, fd, " : atrks = %d", disk_type->dtype_atrks); } if (disk_type->dtype_options & SUP_ASECT) { col = add_entry(col, fd, " : asect = %d", disk_type->dtype_asect); } if (disk_type->dtype_options & SUP_CACHE) { col = add_entry(col, fd, " : cache = %d", disk_type->dtype_cache); } if (disk_type->dtype_options & SUP_PREFETCH) { col = add_entry(col, fd, " : prefetch = %d", disk_type->dtype_threshold); } if (disk_type->dtype_options & SUP_CACHE_MIN) { col = add_entry(col, fd, " : min_prefetch = %d", disk_type->dtype_prefetch_min); } if (disk_type->dtype_options & SUP_CACHE_MAX) { col = add_entry(col, fd, " : max_prefetch = %d", disk_type->dtype_prefetch_max); } if (disk_type->dtype_options & SUP_BPS) { col = add_entry(col, fd, " : bps = %d", disk_type->dtype_bps); } if (disk_type->dtype_options & SUP_DRTYPE) { col = add_entry(col, fd, " : drive_type = %d", disk_type->dtype_dr_type); } /* * Terminate the last line, and print one blank line */ (void) fprintf(fd, col == 0 ? "\n" : "\n\n"); } /* * Once we exceed this length, wrap to a new line */ #define MAX_COLUMNS 50 /* * Add a partition definition to the file fd */ static void add_partition(FILE *fd, struct disk_info *disk_info, struct partition_info *part) { int col; int i; struct disk_type *disk_type; struct dk_map32 *pp; char *s; #if defined(_SUNOS_VTOC_8) struct dk_map2 *pv; #elif defined(_SUNOS_VTOC_16) struct dkl_partition *pv; #else #error No VTOC format defined. #endif /* defined (_SUNOS_VTOC_8) */ struct dk_map2 *dv; disk_type = disk_info->disk_type; (void) fprintf(fd, "partition = \"%s\" \\\n", part->pinfo_name); (void) fprintf(fd, "\t : disk = \"%s\" : ctlr = %s \\\n", disk_type->dtype_asciilabel, ((disk_info->disk_ctlr)->ctlr_ctype)->ctype_name); /* * Print the specifications for each useful partition */ col = 0; pp = part->pinfo_map; pv = part->vtoc.v_part; dv = default_vtoc_map; for (i = 0; i < NDKMAP; i++, pp++, pv++, dv++) { if (pp->dkl_nblk != 0) { col = add_entry(col, fd, " : %c = ", i + PARTITION_BASE); if (pv->p_tag != dv->p_tag || pv->p_flag != dv->p_flag) { s = find_string(ptag_choices, (int)pv->p_tag); if (s != NULL) { col = add_entry(col, fd, " %s,", s); } s = find_string(pflag_choices, (int)pv->p_flag); if (s != NULL) { col = add_entry(col, fd, " %s,", s); } } col = add_entry(col, fd, " %d, %d", pp->dkl_cylno, pp->dkl_nblk); } } /* * Terminate the last line, and print one blank line */ (void) fprintf(fd, col == 0 ? "\n" : "\n\n"); } /* * Add an entry to the file fd. col is the current starting column. * Return the resulting new column position. */ /*PRINTFLIKE3*/ static int add_entry(int col, FILE *fd, char *format, ...) { va_list ap; va_start(ap, format); if (col > MAX_COLUMNS) { (void) fprintf(fd, " \\\n"); col = 0; } if (col == 0) { col += fprintf(fd, "\t"); } col += vfprintf(fd, format, ap); va_end(ap); return (col); } /* * 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 (c) 1991-2001 by Sun Microsystems, Inc. */ #ifndef _ADD_DEFINITION_H #define _ADD_DEFINITION_H #ifdef __cplusplus extern "C" { #endif /* * Prototypes for ANSI C compilers */ int add_definition(void); #ifdef __cplusplus } #endif #endif /* _ADD_DEFINITION_H */ /* * 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. */ /* * This file contains routines to analyze the surface of a disk. */ #include "global.h" #include "analyze.h" #include #include #include "misc.h" #include "defect.h" #include "label.h" #include "param.h" #include "checkdev.h" /* * These global variables control the surface analysis process. They * are set from a command in the defect menu. */ int scan_entire = 1; /* scan whole disk flag */ diskaddr_t scan_lower = 0; /* lower bound */ diskaddr_t scan_upper = 0; /* upper bound */ int scan_correct = 1; /* correct errors flag */ int scan_stop = 0; /* stop after error flag */ int scan_loop = 0; /* loop forever flag */ int scan_passes = 2; /* number of passes */ int scan_random = 0; /* random patterns flag */ uint_t scan_size = 0; /* sectors/scan operation */ int scan_auto = 1; /* scan after format flag */ int scan_restore_defects = 1; /* restore defect list after writing */ int scan_restore_label = 1; /* restore label after writing */ /* * These are summary variables to print out info after analysis. * Values less than 0 imply they are invalid. */ offset_t scan_cur_block = -1; /* current block */ int64_t scan_blocks_fixed = -1; /* # blocks repaired */ /* * This variable is used to tell whether the most recent surface * analysis error was caused by a media defect or some other problem. */ int media_error; /* error was caused by defect */ int disk_error; /* disk errors during analysis */ /* * These are the data patterns used if random patterns are not chosen. * They are designed to show pattern dependent errors. */ static unsigned int scan_patterns[] = { 0xc6dec6de, 0x6db6db6d, 0x00000000, 0xffffffff, 0xaaaaaaaa, }; #define NPATTERNS 5 /* number of predefined patterns */ /* * These are the data patterns from the SunFed requirements document. */ static unsigned int purge_patterns[] = { /* patterns to be written */ 0xaaaaaaaa, /* 10101010... */ 0x55555555, /* 01010101... == UUUU... */ 0xaaaaaaaa, /* 10101010... */ 0xaaaaaaaa, /* 10101010... */ }; static unsigned int alpha_pattern = 0x40404040; /* 10000000... == @@@@... */ static int scan_repair(diskaddr_t bn, int mode); static int analyze_blocks(int flags, diskaddr_t blkno, uint_t blkcnt, unsigned data, int init, int driver_flags, int *xfercntp); static int handle_error_conditions(void); static int verify_blocks(int flags, diskaddr_t blkno, uint_t blkcnt, unsigned data, int driver_flags, int *xfercntp); /* * This routine performs a surface analysis based upon the global * parameters. It is called from several commands in the defect menu, * and from the format command in the command menu (if post-format * analysis is enable). */ int do_scan(int flags, int mode) { diskaddr_t start, end, curnt; int pass, needinit, data; uint_t size; int status, founderr, i, j; int error = 0; int pattern = 0; int xfercnt; /* * Check to be sure we aren't correcting without a defect list * if the controller can correct the defect. */ if (scan_correct && !EMBEDDED_SCSI && (cur_ops->op_repair != NULL) && (cur_list.list == NULL)) { err_print("Current Defect List must be initialized "); err_print("to do automatic repair.\n"); return (-1); } /* * Define the bounds of the scan. */ if (scan_entire) { start = 0; if (cur_label == L_TYPE_SOLARIS) { if (cur_ctype->ctype_flags & CF_SCSI) end = datasects() - 1; else end = physsects() - 1; } else if (cur_label == L_TYPE_EFI) { end = cur_parts->etoc->efi_last_lba; } } else { start = scan_lower; end = scan_upper; } /* * Make sure the user knows if we are scanning over a mounted * partition. */ if ((flags & (SCAN_PATTERN | SCAN_WRITE)) && (checkmount(start, end))) { err_print("Cannot do analysis on a mounted partition.\n"); return (-1); } /* * Make sure the user knows if we are scanning over a * partition being used for swapping. */ if ((flags & (SCAN_PATTERN | SCAN_WRITE)) && (checkswap(start, end))) { err_print("Cannot do analysis on a partition \ which is currently being used for swapping.\n"); return (-1); } /* * Check to see if any partitions used for svm, vxvm, ZFS zpool * or live upgrade are on the disk. */ if ((flags & (SCAN_PATTERN | SCAN_WRITE)) && (checkdevinuse(cur_disk->disk_name, (diskaddr_t)-1, (diskaddr_t)-1, 0, 0))) { err_print("Cannot do analysis on a partition " "while it in use as described above.\n"); return (-1); } /* * If we are scanning destructively over certain sectors, * we mark the defect list and/or label dirty so it will get rewritten. */ if (flags & (SCAN_PATTERN | SCAN_WRITE)) { if (cur_label == L_TYPE_SOLARIS) { if (start < (diskaddr_t)totalsects() && end >= (diskaddr_t)datasects()) { if (!EMBEDDED_SCSI) { cur_list.flags |= LIST_DIRTY; } if (cur_disk->disk_flags & DSK_LABEL) cur_flags |= LABEL_DIRTY; } } if (start == 0) { if (cur_disk->disk_flags & DSK_LABEL) cur_flags |= LABEL_DIRTY; } } /* * Initialize the summary info on sectors repaired. */ scan_blocks_fixed = 0; /* * Loop through the passes of the scan. If required, loop forever. */ for (pass = 0; pass < scan_passes || scan_loop; pass++) { /* * Determine the data pattern to use if pattern testing * is to be done. */ if (flags & SCAN_PATTERN) { if (scan_random) data = (int)mrand48(); else data = scan_patterns[pass % NPPATTERNS]; if (flags & SCAN_PURGE) { flags &= ~(SCAN_PURGE_READ_PASS | SCAN_PURGE_ALPHA_PASS); switch (pattern % (NPPATTERNS + 1)) { case NPPATTERNS: pattern = 0; if (!error) { fmt_print( "\nThe last %d passes were successful, running alpha pattern pass", NPPATTERNS); flags |= SCAN_PURGE_ALPHA_PASS; data = alpha_pattern; } else { data = purge_patterns[pattern]; pattern++; }; break; case READPATTERN: flags |= SCAN_PURGE_READ_PASS; /* FALLTHROUGH */ default: data = purge_patterns[pattern]; pattern++; break; } } fmt_print("\n pass %d", pass); fmt_print(" - pattern = 0x%x", data); } else fmt_print("\n pass %d", pass); fmt_print("\n"); /* * Mark the pattern buffer as corrupt, since it * hasn't been initialized. */ needinit = 1; /* * Print the first block number to the log file if * logging is on so there is some record of what * analysis was performed. */ if (log_file) { pr_dblock(log_print, start); log_print("\n"); } /* * Loop through this pass, each time analyzing an amount * specified by the global parameters. */ xfercnt = 0; for (curnt = start; curnt <= end; curnt += size) { if ((end - curnt) < scan_size) size = end - curnt + 1; else size = scan_size; /* * Print out where we are, so we don't look dead. * Also store it in summary info for logging. */ scan_cur_block = curnt; nolog_print(" "); pr_dblock(nolog_print, curnt); nolog_print(" \015"); (void) fflush(stdout); disk_error = 0; /* * Do the actual analysis. */ status = analyze_blocks(flags, curnt, size, (unsigned)data, needinit, (F_ALLERRS | F_SILENT), &xfercnt); /* * If there were no errors, the pattern buffer is * still initialized, and we just loop to next chunk. */ needinit = 0; if (!status) continue; /* * There was an error. Check if surface analysis * can be continued. */ if (handle_error_conditions()) { scan_blocks_fixed = scan_cur_block = -1; return (-1); } /* * There was an error. Mark the pattern buffer * corrupt so it will get reinitialized. */ needinit = 1; /* * If it was not a media error, ignore it. */ if (!media_error) continue; /* * Loop 5 times through each sector of the chunk, * analyzing them individually. */ nolog_print(" "); pr_dblock(nolog_print, curnt); nolog_print(" \015"); (void) fflush(stdout); founderr = 0; for (j = 0; j < size * 5; j++) { i = j % size; disk_error = 0; status = analyze_blocks(flags, (curnt + i), 1, (unsigned)data, needinit, F_ALLERRS, NULL); needinit = 0; if (!status) continue; /* * There was an error. Check if surface analysis * can be continued. */ if (handle_error_conditions()) { scan_blocks_fixed = scan_cur_block = -1; return (-1); } /* * An error occurred. Mark the buffer * corrupt and see if it was media * related. */ needinit = 1; if (!media_error) continue; /* * We found a bad sector. Print out a message * and fix it if required. */ founderr = 1; if (scan_correct && (flags != SCAN_VALID)) { if (scan_repair(curnt+i, mode)) { error = -1; } } else err_print("\n"); /* * Stop after the error if required. */ if (scan_stop) goto out; } /* * Mark the pattern buffer corrupt to be safe. */ needinit = 1; /* * We didn't find an individual sector that was bad. * Print out a warning. */ if (!founderr) { err_print("Warning: unable to pinpoint "); err_print("defective block.\n"); } } /* * Print the end of each pass to the log file. */ enter_critical(); if (log_file) { pr_dblock(log_print, scan_cur_block); log_print("\n"); } scan_cur_block = -1; exit_critical(); fmt_print("\n"); /* * alternate the read and write for SCAN_VERIFY test */ if (flags & SCAN_VERIFY) { flags ^= SCAN_VERIFY_READ_PASS; } } out: /* * We got here either by giving up after an error or falling * through after all passes were completed. */ fmt_print("\n"); enter_critical(); /* * If the defect list is dirty, write it to disk, * if scan_restore_defects (the default) is true. */ if (!EMBEDDED_SCSI && (cur_list.flags & LIST_DIRTY) && (scan_restore_defects)) { cur_list.flags = 0; write_deflist(&cur_list); } /* * If the label is dirty, write it to disk. * if scan_restore_label (the default) is true. */ if ((cur_flags & LABEL_DIRTY) && (scan_restore_label)) { cur_flags &= ~LABEL_DIRTY; (void) write_label(); } /* * If we dropped down to here after an error, we need to write * the final block number to the log file for record keeping. */ if (log_file && scan_cur_block >= 0) { pr_dblock(log_print, scan_cur_block); log_print("\n"); } fmt_print("Total of %lld defective blocks repaired.\n", scan_blocks_fixed); /* * Reinitialize the logging variables so they don't get used * when they are not really valid. */ scan_blocks_fixed = scan_cur_block = -1; exit_critical(); return (error); } /* * This routine is called to repair a bad block discovered * during a scan operation. Return 0 for success, 1 for failure. * (This has been extracted out of do_scan(), to simplify it.) */ static int scan_repair(diskaddr_t bn, int mode) { int status; int result = 1; char *buf; int buf_is_good; int i; if (cur_ops->op_repair == NULL) { err_print("Warning: Controller does "); err_print("not support repairing.\n\n"); return (result); } buf = malloc(cur_blksz); if (buf == NULL) { err_print("Warning: no memory.\n\n"); return (result); } enter_critical(); /* * Determine if the error appears to be hard or soft. We * already assume there's an error. If we can get any * good data out of the sector, write that data back * after the repair. */ buf_is_good = 0; for (i = 0; i < 5; i++) { status = (*cur_ops->op_rdwr)(DIR_READ, cur_file, bn, 1, buf, F_SILENT, NULL); if (status == 0) { buf_is_good = 1; break; } } fmt_print("Repairing %s error on %llu (", buf_is_good ? "soft" : "hard", bn); pr_dblock(fmt_print, bn); fmt_print(")..."); status = (*cur_ops->op_repair)(bn, mode); if (status) { /* * If the repair failed, we note it and will return the * failure. However, the analysis goes on. */ fmt_print("failed.\n\n"); } else { /* * The repair worked. Write the good data we could * recover from the failed block, if possible. * If not, zero the block. In doing so, try to * determine if the new block appears ok. */ if (!buf_is_good) { bzero(buf, cur_blksz); fmt_print("Warning: Block %llu zero-filled.\n", bn); } else { fmt_print("ok.\n"); } status = (*cur_ops->op_rdwr)(DIR_WRITE, cur_file, bn, 1, buf, (F_SILENT | F_ALLERRS), NULL); if (status == 0) { status = (*cur_ops->op_rdwr)(DIR_READ, cur_file, bn, 1, buf, (F_SILENT | F_ALLERRS), NULL); } if (status) { fmt_print("The new block also appears defective.\n"); } fmt_print("\n"); /* * add the defect to the list and write the list out. * Also, kill the working list so it will get resynced * with the current list. * * For embedded scsi, we don't require a defect list. * However, if we have one, add the defect if the * list includes the grown list. If not, kill it * to force a resync if we need the list later. */ if (EMBEDDED_SCSI) { if (cur_list.list != NULL) { if (cur_list.flags & LIST_PGLIST) { add_ldef(bn, &cur_list); } else { kill_deflist(&cur_list); } } /* * The next "if" statement reflects the fix for * bug id 1026096 where format keeps adding the * same defect to the defect list. */ } else if (cur_ctype->ctype_flags & CF_WLIST) { kill_deflist(&cur_list); (*cur_ops->op_ex_cur)(&cur_list); fmt_print("Current list updated\n"); } else { add_ldef(bn, &cur_list); write_deflist(&cur_list); } kill_deflist(&work_list); /* Log the repair. */ scan_blocks_fixed++; /* return ok */ result = 0; } exit_critical(); free(buf); return (result); } /* * This routine analyzes a set of sectors on the disk. It simply returns * an error if a defect is found. It is called by do_scan(). */ static int analyze_blocks(int flags, diskaddr_t blkno, uint_t blkcnt, unsigned data, int init, int driver_flags, int *xfercntp) { int corrupt = 0; int status; diskaddr_t i, nints; unsigned *ptr = (uint_t *)pattern_buf; media_error = 0; if (flags & SCAN_VERIFY) { return (verify_blocks(flags, blkno, blkcnt, data, driver_flags, xfercntp)); } /* * Initialize the pattern buffer if necessary. */ nints = (diskaddr_t)blkcnt * cur_blksz / sizeof (int); if ((flags & SCAN_PATTERN) && init) { for (i = 0; i < nints; i++) *((int *)((int *)pattern_buf + i)) = data; } /* * Lock out interrupts so we can insure valid data will get * restored. This is necessary because there are modes * of scanning that corrupt the disk data then restore it at * the end of the analysis. */ enter_critical(); /* * If the disk data is valid, read it into the data buffer. */ if (flags & SCAN_VALID) { status = (*cur_ops->op_rdwr)(DIR_READ, cur_file, blkno, blkcnt, (caddr_t)cur_buf, driver_flags, xfercntp); if (status) goto bad; } /* * If we are doing pattern testing, write and read the pattern * from the pattern buffer. */ if (flags & SCAN_PATTERN) { /* * If the disk data was valid, mark it corrupt so we know * to restore it later. */ if (flags & SCAN_VALID) corrupt++; /* * Only write if we're not on the read pass of SCAN_PURGE. */ if (!(flags & SCAN_PURGE_READ_PASS)) { status = (*cur_ops->op_rdwr)(DIR_WRITE, cur_file, blkno, blkcnt, (caddr_t)pattern_buf, driver_flags, xfercntp); if (status) goto bad; } /* * Only read if we are on the read pass of SCAN_PURGE, if we * are purging. */ if ((!(flags & SCAN_PURGE)) || (flags & SCAN_PURGE_READ_PASS)) { status = (*cur_ops->op_rdwr)(DIR_READ, cur_file, blkno, blkcnt, (caddr_t)pattern_buf, driver_flags, xfercntp); if (status) goto bad; } } /* * If we are doing a data compare, make sure the pattern * came back intact. * Only compare if we are on the read pass of SCAN_PURGE, or * we wrote random data instead of the expected data pattern. */ if ((flags & SCAN_COMPARE) || (flags & SCAN_PURGE_READ_PASS)) { for (i = nints, ptr = (uint_t *)pattern_buf; i; i--) if (*ptr++ != data) { err_print("Data miscompare error (expecting "); err_print("0x%x, got 0x%x) at ", data, *((int *)((int *)pattern_buf + (nints - i)))); pr_dblock(err_print, blkno); err_print(", offset = 0x%llx.\n", (nints - i) * sizeof (int)); goto bad; } } /* * If we are supposed to write data out, do so. */ if (flags & SCAN_WRITE) { status = (*cur_ops->op_rdwr)(DIR_WRITE, cur_file, blkno, blkcnt, (caddr_t)cur_buf, driver_flags, xfercntp); if (status) goto bad; } exit_critical(); /* * No errors occurred, return ok. */ return (0); bad: /* * There was an error. If the data was corrupted, we write it * out from the data buffer to restore it. */ if (corrupt) { if ((*cur_ops->op_rdwr)(DIR_WRITE, cur_file, blkno, blkcnt, (caddr_t)cur_buf, F_NORMAL, xfercntp)) err_print("Warning: unable to restore original data.\n"); } exit_critical(); /* * Return the error. */ return (-1); } /* * This routine analyzes a set of sectors on the disk. It simply returns * an error if a defect is found. It is called by analyze_blocks(). * For simplicity, this is done as a separate function instead of * making the analyze_block routine complex. * * This routine implements the 'verify' command. It writes the disk * by writing unique data for each block; after the write pass, it * reads the data and verifies for correctness. Note that the entire * disk (or the range of disk) is fully written first and then read. * This should eliminate any caching effect on the drives. */ static int verify_blocks(int flags, diskaddr_t blkno, uint_t blkcnt, unsigned data, int driver_flags, int *xfercntp) { int status, i, nints; unsigned *ptr = (uint_t *)pattern_buf; nints = cur_blksz / sizeof (int); /* * Initialize the pattern buffer if we are in write pass. * Use the block number itself as data, each block has unique * buffer data that way. */ if (!(flags & SCAN_VERIFY_READ_PASS)) { for (data = blkno; data < blkno + blkcnt; data++) { for (i = 0; i < nints; i++) { *ptr++ = data; } } ptr = (uint_t *)pattern_buf; } /* * Only write if we're not on the read pass of SCAN_VERIFY. */ if (!(flags & SCAN_VERIFY_READ_PASS)) { status = (*cur_ops->op_rdwr)(DIR_WRITE, cur_file, blkno, blkcnt, (caddr_t)pattern_buf, driver_flags, xfercntp); if (status) goto bad; } else { /* * Only read if we are on the read pass of SCAN_VERIFY */ status = (*cur_ops->op_rdwr)(DIR_READ, cur_file, blkno, blkcnt, (caddr_t)pattern_buf, driver_flags, xfercntp); if (status) goto bad; /* * compare and make sure the pattern came back intact. */ for (data = blkno; data < blkno + blkcnt; data++) { for (i = 0; i < nints; i++) { if (*ptr++ != data) { ptr--; err_print("Data miscompare error " "(expecting 0x%x, got 0x%x) at ", data, *ptr); pr_dblock(err_print, blkno); err_print(", offset = 0x%x.\n", (ptr - (uint_t *)pattern_buf) * sizeof (int)); goto bad; } } } } /* * No errors occurred, return ok. */ return (0); bad: return (-1); } static int handle_error_conditions(void) { /* * Check if the errno is ENXIO. */ if (errno == ENXIO) { fmt_print("\n\nWarning:Cannot access drive, "); fmt_print("aborting surface analysis.\n"); return (-1); } /* * check for disk errors */ switch (disk_error) { case DISK_STAT_RESERVED: case DISK_STAT_UNAVAILABLE: fmt_print("\n\nWarning:Drive may be reserved "); fmt_print("or has been removed, "); fmt_print("aborting surface analysis.\n"); return (-1); case DISK_STAT_NOTREADY: fmt_print("\n\nWarning: Drive not ready, "); fmt_print("aborting surface analysis.\n"); return (-1); case DISK_STAT_DATA_PROTECT: fmt_print("\n\nWarning: Drive is write protected, "); fmt_print("aborting surface analysis.\n"); return (-1); default: break; } return (0); } /* * 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 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _ANALYZE_H #define _ANALYZE_H #ifdef __cplusplus extern "C" { #endif /* * This file contains definitions related to surface analysis. */ /* * These are variables referenced by the analysis routines. They * are declared in analyze.c. */ extern int scan_entire; extern diskaddr_t scan_lower, scan_upper; extern int scan_correct, scan_stop, scan_loop, scan_passes; extern int scan_random, scan_auto; extern uint_t scan_size; extern int scan_restore_defects, scan_restore_label; /* * These variables hold summary info for the end of analysis. * They are declared in analyze.c. */ extern offset_t scan_cur_block; extern int64_t scan_blocks_fixed; /* * This variable is used to tell whether the most recent surface * analysis error was caused by a media defect or some other problem. * It is declared in analyze.c. */ extern int media_error; extern int disk_error; /* * These defines are flags for the surface analysis types. */ #define SCAN_VALID 0x01 /* read data off disk */ #define SCAN_PATTERN 0x02 /* write and read pattern */ #define SCAN_COMPARE 0x04 /* manually check pattern */ #define SCAN_WRITE 0x08 /* write data to disk */ #define SCAN_PURGE 0x10 /* purge data on disk */ #define SCAN_PURGE_READ_PASS 0x20 /* read/compare pass */ #define SCAN_PURGE_ALPHA_PASS 0x40 /* alpha pattern pass */ #define SCAN_VERIFY 0x80 /* verify data on disk */ #define SCAN_VERIFY_READ_PASS 0x100 /* read/compare pass */ /* * Miscellaneous defines. */ #define BUF_SECTS 126 /* size of the buffers */ /* * Number of passes for purge command. It is kept here to allow * it to be used in menu_analyze.c also * This feature is added at the request of Sun Fed. */ #define NPPATTERNS 4 /* number of purge patterns */ #define READPATTERN (NPPATTERNS - 1) /* * defines for disk errors during surface analysis. */ #define DISK_STAT_RESERVED 0x01 /* disk is reserved */ #define DISK_STAT_NOTREADY 0x02 /* disk not ready */ #define DISK_STAT_UNAVAILABLE 0x03 /* disk is being formatted */ #define DISK_STAT_DATA_PROTECT 0x04 /* disk is write protected */ /* * Prototypes for ANSI C compilers */ int do_scan(int flags, int mode); #ifdef __cplusplus } #endif #endif /* _ANALYZE_H */ /* * 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 (c) 2011 Gary Mills * * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * Copyright 2015 Nexenta Systems, Inc. All rights reserved. * Copyright 2016 Igor Kozhukhov */ /* * This file contains functions to implement automatic configuration * of scsi disks. */ #include "global.h" #include #include #include #include #include #include #include "misc.h" #include "param.h" #include "ctlr_scsi.h" #include "auto_sense.h" #include "partition.h" #include "label.h" #include "startup.h" #include "analyze.h" #include "io.h" #include "hardware_structs.h" #include "menu_fdisk.h" #define DISK_NAME_MAX 256 extern int nctypes; extern struct ctlr_type ctlr_types[]; /* * Marker for free hog partition */ #define HOG (-1) /* * Default partition tables * * Disk capacity root swap usr * ------------- ---- ---- --- * 0mb to 64mb 0 0 remainder * 64mb to 180mb 16mb 16mb remainder * 180mb to 280mb 16mb 32mb remainder * 280mb to 380mb 24mb 32mb remainder * 380mb to 600mb 32mb 32mb remainder * 600mb to 1gb 32mb 64mb remainder * 1gb to 2gb 64mb 128mb remainder * 2gb on up 128mb 128mb remainder */ struct part_table { int partitions[NDKMAP]; }; static struct part_table part_table_64mb = { { 0, 0, 0, 0, 0, 0, HOG, 0} }; static struct part_table part_table_180mb = { { 16, 16, 0, 0, 0, 0, HOG, 0} }; static struct part_table part_table_280mb = { { 16, 32, 0, 0, 0, 0, HOG, 0} }; static struct part_table part_table_380mb = { { 24, 32, 0, 0, 0, 0, HOG, 0} }; static struct part_table part_table_600mb = { { 32, 32, 0, 0, 0, 0, HOG, 0} }; static struct part_table part_table_1gb = { { 32, 64, 0, 0, 0, 0, HOG, 0} }; static struct part_table part_table_2gb = { { 64, 128, 0, 0, 0, 0, HOG, 0} }; static struct part_table part_table_infinity = { { 128, 128, 0, 0, 0, 0, HOG, 0} }; static struct default_partitions { diskaddr_t min_capacity; diskaddr_t max_capacity; struct part_table *part_table; } default_partitions[] = { { 0, 64, &part_table_64mb }, /* 0 to 64 mb */ { 64, 180, &part_table_180mb }, /* 64 to 180 mb */ { 180, 280, &part_table_280mb }, /* 180 to 280 mb */ { 280, 380, &part_table_380mb }, /* 280 to 380 mb */ { 380, 600, &part_table_600mb }, /* 380 to 600 mb */ { 600, 1024, &part_table_1gb }, /* 600 to 1 gb */ { 1024, 2048, &part_table_2gb }, /* 1 to 2 gb */ { 2048, INFINITY, &part_table_infinity }, /* 2 gb on up */ }; #define DEFAULT_PARTITION_TABLE_SIZE \ (sizeof (default_partitions) / sizeof (struct default_partitions)) /* * msgs for check() */ #define FORMAT_MSG "Auto configuration via format.dat" #define GENERIC_MSG "Auto configuration via generic SCSI-2" /* * Disks on symbios(Hardwire raid controller) return a fixed number * of heads(64)/cylinders(64) and adjust the cylinders depending * capacity of the configured lun. * In such a case we get number of physical cylinders < 3 which * is the minimum required by solaris(2 reserved + 1 data cylinders). * Hence try to adjust the cylinders by reducing the "nsect/nhead". * */ /* * assuming a minimum of 32 block cylinders. */ #define MINIMUM_NO_HEADS 2 #define MINIMUM_NO_SECTORS 16 #define MINIMUM_NO_CYLINDERS 128 #if defined(_SUNOS_VTOC_8) /* These are 16-bit fields */ #define MAXIMUM_NO_HEADS 65535 #define MAXIMUM_NO_SECTORS 65535 #define MAXIMUM_NO_CYLINDERS 65535 #endif /* defined(_SUNOS_VTOC_8) */ /* * minimum number of cylinders required by Solaris. */ #define SUN_MIN_CYL 3 static struct disk_type *generic_disk_sense( int fd, int can_prompt, struct dk_label *label, struct scsi_inquiry *inquiry, struct scsi_capacity_16 *capacity, char *disk_name); static int use_existing_disk_type( int fd, int can_prompt, struct dk_label *label, struct scsi_inquiry *inquiry, struct disk_type *disk_type, struct scsi_capacity_16 *capacity); int build_default_partition(struct dk_label *label, int ctrl_type); static struct disk_type *find_scsi_disk_type( char *disk_name, struct dk_label *label); static struct disk_type *find_scsi_disk_by_name( char *disk_name); static struct ctlr_type *find_scsi_ctlr_type(void); static struct ctlr_info *find_scsi_ctlr_info( struct dk_cinfo *dkinfo); static struct disk_type *new_scsi_disk_type( int fd, char *disk_name, struct dk_label *label); static struct disk_info *find_scsi_disk_info( struct dk_cinfo *dkinfo); static struct disk_type *new_direct_disk_type(int fd, char *disk_name, struct dk_label *label); static int efi_ioctl(int fd, int cmd, dk_efi_t *dk_ioc); static int auto_label_init(struct dk_label *label); static struct ctlr_type *find_ctlr_type(ushort_t); static struct ctlr_info *find_ctlr_info(struct dk_cinfo *, ushort_t); static struct disk_info *find_disk_info(struct dk_cinfo *, ushort_t); static char *get_sun_disk_name( char *disk_name, struct scsi_inquiry *inquiry); static char *strcopy( char *dst, char *src, int n); static int adjust_disk_geometry(diskaddr_t capacity, uint_t *cyl, uint_t *nsect, uint_t *nhead); static void compute_chs_values(diskaddr_t total_capacity, diskaddr_t usable_capacity, uint_t *pcylp, uint_t *nheadp, uint_t *nsectp); #if defined(_SUNOS_VTOC_8) static diskaddr_t square_box( diskaddr_t capacity, uint_t *dim1, uint_t lim1, uint_t *dim2, uint_t lim2, uint_t *dim3, uint_t lim3); #endif /* defined(_SUNOS_VTOC_8) */ /* * We need to get information necessary to construct a *new* efi * label type */ struct disk_type * auto_efi_sense(int fd, struct efi_info *label) { struct dk_gpt *vtoc; int i; struct disk_type *disk, *dp; struct disk_info *disk_info; struct ctlr_info *ctlr; struct dk_cinfo dkinfo; struct partition_info *part; uint64_t reserved; uint16_t type; if (ioctl(fd, DKIOCINFO, &dkinfo) == -1) { if (option_msg && diag_msg) { err_print("DKIOCINFO failed\n"); } return (NULL); } if (cur_ctype != NULL) type = cur_ctype->ctype_ctype; else type = dkinfo.dki_ctype; if (type == DKC_DIRECT || type == DKC_VBD || type == DKC_BLKDEV) { ctlr = find_ctlr_info(&dkinfo, type); disk_info = find_disk_info(&dkinfo, type); } else { ctlr = find_scsi_ctlr_info(&dkinfo); disk_info = find_scsi_disk_info(&dkinfo); } /* * get vendor, product, revision and capacity info. */ if (get_disk_info(fd, label, disk_info) == -1) { return (NULL); } /* * Now build the default partition table */ if (efi_alloc_and_init(fd, EFI_NUMPAR, &vtoc) != 0) { err_print("efi_alloc_and_init failed. \n"); return (NULL); } label->e_parts = vtoc; reserved = efi_reserved_sectors(vtoc); /* * Create a whole hog EFI partition table: * S0 takes the whole disk except the primary EFI label, * backup EFI label, and the reserved partition. */ vtoc->efi_parts[0].p_tag = V_USR; vtoc->efi_parts[0].p_start = vtoc->efi_first_u_lba; vtoc->efi_parts[0].p_size = vtoc->efi_last_u_lba - vtoc->efi_first_u_lba - reserved + 1; /* * S1-S6 are unassigned slices. */ for (i = 1; i < vtoc->efi_nparts - 2; i ++) { vtoc->efi_parts[i].p_tag = V_UNASSIGNED; vtoc->efi_parts[i].p_start = 0; vtoc->efi_parts[i].p_size = 0; } /* * The reserved slice */ vtoc->efi_parts[vtoc->efi_nparts - 1].p_tag = V_RESERVED; vtoc->efi_parts[vtoc->efi_nparts - 1].p_start = vtoc->efi_last_u_lba - reserved + 1; vtoc->efi_parts[vtoc->efi_nparts - 1].p_size = reserved; /* * Now stick all of it into the disk_type struct */ disk = (struct disk_type *)zalloc(sizeof (struct disk_type)); assert(disk_info->disk_ctlr == ctlr); dp = ctlr->ctlr_ctype->ctype_dlist; if (dp == NULL) { ctlr->ctlr_ctype->ctype_dlist = dp; } else { while (dp->dtype_next != NULL) { dp = dp->dtype_next; } dp->dtype_next = disk; } disk->dtype_next = NULL; disk->vendor = strdup(label->vendor); disk->product = strdup(label->product); disk->revision = strdup(label->revision); if (disk->vendor == NULL || disk->product == NULL || disk->revision == NULL) { free(disk->vendor); free(disk->product); free(disk->revision); free(disk); return (NULL); } disk->capacity = label->capacity; part = (struct partition_info *) zalloc(sizeof (struct partition_info)); disk->dtype_plist = part; part->pinfo_name = alloc_string("default"); part->pinfo_next = NULL; part->etoc = vtoc; bzero(disk_info->v_volume, LEN_DKL_VVOL); disk_info->disk_parts = part; return (disk); } static int efi_ioctl(int fd, int cmd, dk_efi_t *dk_ioc) { void *data = dk_ioc->dki_data; int error; dk_ioc->dki_data_64 = (uint64_t)(uintptr_t)data; error = ioctl(fd, cmd, (void *)dk_ioc); dk_ioc->dki_data = data; return (error); } static struct ctlr_type * find_ctlr_type(ushort_t type) { struct mctlr_list *mlp; assert(type == DKC_DIRECT || type == DKC_VBD || type == DKC_BLKDEV); mlp = controlp; while (mlp != NULL) { if (mlp->ctlr_type->ctype_ctype == type) { return (mlp->ctlr_type); } mlp = mlp->next; } impossible("no DIRECT/VBD/BLKDEV controller type"); return (NULL); } static struct ctlr_info * find_ctlr_info(struct dk_cinfo *dkinfo, ushort_t type) { struct ctlr_info *ctlr; assert(type == DKC_DIRECT || type == DKC_VBD || type == DKC_BLKDEV); for (ctlr = ctlr_list; ctlr != NULL; ctlr = ctlr->ctlr_next) { if (ctlr->ctlr_addr == dkinfo->dki_addr && ctlr->ctlr_space == dkinfo->dki_space && ctlr->ctlr_ctype->ctype_ctype == dkinfo->dki_ctype) { return (ctlr); } } impossible("no DIRECT/VBD/BLKDEV controller info"); /*NOTREACHED*/ return (NULL); } static struct disk_info * find_disk_info(struct dk_cinfo *dkinfo, ushort_t type) { struct disk_info *disk; struct dk_cinfo *dp; assert(type == DKC_DIRECT || type == DKC_VBD || type == DKC_BLKDEV); for (disk = disk_list; disk != NULL; disk = disk->disk_next) { dp = &disk->disk_dkinfo; if (dp->dki_ctype == dkinfo->dki_ctype && dp->dki_cnum == dkinfo->dki_cnum && dp->dki_unit == dkinfo->dki_unit && strcmp(dp->dki_dname, dkinfo->dki_dname) == 0) { return (disk); } } impossible("No DIRECT/VBD/BLKDEV disk info instance\n"); /*NOTREACHED*/ return (NULL); } /* * To convert EFI to SMI labels, we need to get label geometry. * Unfortunately at this time there is no good way to do so. * DKIOCGGEOM will fail if disk is EFI labeled. So we hack around * it and clear EFI label, do a DKIOCGGEOM and put the EFI label * back on disk. * This routine gets the label geometry and initializes the label * It uses cur_file as opened device. * returns 0 if succeeds or -1 if failed. */ static int auto_label_init(struct dk_label *label) { dk_efi_t dk_ioc; dk_efi_t dk_ioc_back; efi_gpt_t *data = NULL; efi_gpt_t *databack = NULL; struct dk_geom disk_geom; struct dk_minfo disk_info; efi_gpt_t *backsigp; int fd = cur_file; int rval = -1; int efisize = EFI_LABEL_SIZE * 2; int success = 0; uint64_t sig; uint64_t backsig; if ((data = calloc(efisize, 1)) == NULL) { err_print("auto_label_init: calloc failed\n"); goto auto_label_init_out; } dk_ioc.dki_data = data; dk_ioc.dki_lba = 1; dk_ioc.dki_length = efisize; if (efi_ioctl(fd, DKIOCGETEFI, &dk_ioc) != 0) { err_print("auto_label_init: GETEFI failed\n"); goto auto_label_init_out; } if ((databack = calloc(efisize, 1)) == NULL) { err_print("auto_label_init calloc2 failed"); goto auto_label_init_out; } /* get the LBA size and capacity */ if (ioctl(fd, DKIOCGMEDIAINFO, (caddr_t)&disk_info) == -1) { err_print("auto_label_init: dkiocgmediainfo failed\n"); goto auto_label_init_out; } if (disk_info.dki_lbsize == 0) { if (option_msg && diag_msg) { err_print("auto_lbal_init: assuming 512 byte" "block size"); } disk_info.dki_lbsize = DEV_BSIZE; } dk_ioc_back.dki_data = databack; /* * back up efi label goes to capacity - 1, we are reading an extra block * before the back up label. */ dk_ioc_back.dki_lba = disk_info.dki_capacity - 1 - 1; dk_ioc_back.dki_length = efisize; if (efi_ioctl(fd, DKIOCGETEFI, &dk_ioc_back) != 0) { err_print("auto_label_init: GETEFI backup failed\n"); goto auto_label_init_out; } sig = dk_ioc.dki_data->efi_gpt_Signature; dk_ioc.dki_data->efi_gpt_Signature = 0x0; enter_critical(); if (efi_ioctl(fd, DKIOCSETEFI, &dk_ioc) == -1) { err_print("auto_label_init: SETEFI failed\n"); exit_critical(); goto auto_label_init_out; } backsigp = (efi_gpt_t *)((uintptr_t)dk_ioc_back.dki_data + cur_blksz); backsig = backsigp->efi_gpt_Signature; backsigp->efi_gpt_Signature = 0; if (efi_ioctl(fd, DKIOCSETEFI, &dk_ioc_back) == -1) { err_print("auto_label_init: SETEFI backup failed\n"); } if (ioctl(cur_file, DKIOCGGEOM, &disk_geom) != 0) err_print("auto_label_init: GGEOM failed\n"); else success = 1; dk_ioc.dki_data->efi_gpt_Signature = sig; backsigp->efi_gpt_Signature = backsig; if (efi_ioctl(cur_file, DKIOCSETEFI, &dk_ioc_back) == -1) { err_print("auto_label_init: SETEFI revert backup failed\n"); success = 0; } if (efi_ioctl(cur_file, DKIOCSETEFI, &dk_ioc) == -1) { err_print("auto_label_init: SETEFI revert failed\n"); success = 0; } exit_critical(); if (success == 0) goto auto_label_init_out; ncyl = disk_geom.dkg_ncyl; acyl = disk_geom.dkg_acyl; nhead = disk_geom.dkg_nhead; nsect = disk_geom.dkg_nsect; pcyl = ncyl + acyl; label->dkl_pcyl = pcyl; label->dkl_ncyl = ncyl; label->dkl_acyl = acyl; label->dkl_nhead = nhead; label->dkl_nsect = nsect; label->dkl_apc = 0; label->dkl_intrlv = 1; label->dkl_rpm = disk_geom.dkg_rpm; label->dkl_magic = DKL_MAGIC; (void) snprintf(label->dkl_asciilabel, sizeof (label->dkl_asciilabel), "%s cyl %u alt %u hd %u sec %u", "DEFAULT", ncyl, acyl, nhead, nsect); rval = 0; #if defined(_FIRMWARE_NEEDS_FDISK) (void) auto_solaris_part(label); ncyl = label->dkl_ncyl; #endif /* defined(_FIRMWARE_NEEDS_FDISK) */ if (!build_default_partition(label, DKC_DIRECT)) { rval = -1; } (void) checksum(label, CK_MAKESUM); auto_label_init_out: if (data) free(data); if (databack) free(databack); return (rval); } static struct disk_type * new_direct_disk_type( int fd, char *disk_name, struct dk_label *label) { struct disk_type *dp; struct disk_type *disk; struct ctlr_info *ctlr; struct dk_cinfo dkinfo; struct partition_info *part = NULL; struct partition_info *pt; struct disk_info *disk_info; int i; /* * Get the disk controller info for this disk */ if (ioctl(fd, DKIOCINFO, &dkinfo) == -1) { if (option_msg && diag_msg) { err_print("DKIOCINFO failed\n"); } return (NULL); } /* * Find the ctlr_info for this disk. */ ctlr = find_ctlr_info(&dkinfo, dkinfo.dki_ctype); /* * Allocate a new disk type for the direct controller. */ disk = (struct disk_type *)zalloc(sizeof (struct disk_type)); /* * Find the disk_info instance for this disk. */ disk_info = find_disk_info(&dkinfo, dkinfo.dki_ctype); /* * The controller and the disk should match. */ assert(disk_info->disk_ctlr == ctlr); /* * Link the disk into the list of disks */ dp = ctlr->ctlr_ctype->ctype_dlist; if (dp == NULL) { ctlr->ctlr_ctype->ctype_dlist = dp; } else { while (dp->dtype_next != NULL) { dp = dp->dtype_next; } dp->dtype_next = disk; } disk->dtype_next = NULL; /* * Allocate and initialize the disk name. */ disk->dtype_asciilabel = alloc_string(disk_name); /* * Initialize disk geometry info */ disk->dtype_pcyl = label->dkl_pcyl; disk->dtype_ncyl = label->dkl_ncyl; disk->dtype_acyl = label->dkl_acyl; disk->dtype_nhead = label->dkl_nhead; disk->dtype_nsect = label->dkl_nsect; disk->dtype_rpm = label->dkl_rpm; part = (struct partition_info *) zalloc(sizeof (struct partition_info)); pt = disk->dtype_plist; if (pt == NULL) { disk->dtype_plist = part; } else { while (pt->pinfo_next != NULL) { pt = pt->pinfo_next; } pt->pinfo_next = part; } part->pinfo_next = NULL; /* * Set up the partition name */ part->pinfo_name = alloc_string("default"); /* * Fill in the partition info from the label */ for (i = 0; i < NDKMAP; i++) { #if defined(_SUNOS_VTOC_8) part->pinfo_map[i] = label->dkl_map[i]; #elif defined(_SUNOS_VTOC_16) part->pinfo_map[i].dkl_cylno = label->dkl_vtoc.v_part[i].p_start / ((blkaddr_t)(disk->dtype_nhead * disk->dtype_nsect - apc)); part->pinfo_map[i].dkl_nblk = label->dkl_vtoc.v_part[i].p_size; #else #error No VTOC format defined. #endif /* defined(_SUNOS_VTOC_8) */ } /* * Use the VTOC if valid, or install a default */ if (label->dkl_vtoc.v_version == V_VERSION) { (void) memcpy(disk_info->v_volume, label->dkl_vtoc.v_volume, LEN_DKL_VVOL); part->vtoc = label->dkl_vtoc; } else { (void) memset(disk_info->v_volume, 0, LEN_DKL_VVOL); set_vtoc_defaults(part); } /* * Link the disk to the partition map */ disk_info->disk_parts = part; return (disk); } /* * Get a disk type that has label info. This is used to convert * EFI label to SMI label */ struct disk_type * auto_direct_get_geom_label(int fd, struct dk_label *label) { struct disk_type *disk_type; if (auto_label_init(label) != 0) { err_print("auto_direct_get_geom_label: failed to get label" "geometry"); return (NULL); } else { disk_type = new_direct_disk_type(fd, "DEFAULT", label); return (disk_type); } } /* * Auto-sense a scsi disk configuration, ie get the information * necessary to construct a label. We have two different * ways to auto-sense a scsi disk: * - format.dat override, via inquiry name * - generic scsi, via standard mode sense and inquiry * Depending on how and when we are called, and/or * change geometry and reformat. */ struct disk_type * auto_sense( int fd, int can_prompt, struct dk_label *label) { struct scsi_inquiry inquiry; struct scsi_capacity_16 capacity; struct disk_type *disk_type; char disk_name[DISK_NAME_MAX]; int force_format_dat = 0; int force_generic = 0; u_ioparam_t ioparam; int deflt; char *buf; /* * First, if expert mode, find out if the user * wants to override any of the standard methods. */ if (can_prompt && expert_mode) { deflt = 1; ioparam.io_charlist = confirm_list; if (input(FIO_MSTR, FORMAT_MSG, '?', &ioparam, &deflt, DATA_INPUT) == 0) { force_format_dat = 1; } else if (input(FIO_MSTR, GENERIC_MSG, '?', &ioparam, &deflt, DATA_INPUT) == 0) { force_generic = 1; } } /* * Get the Inquiry data. If this fails, there's * no hope for this disk, so give up. */ if (uscsi_inquiry(fd, (char *)&inquiry, sizeof (inquiry))) { return (NULL); } if (option_msg && diag_msg) { err_print("Product id: "); print_buf(inquiry.inq_pid, sizeof (inquiry.inq_pid)); err_print("\n"); } /* * Get the Read Capacity */ if (uscsi_read_capacity(fd, &capacity)) { return (NULL); } /* * If the reported capacity is set to zero, then the disk * is not usable. If the reported capacity is set to all * 0xf's, then this disk is too large. These could only * happen with a device that supports LBAs larger than 64 * bits which are not defined by any current T10 standards * or by error responding from target. */ if ((capacity.sc_capacity == 0) || (capacity.sc_capacity == UINT_MAX64)) { if (option_msg && diag_msg) { err_print("Invalid capacity\n"); } return (NULL); } if (option_msg && diag_msg) { err_print("blocks: %llu (0x%llx)\n", capacity.sc_capacity, capacity.sc_capacity); err_print("blksize: %u\n", capacity.sc_lbasize); } /* * Extract the disk name for the format.dat override */ (void) get_sun_disk_name(disk_name, &inquiry); if (option_msg && diag_msg) { err_print("disk name: `%s`\n", disk_name); } buf = zalloc(cur_blksz); if (scsi_rdwr(DIR_READ, fd, (diskaddr_t)0, 1, (caddr_t)buf, F_SILENT, NULL)) { free(buf); return (NULL); } free(buf); /* * Figure out which method we use for auto sense. * If a particular method fails, we fall back to * the next possibility. */ if (force_generic) { return (generic_disk_sense(fd, can_prompt, label, &inquiry, &capacity, disk_name)); } /* * Try for an existing format.dat first */ if ((disk_type = find_scsi_disk_by_name(disk_name)) != NULL) { if (use_existing_disk_type(fd, can_prompt, label, &inquiry, disk_type, &capacity)) { return (disk_type); } if (force_format_dat) { return (NULL); } } /* * Otherwise, try using generic SCSI-2 sense and inquiry. */ return (generic_disk_sense(fd, can_prompt, label, &inquiry, &capacity, disk_name)); } /*ARGSUSED*/ static struct disk_type * generic_disk_sense( int fd, int can_prompt, struct dk_label *label, struct scsi_inquiry *inquiry, struct scsi_capacity_16 *capacity, char *disk_name) { struct disk_type *disk; int setdefault = 0; uint_t pcyl = 0; uint_t ncyl = 0; uint_t acyl = 0; uint_t nhead = 0; uint_t nsect = 0; int rpm = 0; diskaddr_t nblocks = 0; diskaddr_t tblocks = 0; union { struct mode_format page3; uchar_t buf3[MAX_MODE_SENSE_SIZE]; } u_page3; union { struct mode_geometry page4; uchar_t buf4[MAX_MODE_SENSE_SIZE]; } u_page4; struct mode_format *page3 = &u_page3.page3; struct mode_geometry *page4 = &u_page4.page4; struct scsi_ms_header header; /* * If the name of this disk appears to be "SUN", use it, * otherwise construct a name out of the generic * Inquiry info. If it turns out that we already * have a SUN disk type of this name that differs * in geometry, we will revert to the generic name * anyway. */ if (memcmp(disk_name, "SUN", strlen("SUN")) != 0) { (void) get_generic_disk_name(disk_name, inquiry); } /* * Get the number of blocks from Read Capacity data. Note that * the logical block address range from 0 to capacity->sc_capacity. * Limit the size to 2 TB (UINT32_MAX) to use with SMI labels. */ tblocks = (capacity->sc_capacity + 1); if (tblocks > UINT32_MAX) nblocks = UINT32_MAX; else nblocks = tblocks; /* * Get current Page 3 - Format Parameters page */ if (uscsi_mode_sense(fd, DAD_MODE_FORMAT, MODE_SENSE_PC_CURRENT, (caddr_t)&u_page3, MAX_MODE_SENSE_SIZE, &header)) { setdefault = 1; } /* * Get current Page 4 - Drive Geometry page */ if (uscsi_mode_sense(fd, DAD_MODE_GEOMETRY, MODE_SENSE_PC_CURRENT, (caddr_t)&u_page4, MAX_MODE_SENSE_SIZE, &header)) { setdefault = 1; } if (setdefault != 1) { /* The inquiry of mode page 3 & page 4 are successful */ /* * Correct for byte order if necessary */ page4->rpm = BE_16(page4->rpm); page4->step_rate = BE_16(page4->step_rate); page3->tracks_per_zone = BE_16(page3->tracks_per_zone); page3->alt_sect_zone = BE_16(page3->alt_sect_zone); page3->alt_tracks_zone = BE_16(page3->alt_tracks_zone); page3->alt_tracks_vol = BE_16(page3->alt_tracks_vol); page3->sect_track = BE_16(page3->sect_track); page3->data_bytes_sect = BE_16(page3->data_bytes_sect); page3->interleave = BE_16(page3->interleave); page3->track_skew = BE_16(page3->track_skew); page3->cylinder_skew = BE_16(page3->cylinder_skew); /* * Construct a new label out of the sense data, * Inquiry and Capacity. * * If the disk capacity is > 1TB then simply compute * the CHS values based on the total disk capacity and * not use the values from mode-sense data. */ if (tblocks > INT32_MAX) { compute_chs_values(tblocks, nblocks, &pcyl, &nhead, &nsect); } else { pcyl = (page4->cyl_ub << 16) + (page4->cyl_mb << 8) + page4->cyl_lb; nhead = page4->heads; nsect = page3->sect_track; } rpm = page4->rpm; /* * If the number of physical cylinders reported is less * the SUN_MIN_CYL(3) then try to adjust the geometry so that * we have atleast SUN_MIN_CYL cylinders. */ if (pcyl < SUN_MIN_CYL) { if (nhead == 0 || nsect == 0) { setdefault = 1; } else if (adjust_disk_geometry( (diskaddr_t)(capacity->sc_capacity + 1), &pcyl, &nhead, &nsect)) { setdefault = 1; } } } /* * Mode sense page 3 and page 4 are obsolete in SCSI-3. For * newly developed large sector size disk, we will not rely on * those two pages but compute geometry directly. */ if ((setdefault == 1) || (capacity->sc_lbasize != DEV_BSIZE)) { /* * If the number of cylinders or the number of heads reported * is zero, we think the inquiry of page 3 and page 4 failed. * We will set the geometry infomation by ourselves. */ compute_chs_values(tblocks, nblocks, &pcyl, &nhead, &nsect); } /* * The sd driver reserves 2 cylinders the backup disk label and * the deviceid. Set the number of data cylinders to pcyl-acyl. */ acyl = DK_ACYL; ncyl = pcyl - acyl; if (option_msg && diag_msg) { err_print("Geometry:\n"); err_print(" pcyl: %u\n", pcyl); err_print(" ncyl: %u\n", ncyl); err_print(" heads: %u\n", nhead); err_print(" nsects: %u\n", nsect); err_print(" acyl: %u\n", acyl); #if defined(_SUNOS_VTOC_16) err_print(" bcyl: %u\n", bcyl); #endif /* defined(_SUNOS_VTOC_16) */ err_print(" rpm: %d\n", rpm); err_print(" nblocks: %llu\n", nblocks); } /* * Some drives do not support page4 or report 0 for page4->rpm, * adjust it to AVG_RPM, 3600. */ if (rpm < MIN_RPM || rpm > MAX_RPM) { if (option_msg && diag_msg) err_print("The current rpm value %d is invalid," " adjusting it to %d\n", rpm, AVG_RPM); rpm = AVG_RPM; } /* * Some drives report 0 for nsect (page 3, byte 10 and 11) if they * have variable number of sectors per track. So adjust nsect. * Also the value is defined as vendor specific, hence check if * it is in a tolerable range. The values (32 and 4 below) are * chosen so that this change below does not generate a different * geometry for currently supported sun disks. */ if ((nsect == 0) || ((diskaddr_t)pcyl * nhead * nsect) < (nblocks - nblocks/32) || ((diskaddr_t)pcyl * nhead * nsect) > (nblocks + nblocks/4)) { if (nblocks > (pcyl * nhead)) { err_print("Mode sense page(3) reports nsect value" " as %d, adjusting it to %llu\n", nsect, nblocks / (pcyl * nhead)); nsect = nblocks / (pcyl * nhead); } else { /* convert capacity to nsect * nhead * pcyl */ err_print("\nWARNING: Disk geometry is based on " "capacity data.\n\n"); compute_chs_values(tblocks, nblocks, &pcyl, &nhead, &nsect); ncyl = pcyl - acyl; if (option_msg && diag_msg) { err_print("Geometry:(after adjustment)\n"); err_print(" pcyl: %u\n", pcyl); err_print(" ncyl: %u\n", ncyl); err_print(" heads: %u\n", nhead); err_print(" nsects: %u\n", nsect); err_print(" acyl: %u\n", acyl); #if defined(_SUNOS_VTOC_16) err_print(" bcyl: %u\n", bcyl); #endif err_print(" rpm: %d\n", rpm); err_print(" nblocks: %llu\n", nblocks); } } } /* * Some drives report their physical geometry such that * it is greater than the actual capacity. Adjust the * geometry to allow for this, so we don't run off * the end of the disk. */ if (((diskaddr_t)pcyl * nhead * nsect) > nblocks) { uint_t p = pcyl; if (option_msg && diag_msg) { err_print("Computed capacity (%llu) exceeds actual " "disk capacity (%llu)\n", (diskaddr_t)pcyl * nhead * nsect, nblocks); } do { pcyl--; } while (((diskaddr_t)pcyl * nhead * nsect) > nblocks); if (can_prompt && expert_mode && !option_f) { /* * Try to adjust nsect instead of pcyl to see if we * can optimize. For compatability reasons do this * only in expert mode (refer to bug 1144812). */ uint_t n = nsect; do { n--; } while (((diskaddr_t)p * nhead * n) > nblocks); if (((diskaddr_t)p * nhead * n) > ((diskaddr_t)pcyl * nhead * nsect)) { u_ioparam_t ioparam; int deflt = 1; /* * Ask the user for a choice here. */ ioparam.io_bounds.lower = 1; ioparam.io_bounds.upper = 2; err_print("1. Capacity = %llu, with pcyl = %u " "nhead = %u nsect = %u\n", ((diskaddr_t)pcyl * nhead * nsect), pcyl, nhead, nsect); err_print("2. Capacity = %llu, with pcyl = %u " "nhead = %u nsect = %u\n", ((diskaddr_t)p * nhead * n), p, nhead, n); if (input(FIO_INT, "Select one of the above " "choices ", ':', &ioparam, &deflt, DATA_INPUT) == 2) { pcyl = p; nsect = n; } } } } #if defined(_SUNOS_VTOC_8) /* * Finally, we need to make sure we don't overflow any of the * fields in our disk label. To do this we need to `square * the box' so to speak. We will lose bits here. */ if ((pcyl > MAXIMUM_NO_CYLINDERS && ((nsect > MAXIMUM_NO_SECTORS) || (nhead > MAXIMUM_NO_HEADS))) || ((nsect > MAXIMUM_NO_SECTORS) && (nhead > MAXIMUM_NO_HEADS))) { err_print("This disk is too big to label. " " You will lose some blocks.\n"); } if ((pcyl > MAXIMUM_NO_CYLINDERS) || (nsect > MAXIMUM_NO_SECTORS) || (nhead > MAXIMUM_NO_HEADS)) { u_ioparam_t ioparam; int order; char msg[256]; order = ((pcyl > nhead)<<2) | ((pcyl > nsect)<<1) | (nhead > nsect); switch (order) { case 0x7: /* pcyl > nhead > nsect */ nblocks = square_box(nblocks, &pcyl, MAXIMUM_NO_CYLINDERS, &nhead, MAXIMUM_NO_HEADS, &nsect, MAXIMUM_NO_SECTORS); break; case 0x6: /* pcyl > nsect > nhead */ nblocks = square_box(nblocks, &pcyl, MAXIMUM_NO_CYLINDERS, &nsect, MAXIMUM_NO_SECTORS, &nhead, MAXIMUM_NO_HEADS); break; case 0x4: /* nsect > pcyl > nhead */ nblocks = square_box(nblocks, &nsect, MAXIMUM_NO_SECTORS, &pcyl, MAXIMUM_NO_CYLINDERS, &nhead, MAXIMUM_NO_HEADS); break; case 0x0: /* nsect > nhead > pcyl */ nblocks = square_box(nblocks, &nsect, MAXIMUM_NO_SECTORS, &nhead, MAXIMUM_NO_HEADS, &pcyl, MAXIMUM_NO_CYLINDERS); break; case 0x3: /* nhead > pcyl > nsect */ nblocks = square_box(nblocks, &nhead, MAXIMUM_NO_HEADS, &pcyl, MAXIMUM_NO_CYLINDERS, &nsect, MAXIMUM_NO_SECTORS); break; case 0x1: /* nhead > nsect > pcyl */ nblocks = square_box(nblocks, &nhead, MAXIMUM_NO_HEADS, &nsect, MAXIMUM_NO_SECTORS, &pcyl, MAXIMUM_NO_CYLINDERS); break; default: /* How did we get here? */ impossible("label overflow adjustment"); /* Do something useful */ nblocks = square_box(nblocks, &nhead, MAXIMUM_NO_HEADS, &nsect, MAXIMUM_NO_SECTORS, &pcyl, MAXIMUM_NO_CYLINDERS); break; } if (option_msg && diag_msg && (capacity->sc_capacity + 1 != nblocks)) { err_print("After adjusting geometry you lost" " %llu of %llu blocks.\n", (capacity->sc_capacity + 1 - nblocks), capacity->sc_capacity + 1); } while (can_prompt && expert_mode && !option_f) { int deflt = 1; /* * Allow user to modify this by hand if desired. */ (void) sprintf(msg, "\nGeometry: %u heads, %u sectors %u cylinders" " result in %llu out of %llu blocks.\n" "Do you want to modify the device geometry", nhead, nsect, pcyl, nblocks, capacity->sc_capacity + 1); ioparam.io_charlist = confirm_list; if (input(FIO_MSTR, msg, '?', &ioparam, &deflt, DATA_INPUT) != 0) break; ioparam.io_bounds.lower = MINIMUM_NO_HEADS; ioparam.io_bounds.upper = MAXIMUM_NO_HEADS; nhead = input(FIO_INT, "Number of heads", ':', &ioparam, (int *)&nhead, DATA_INPUT); ioparam.io_bounds.lower = MINIMUM_NO_SECTORS; ioparam.io_bounds.upper = MAXIMUM_NO_SECTORS; nsect = input(FIO_INT, "Number of sectors per track", ':', &ioparam, (int *)&nsect, DATA_INPUT); ioparam.io_bounds.lower = SUN_MIN_CYL; ioparam.io_bounds.upper = MAXIMUM_NO_CYLINDERS; pcyl = input(FIO_INT, "Number of cylinders", ':', &ioparam, (int *)&pcyl, DATA_INPUT); nblocks = (diskaddr_t)nhead * nsect * pcyl; if (nblocks > capacity->sc_capacity + 1) { err_print("Warning: %llu blocks exceeds " "disk capacity of %llu blocks\n", nblocks, capacity->sc_capacity + 1); } } } #endif /* defined(_SUNOS_VTOC_8) */ ncyl = pcyl - acyl; if (option_msg && diag_msg) { err_print("\nGeometry after adjusting for capacity:\n"); err_print(" pcyl: %u\n", pcyl); err_print(" ncyl: %u\n", ncyl); err_print(" heads: %u\n", nhead); err_print(" nsects: %u\n", nsect); err_print(" acyl: %u\n", acyl); err_print(" rpm: %d\n", rpm); } (void) memset((char *)label, 0, sizeof (struct dk_label)); label->dkl_magic = DKL_MAGIC; (void) snprintf(label->dkl_asciilabel, sizeof (label->dkl_asciilabel), "%s cyl %u alt %u hd %u sec %u", disk_name, ncyl, acyl, nhead, nsect); label->dkl_pcyl = pcyl; label->dkl_ncyl = ncyl; label->dkl_acyl = acyl; label->dkl_nhead = nhead; label->dkl_nsect = nsect; label->dkl_apc = 0; label->dkl_intrlv = 1; label->dkl_rpm = rpm; #if defined(_FIRMWARE_NEEDS_FDISK) if (auto_solaris_part(label) == -1) goto err; ncyl = label->dkl_ncyl; #endif /* defined(_FIRMWARE_NEEDS_FDISK) */ if (!build_default_partition(label, DKC_SCSI_CCS)) { goto err; } (void) checksum(label, CK_MAKESUM); /* * Find an existing disk type defined for this disk. * For this to work, both the name and geometry must * match. If there is no such type, but there already * is a disk defined with that name, but with a different * geometry, construct a new generic disk name out of * the inquiry information. Whatever name we're * finally using, if there's no such disk type defined, * build a new disk definition. */ if ((disk = find_scsi_disk_type(disk_name, label)) == NULL) { if (find_scsi_disk_by_name(disk_name) != NULL) { char old_name[DISK_NAME_MAX]; (void) strcpy(old_name, disk_name); (void) get_generic_disk_name(disk_name, inquiry); if (option_msg && diag_msg) { err_print( "Changing disk type name from '%s' to '%s'\n", old_name, disk_name); } (void) snprintf(label->dkl_asciilabel, sizeof (label->dkl_asciilabel), "%s cyl %u alt %u hd %u sec %u", disk_name, ncyl, acyl, nhead, nsect); (void) checksum(label, CK_MAKESUM); disk = find_scsi_disk_type(disk_name, label); } if (disk == NULL) { disk = new_scsi_disk_type(fd, disk_name, label); if (disk == NULL) goto err; } } return (disk); err: if (option_msg && diag_msg) { err_print( "Configuration via generic SCSI-2 information failed\n"); } return (NULL); } /*ARGSUSED*/ static int use_existing_disk_type( int fd, int can_prompt, struct dk_label *label, struct scsi_inquiry *inquiry, struct disk_type *disk_type, struct scsi_capacity_16 *capacity) { int pcyl; int acyl; int nhead; int nsect; int rpm; /* * Construct a new label out of the format.dat */ pcyl = disk_type->dtype_pcyl; acyl = disk_type->dtype_acyl; ncyl = disk_type->dtype_ncyl; nhead = disk_type->dtype_nhead; nsect = disk_type->dtype_nsect; rpm = disk_type->dtype_rpm; if (option_msg && diag_msg) { err_print("Format.dat geometry:\n"); err_print(" pcyl: %u\n", pcyl); err_print(" heads: %u\n", nhead); err_print(" nsects: %u\n", nsect); err_print(" acyl: %u\n", acyl); err_print(" rpm: %d\n", rpm); } (void) memset((char *)label, 0, sizeof (struct dk_label)); label->dkl_magic = DKL_MAGIC; (void) snprintf(label->dkl_asciilabel, sizeof (label->dkl_asciilabel), "%s cyl %u alt %u hd %u sec %u", disk_type->dtype_asciilabel, ncyl, acyl, nhead, nsect); label->dkl_pcyl = pcyl; label->dkl_ncyl = ncyl; label->dkl_acyl = acyl; label->dkl_nhead = nhead; label->dkl_nsect = nsect; label->dkl_apc = 0; label->dkl_intrlv = 1; label->dkl_rpm = rpm; if (!build_default_partition(label, DKC_SCSI_CCS)) { goto err; } (void) checksum(label, CK_MAKESUM); return (1); err: if (option_msg && diag_msg) { err_print( "Configuration via format.dat geometry failed\n"); } return (0); } int build_default_partition( struct dk_label *label, int ctrl_type) { int i; int ncyls[NDKMAP]; diskaddr_t nblks; int cyl; struct dk_vtoc *vtoc; struct part_table *pt; struct default_partitions *dpt; diskaddr_t capacity; int freecyls; int blks_per_cyl; int ncyl; #ifdef lint ctrl_type = ctrl_type; #endif /* * Install a default vtoc */ vtoc = &label->dkl_vtoc; vtoc->v_version = V_VERSION; vtoc->v_nparts = NDKMAP; vtoc->v_sanity = VTOC_SANE; for (i = 0; i < NDKMAP; i++) { vtoc->v_part[i].p_tag = default_vtoc_map[i].p_tag; vtoc->v_part[i].p_flag = default_vtoc_map[i].p_flag; } /* * Find a partition that matches this disk. Capacity * is in integral number of megabytes. */ capacity = ((diskaddr_t)(label->dkl_ncyl) * label->dkl_nhead * label->dkl_nsect) / (diskaddr_t)((1024 * 1024) / cur_blksz); dpt = default_partitions; for (i = 0; i < DEFAULT_PARTITION_TABLE_SIZE; i++, dpt++) { if (capacity >= dpt->min_capacity && capacity < dpt->max_capacity) { break; } } if (i == DEFAULT_PARTITION_TABLE_SIZE) { if (option_msg && diag_msg) { err_print("No matching default partition (%llu)\n", capacity); } return (0); } pt = dpt->part_table; /* * Go through default partition table, finding fixed * sized entries. */ freecyls = label->dkl_ncyl; blks_per_cyl = label->dkl_nhead * label->dkl_nsect; for (i = 0; i < NDKMAP; i++) { if (pt->partitions[i] == HOG || pt->partitions[i] == 0) { ncyls[i] = 0; } else { /* * Calculate number of cylinders necessary * for specified size, rounding up to * the next greatest integral number of * cylinders. Always give what they * asked or more, never less. */ nblks = pt->partitions[i] * ((1024*1024)/cur_blksz); nblks += (blks_per_cyl - 1); ncyls[i] = nblks / blks_per_cyl; freecyls -= ncyls[i]; } } if (freecyls < 0) { if (option_msg && diag_msg) { for (i = 0; i < NDKMAP; i++) { if (ncyls[i] == 0) continue; err_print("Partition %d: %u cyls\n", i, ncyls[i]); } err_print("Free cylinders exhausted (%d)\n", freecyls); } return (0); } #if defined(i386) /* * Set the default boot partition to 1 cylinder */ ncyls[8] = 1; freecyls -= 1; /* * If current disk type is not a SCSI disk, * set the default alternates partition to 2 cylinders */ if (ctrl_type != DKC_SCSI_CCS) { ncyls[9] = 2; freecyls -= 2; } #endif /* defined(i386) */ /* * Set the free hog partition to whatever space remains. * It's an error to have more than one HOG partition, * but we don't verify that here. */ for (i = 0; i < NDKMAP; i++) { if (pt->partitions[i] == HOG) { assert(ncyls[i] == 0); ncyls[i] = freecyls; break; } } /* * Error checking */ ncyl = 0; for (i = 0; i < NDKMAP; i++) { ncyl += ncyls[i]; } assert(ncyl == (label->dkl_ncyl)); /* * Finally, install the partition in the label. */ cyl = 0; #if defined(_SUNOS_VTOC_16) for (i = NDKMAP/2; i < NDKMAP; i++) { if (i == 2 || ncyls[i] == 0) continue; label->dkl_vtoc.v_part[i].p_start = cyl * blks_per_cyl; label->dkl_vtoc.v_part[i].p_size = ncyls[i] * blks_per_cyl; cyl += ncyls[i]; } for (i = 0; i < NDKMAP/2; i++) { #elif defined(_SUNOS_VTOC_8) for (i = 0; i < NDKMAP; i++) { #else #error No VTOC format defined. #endif /* defined(_SUNOS_VTOC_16) */ if (i == 2 || ncyls[i] == 0) { #if defined(_SUNOS_VTOC_8) if (i != 2) { label->dkl_map[i].dkl_cylno = 0; label->dkl_map[i].dkl_nblk = 0; } #endif continue; } #if defined(_SUNOS_VTOC_8) label->dkl_map[i].dkl_cylno = cyl; label->dkl_map[i].dkl_nblk = ncyls[i] * blks_per_cyl; #elif defined(_SUNOS_VTOC_16) label->dkl_vtoc.v_part[i].p_start = cyl * blks_per_cyl; label->dkl_vtoc.v_part[i].p_size = ncyls[i] * blks_per_cyl; #else #error No VTOC format defined. #endif /* defined(_SUNOS_VTOC_8) */ cyl += ncyls[i]; } /* * Set the whole disk partition */ #if defined(_SUNOS_VTOC_8) label->dkl_map[2].dkl_cylno = 0; label->dkl_map[2].dkl_nblk = label->dkl_ncyl * label->dkl_nhead * label->dkl_nsect; #elif defined(_SUNOS_VTOC_16) label->dkl_vtoc.v_part[2].p_start = 0; label->dkl_vtoc.v_part[2].p_size = (label->dkl_ncyl + label->dkl_acyl) * label->dkl_nhead * label->dkl_nsect; #else #error No VTOC format defined. #endif /* defined(_SUNOS_VTOC_8) */ if (option_msg && diag_msg) { float scaled; err_print("\n"); for (i = 0; i < NDKMAP; i++) { #if defined(_SUNOS_VTOC_8) if (label->dkl_map[i].dkl_nblk == 0) #elif defined(_SUNOS_VTOC_16) if (label->dkl_vtoc.v_part[i].p_size == 0) #else #error No VTOC format defined. #endif /* defined(_SUNOS_VTOC_8) */ continue; err_print("Partition %d: ", i); #if defined(_SUNOS_VTOC_8) scaled = bn2mb(label->dkl_map[i].dkl_nblk); #elif defined(_SUNOS_VTOC_16) scaled = bn2mb(label->dkl_vtoc.v_part[i].p_size); #else #error No VTOC format defined. #endif /* defined(_SUNOS_VTOC_8) */ if (scaled > 1024.0) { err_print("%6.2fGB ", scaled/1024.0); } else { err_print("%6.2fMB ", scaled); } #if defined(_SUNOS_VTOC_8) err_print(" %6d cylinders\n", label->dkl_map[i].dkl_nblk/blks_per_cyl); #elif defined(_SUNOS_VTOC_16) err_print(" %6d cylinders\n", label->dkl_vtoc.v_part[i].p_size/blks_per_cyl); #else #error No VTOC format defined. #endif /* defined(_SUNOS_VTOC_8) */ } err_print("\n"); } return (1); } /* * Find an existing scsi disk definition by this name, * if possible. */ static struct disk_type * find_scsi_disk_type( char *disk_name, struct dk_label *label) { struct ctlr_type *ctlr; struct disk_type *dp; ctlr = find_scsi_ctlr_type(); for (dp = ctlr->ctype_dlist; dp != NULL; dp = dp->dtype_next) { if (dp->dtype_asciilabel) { if ((strcmp(dp->dtype_asciilabel, disk_name) == 0) && dp->dtype_pcyl == label->dkl_pcyl && dp->dtype_ncyl == label->dkl_ncyl && dp->dtype_acyl == label->dkl_acyl && dp->dtype_nhead == label->dkl_nhead && dp->dtype_nsect == label->dkl_nsect) { return (dp); } } } return (NULL); } /* * Find an existing scsi disk definition by this name, * if possible. */ static struct disk_type * find_scsi_disk_by_name( char *disk_name) { struct ctlr_type *ctlr; struct disk_type *dp; ctlr = find_scsi_ctlr_type(); for (dp = ctlr->ctype_dlist; dp != NULL; dp = dp->dtype_next) { if (dp->dtype_asciilabel) { if ((strcmp(dp->dtype_asciilabel, disk_name) == 0)) { return (dp); } } } return (NULL); } /* * Return a pointer to the ctlr_type structure for SCSI * disks. This list is built into the program, so there's * no chance of not being able to find it, unless someone * totally mangles the code. */ static struct ctlr_type * find_scsi_ctlr_type(void) { struct mctlr_list *mlp; mlp = controlp; while (mlp != NULL) { if (mlp->ctlr_type->ctype_ctype == DKC_SCSI_CCS) { return (mlp->ctlr_type); } mlp = mlp->next; } impossible("no SCSI controller type"); return (NULL); } /* * Return a pointer to the scsi ctlr_info structure. This * structure is allocated the first time format sees a * disk on this controller, so it must be present. */ static struct ctlr_info * find_scsi_ctlr_info( struct dk_cinfo *dkinfo) { struct ctlr_info *ctlr; if (dkinfo->dki_ctype != DKC_SCSI_CCS) { return (NULL); } for (ctlr = ctlr_list; ctlr != NULL; ctlr = ctlr->ctlr_next) { if (ctlr->ctlr_addr == dkinfo->dki_addr && ctlr->ctlr_space == dkinfo->dki_space && ctlr->ctlr_ctype->ctype_ctype == DKC_SCSI_CCS) { return (ctlr); } } impossible("no SCSI controller info"); return (NULL); } static struct disk_type * new_scsi_disk_type( int fd, char *disk_name, struct dk_label *label) { struct disk_type *dp; struct disk_type *disk; struct ctlr_info *ctlr; struct dk_cinfo dkinfo; struct partition_info *part; struct partition_info *pt; struct disk_info *disk_info; int i; /* * Get the disk controller info for this disk */ if (ioctl(fd, DKIOCINFO, &dkinfo) == -1) { if (option_msg && diag_msg) { err_print("DKIOCINFO failed\n"); } return (NULL); } /* * Find the ctlr_info for this disk. */ ctlr = find_scsi_ctlr_info(&dkinfo); /* * Allocate a new disk type for the SCSI controller. */ disk = (struct disk_type *)zalloc(sizeof (struct disk_type)); /* * Find the disk_info instance for this disk. */ disk_info = find_scsi_disk_info(&dkinfo); /* * The controller and the disk should match. */ assert(disk_info->disk_ctlr == ctlr); /* * Link the disk into the list of disks */ dp = ctlr->ctlr_ctype->ctype_dlist; if (dp == NULL) { ctlr->ctlr_ctype->ctype_dlist = disk; } else { while (dp->dtype_next != NULL) { dp = dp->dtype_next; } dp->dtype_next = disk; } disk->dtype_next = NULL; /* * Allocate and initialize the disk name. */ disk->dtype_asciilabel = alloc_string(disk_name); /* * Initialize disk geometry info */ disk->dtype_pcyl = label->dkl_pcyl; disk->dtype_ncyl = label->dkl_ncyl; disk->dtype_acyl = label->dkl_acyl; disk->dtype_nhead = label->dkl_nhead; disk->dtype_nsect = label->dkl_nsect; disk->dtype_rpm = label->dkl_rpm; /* * Attempt to match the partition map in the label * with a know partition for this disk type. */ for (part = disk->dtype_plist; part; part = part->pinfo_next) { if (parts_match(label, part)) { break; } } /* * If no match was made, we need to create a partition * map for this disk. */ if (part == NULL) { part = (struct partition_info *) zalloc(sizeof (struct partition_info)); pt = disk->dtype_plist; if (pt == NULL) { disk->dtype_plist = part; } else { while (pt->pinfo_next != NULL) { pt = pt->pinfo_next; } pt->pinfo_next = part; } part->pinfo_next = NULL; /* * Set up the partition name */ part->pinfo_name = alloc_string("default"); /* * Fill in the partition info from the label */ for (i = 0; i < NDKMAP; i++) { #if defined(_SUNOS_VTOC_8) part->pinfo_map[i] = label->dkl_map[i]; #elif defined(_SUNOS_VTOC_16) part->pinfo_map[i].dkl_cylno = label->dkl_vtoc.v_part[i].p_start / ((blkaddr32_t)(disk->dtype_nhead * disk->dtype_nsect - apc)); part->pinfo_map[i].dkl_nblk = label->dkl_vtoc.v_part[i].p_size; #else #error No VTOC format defined. #endif /* defined(_SUNOS_VTOC_8) */ } } /* * Use the VTOC if valid, or install a default */ if (label->dkl_vtoc.v_version == V_VERSION) { (void) memcpy(disk_info->v_volume, label->dkl_vtoc.v_volume, LEN_DKL_VVOL); part->vtoc = label->dkl_vtoc; } else { (void) memset(disk_info->v_volume, 0, LEN_DKL_VVOL); set_vtoc_defaults(part); } /* * Link the disk to the partition map */ disk_info->disk_parts = part; return (disk); } /* * Delete a disk type from disk type list. */ int delete_disk_type(struct disk_type *disk_type) { struct ctlr_type *ctlr; struct disk_type *dp, *disk; if (cur_ctype->ctype_ctype == DKC_DIRECT || cur_ctype->ctype_ctype == DKC_VBD || cur_ctype->ctype_ctype == DKC_BLKDEV) ctlr = find_ctlr_type(cur_ctype->ctype_ctype); else ctlr = find_scsi_ctlr_type(); if (ctlr == NULL || ctlr->ctype_dlist == NULL) { return (-1); } disk = ctlr->ctype_dlist; if (disk == disk_type) { ctlr->ctype_dlist = disk->dtype_next; if (cur_label == L_TYPE_EFI) free(disk->dtype_plist->etoc); free(disk->dtype_plist); free(disk->vendor); free(disk->product); free(disk->revision); free(disk); return (0); } else { for (dp = disk->dtype_next; dp != NULL; disk = disk->dtype_next, dp = dp->dtype_next) { if (dp == disk_type) { disk->dtype_next = dp->dtype_next; if (cur_label == L_TYPE_EFI) free(dp->dtype_plist->etoc); free(dp->dtype_plist); free(dp->vendor); free(dp->product); free(dp->revision); free(dp); return (0); } } return (-1); } } static struct disk_info * find_scsi_disk_info( struct dk_cinfo *dkinfo) { struct disk_info *disk; struct dk_cinfo *dp; for (disk = disk_list; disk != NULL; disk = disk->disk_next) { assert(dkinfo->dki_ctype == DKC_SCSI_CCS); dp = &disk->disk_dkinfo; if (dp->dki_ctype == dkinfo->dki_ctype && dp->dki_cnum == dkinfo->dki_cnum && dp->dki_unit == dkinfo->dki_unit && strcmp(dp->dki_dname, dkinfo->dki_dname) == 0) { return (disk); } } impossible("No SCSI disk info instance\n"); return (NULL); } static char * get_sun_disk_name( char *disk_name, struct scsi_inquiry *inquiry) { /* * Extract the sun name of the disk */ (void) memset(disk_name, 0, DISK_NAME_MAX); (void) memcpy(disk_name, (char *)&inquiry->inq_pid[9], 7); return (disk_name); } char * get_generic_disk_name( char *disk_name, struct scsi_inquiry *inquiry) { char *p; (void) memset(disk_name, 0, DISK_NAME_MAX); p = strcopy(disk_name, inquiry->inq_vid, sizeof (inquiry->inq_vid)); *p++ = '-'; p = strcopy(p, inquiry->inq_pid, sizeof (inquiry->inq_pid)); *p++ = '-'; p = strcopy(p, inquiry->inq_revision, sizeof (inquiry->inq_revision)); return (disk_name); } /* * Copy a string of characters from src to dst, for at * most n bytes. Strip all leading and trailing spaces, * and stop if there are any non-printable characters. * Return ptr to the next character to be filled. */ static char * strcopy( char *dst, char *src, int n) { int i; while (*src == ' ' && n > 0) { src++; n--; } for (i = 0; n-- > 0 && isascii(*src) && isprint(*src); src++) { if (*src == ' ') { i++; } else { while (i-- > 0) *dst++ = ' '; *dst++ = *src; } } *dst = 0; return (dst); } /* * adjust disk geometry. * This is used when disk reports a disk geometry page having * no of physical cylinders is < 3 which is the minimum required * by Solaris (2 for storing labels and at least one as a data * cylinder ) */ int adjust_disk_geometry(diskaddr_t capacity, uint_t *cyl, uint_t *nhead, uint_t *nsect) { uint_t lcyl = *cyl; uint_t lnhead = *nhead; uint_t lnsect = *nsect; assert(lcyl < SUN_MIN_CYL); /* * reduce nsect by 2 for each iteration and re-calculate * the number of cylinders. */ while (lnsect > MINIMUM_NO_SECTORS && lcyl < MINIMUM_NO_CYLINDERS) { /* * make sure that we do not go below MINIMUM_NO_SECTORS. */ lnsect = max(MINIMUM_NO_SECTORS, lnsect / 2); lcyl = (capacity) / (lnhead * lnsect); } /* * If the geometry still does not satisfy * MINIMUM_NO_CYLINDERS then try to reduce the * no of heads. */ while (lnhead > MINIMUM_NO_HEADS && lcyl < MINIMUM_NO_CYLINDERS) { lnhead = max(MINIMUM_NO_HEADS, lnhead / 2); lcyl = (capacity) / (lnhead * lnsect); } /* * now we should have atleast SUN_MIN_CYL cylinders. * If we still do not get SUN_MIN_CYL with MINIMUM_NO_HEADS * and MINIMUM_NO_HEADS then return error. */ if (lcyl < SUN_MIN_CYL) return (1); else { *cyl = lcyl; *nhead = lnhead; *nsect = lnsect; return (0); } } #if defined(_SUNOS_VTOC_8) /* * Reduce the size of one dimention below a specified * limit with a minimum loss of volume. Dimenstions are * assumed to be passed in form the largest value (the one * that needs to be reduced) to the smallest value. The * values will be twiddled until they are all less than or * equal to their limit. Returns the number in the new geometry. */ static diskaddr_t square_box( diskaddr_t capacity, uint_t *dim1, uint_t lim1, uint_t *dim2, uint_t lim2, uint_t *dim3, uint_t lim3) { uint_t i; /* * Although the routine should work with any ordering of * parameters, it's most efficient if they are passed in * in decreasing magnitude. */ assert(*dim1 >= *dim2); assert(*dim2 >= *dim3); /* * This is done in a very arbitrary manner. We could try to * find better values but I can't come up with a method that * would run in a reasonable amount of time. That could take * approximately 65535 * 65535 iterations of a dozen flops each * or well over 4G flops. * * First: * * Let's see how far we can go with bitshifts w/o losing * any blocks. */ for (i = 0; (((*dim1)>>i)&1) == 0 && ((*dim1)>>i) > lim1; i++) ; if (i) { *dim1 = ((*dim1)>>i); *dim3 = ((*dim3)< lim1) || ((*dim2) > lim2) || ((*dim3) > lim3)) { double d[4]; /* * Second: * * Set the highest value at its limit then calculate errors, * adjusting the 2nd highest value (we get better resolution * that way). */ d[1] = lim1; d[3] = *dim3; d[2] = (double)capacity/(d[1]*d[3]); /* * If we overflowed the middle term, set it to its limit and * chose a new low term. */ if (d[2] > lim2) { d[2] = lim2; d[3] = (double)capacity/(d[1]*d[2]); } /* * Convert to integers. */ *dim1 = (int)d[1]; *dim2 = (int)d[2]; *dim3 = (int)d[3]; } /* * Fixup any other possible problems. * If this happens, we need a new disklabel format. */ if (*dim1 > lim1) *dim1 = lim1; if (*dim2 > lim2) *dim2 = lim2; if (*dim3 > lim3) *dim3 = lim3; return (*dim1 * *dim2 * *dim3); } #endif /* defined(_SUNOS_VTOC_8) */ /* * Calculate CHS values based on the capacity data. * * NOTE: This function is same as cmlb_convert_geomerty() function in * cmlb kernel module. */ static void compute_chs_values(diskaddr_t total_capacity, diskaddr_t usable_capacity, uint_t *pcylp, uint_t *nheadp, uint_t *nsectp) { /* Unlabeled SCSI floppy device */ if (total_capacity < 160) { /* Less than 80K */ *nheadp = 1; *pcylp = total_capacity; *nsectp = 1; return; } else if (total_capacity <= 0x1000) { *nheadp = 2; *pcylp = 80; *nsectp = total_capacity / (80 * 2); return; } /* * For all devices we calculate cylinders using the heads and sectors * we assign based on capacity of the device. The algorithm is * designed to be compatible with the way other operating systems * lay out fdisk tables for X86 and to insure that the cylinders never * exceed 65535 to prevent problems with X86 ioctls that report * geometry. * For some smaller disk sizes we report geometry that matches those * used by X86 BIOS usage. For larger disks, we use SPT that are * multiples of 63, since other OSes that are not limited to 16-bits * for cylinders stop at 63 SPT we make do by using multiples of 63 SPT. * * The following table (in order) illustrates some end result * calculations: * * Maximum number of blocks nhead nsect * * 2097152 (1GB) 64 32 * 16777216 (8GB) 128 32 * 1052819775 (502.02GB) 255 63 * 2105639550 (0.98TB) 255 126 * 3158459325 (1.47TB) 255 189 * 4211279100 (1.96TB) 255 252 * 5264098875 (2.45TB) 255 315 * ... */ if (total_capacity <= 0x200000) { *nheadp = 64; *nsectp = 32; } else if (total_capacity <= 0x01000000) { *nheadp = 128; *nsectp = 32; } else { *nheadp = 255; /* make nsect be smallest multiple of 63 */ *nsectp = ((total_capacity + (UINT16_MAX * 255 * 63) - 1) / (UINT16_MAX * 255 * 63)) * 63; if (*nsectp == 0) *nsectp = (UINT16_MAX / 63) * 63; } if (usable_capacity < total_capacity) *pcylp = usable_capacity / ((*nheadp) * (*nsectp)); else *pcylp = total_capacity / ((*nheadp) * (*nsectp)); } /* * 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 (c) 2011 Gary Mills * * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _AUTO_SENSE_H #define _AUTO_SENSE_H #ifdef __cplusplus extern "C" { #endif struct scsi_inquiry; /* anonymous struct */ #ifdef __STDC__ /* * Prototypes for ANSI C compilers */ struct disk_type *auto_sense( int fd, int can_prompt, struct dk_label *label); struct disk_type *auto_efi_sense( int fd, struct efi_info *label); int build_default_partition( struct dk_label *label, int ctrl_type); int delete_disk_type( struct disk_type *disk_type); struct disk_type *auto_direct_get_geom_label(int fd, struct dk_label *label); char *get_generic_disk_name( char *disk_name, struct scsi_inquiry *inquiry); #else struct disk_type *auto_sense(); struct disk_type *auto_efi_sense(); int build_default_partition(); struct disk_type *auto_direct_get_geom_label(); char *get_generic_disk_name(); #endif /* __STDC__ */ #ifdef __cplusplus } #endif #endif /* _AUTO_SENSE_H */ /* * 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 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * copyright (c) 1990, 1991 UNIX System Laboratories, Inc. * copyright (c) 1984, 1986, 1987, 1988, 1989, 1990 AT&T * All Rights Reserved */ #ifndef _BADSEC_H #define _BADSEC_H #ifdef __cplusplus extern "C" { #endif #define BADSECFILE "/etc/scsi/badsec" #define FAILURE 1 #define SUCCESS 0 #define MAXBLENT 4 struct badsec_lst { uint_t bl_cnt; struct badsec_lst *bl_nxt; uint_t bl_sec[MAXBLENT]; }; #define BADSLSZ sizeof (struct badsec_lst) #define BFI_FORMAT 0 #define ATA_FORMAT 1 #define ALTS_ADDPART 0x1 /* add alternate partition */ struct alts_mempart { /* incore alts partition info */ int ap_flag; /* flag for alternate partition */ struct alts_parttbl *ap_tblp; /* alts partition table */ uint_t ap_tbl_secsiz; /* alts parttbl sector size */ uchar_t *ap_memmapp; /* incore alternate sector map */ uchar_t *ap_mapp; /* alternate sector map */ uint_t ap_map_secsiz; /* alts partmap sector size */ uint_t ap_map_sectot; /* alts partmap # sector */ struct alts_ent *ap_entp; /* alternate sector entry table */ uint_t ap_ent_secsiz; /* alts entry sector size */ struct alts_ent *ap_gbadp; /* growing badsec entry table */ uint_t ap_gbadcnt; /* growing bad sector count */ struct dkl_partition part; /* alts partition configuration */ }; /* size of incore alternate partition memory structure */ #define ALTS_MEMPART_SIZE sizeof (struct alts_mempart) struct altsectbl { /* working alts info */ struct alts_ent *ast_entp; /* alternate sector entry table */ uint_t ast_entused; /* entry used */ struct alt_info *ast_alttblp; /* alts info */ uint_t ast_altsiz; /* size of alts info */ struct alts_ent *ast_gbadp; /* growing bad sector entry ptr */ uint_t ast_gbadcnt; /* growing bad sector entry cnt */ }; /* size of incore alternate partition memory structure */ #define ALTSECTBL_SIZE sizeof (struct altsectbl) /* macro definitions */ #define byte_to_secsiz(APSIZE, BPS) (uint_t) \ ((((APSIZE) + (BPS) - 1) \ / (uint_t)(BPS)) * (BPS)) #ifdef __cplusplus } #endif #endif /* _BADSEC_H */ /* * 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. */ /* * This file contains miscellaneous device validation routines. */ #include "global.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "misc.h" #include "checkdev.h" #include /* Function prototypes */ static struct swaptable *getswapentries(void); static void freeswapentries(struct swaptable *); static int getpartition(char *pathname); static int checkpartitions(int bm_mounted); static struct swaptable * getswapentries(void) { struct swaptable *st; struct swapent *swapent; int i, num; char fullpathname[MAXPATHLEN]; /* * get the number of swap entries */ if ((num = swapctl(SC_GETNSWP, NULL)) == -1) { err_print("swapctl error "); fullabort(); } if (num == 0) return (NULL); if ((st = (swaptbl_t *)malloc(num * sizeof (swapent_t) + sizeof (int))) == NULL) { err_print("getswapentries: malloc failed.\n"); fullabort(); } swapent = st->swt_ent; for (i = 0; i < num; i++, swapent++) { if ((swapent->ste_path = malloc(MAXPATHLEN)) == NULL) { err_print("getswapentries: malloc failed.\n"); fullabort(); } } st->swt_n = num; if ((num = swapctl(SC_LIST, (void *)st)) == -1) { err_print("swapctl error "); fullabort(); } swapent = st->swt_ent; for (i = 0; i < num; i++, swapent++) { if (*swapent->ste_path != '/') { (void) snprintf(fullpathname, sizeof (fullpathname), "/dev/%s", swapent->ste_path); (void) strcpy(swapent->ste_path, fullpathname); } } return (st); } static void freeswapentries(struct swaptable *st) { struct swapent *swapent; int i; swapent = st->swt_ent; for (i = 0; i < st->swt_n; i++, swapent++) free(swapent->ste_path); free(st); } /* * function getpartition: */ static int getpartition(char *pathname) { int mfd; struct dk_cinfo dkinfo; struct stat stbuf; char raw_device[MAXPATHLEN]; int found = -1; /* * Map the block device name to the raw device name. * If it doesn't appear to be a device name, skip it. */ if (match_substr(pathname, "/dev/") == 0) return (found); (void) strcpy(raw_device, "/dev/r"); (void) strcat(raw_device, pathname + strlen("/dev/")); /* * Determine if this appears to be a disk device. * First attempt to open the device. If if fails, skip it. */ if ((mfd = open(raw_device, O_RDWR | O_NDELAY)) < 0) { return (found); } /* * Must be a character device */ if (fstat(mfd, &stbuf) == -1 || !S_ISCHR(stbuf.st_mode)) { (void) close(mfd); return (found); } /* * Attempt to read the configuration info on the disk. */ if (ioctl(mfd, DKIOCINFO, &dkinfo) < 0) { (void) close(mfd); return (found); } /* * Finished with the opened device */ (void) close(mfd); /* * If it's not the disk we're interested in, it doesn't apply. */ if (cur_disk->disk_dkinfo.dki_ctype != dkinfo.dki_ctype || cur_disk->disk_dkinfo.dki_cnum != dkinfo.dki_cnum || cur_disk->disk_dkinfo.dki_unit != dkinfo.dki_unit || strcmp(cur_disk->disk_dkinfo.dki_dname, dkinfo.dki_dname) != 0) { return (found); } /* * Extract the partition that is mounted. */ return (PARTITION(stbuf.st_rdev)); } /* * This Routine checks to see if there are partitions used for swapping overlaps * a given portion of a disk. If the start parameter is < 0, it means * that the entire disk should be checked */ int checkswap(diskaddr_t start, diskaddr_t end) { struct swaptable *st; struct swapent *swapent; int i; int found = 0; struct dk_map32 *map; int part; /* * If we are only checking part of the disk, the disk must * have a partition map to check against. If it doesn't, * we hope for the best. */ if (cur_parts == NULL) return (0); /* * check for swap entries */ st = getswapentries(); /* * if there are no swap entries return. */ if (st == NULL) return (0); swapent = st->swt_ent; for (i = 0; i < st->swt_n; i++, swapent++) { if ((part = getpartition(swapent->ste_path)) != -1) { if (start == UINT_MAX64) { found = -1; break; } map = &cur_parts->pinfo_map[part]; if ((start >= (int)(map->dkl_cylno * spc() + map->dkl_nblk)) || (end < (int)(map->dkl_cylno * spc()))) { continue; } found = -1; break; }; } freeswapentries(st); /* * If we found trouble and we're running from a command file, * quit before doing something we really regret. */ if (found && option_f) { err_print( "Operation on disks being used for swapping must be interactive.\n"); cmdabort(SIGINT); } return (found); } /* * Determines if there are partitions that are a part of an SVM, VxVM, zpool * volume or a live upgrade device, overlapping a given portion of a disk. * Mounts and swap devices are checked in legacy format code. */ int checkdevinuse(char *cur_disk_path, diskaddr_t start, diskaddr_t end, int print, int check_label) { int error; int found = 0; int check = 0; int i; int bm_inuse = 0; int part = 0; uint64_t slice_start, slice_size; dm_descriptor_t *slices = NULL; nvlist_t *attrs = NULL; char *usage; char *name; /* * If the user does not want to do in use checking, return immediately. * Normally, this is handled in libdiskmgt. For format, there is more * processing required, so we want to bypass the in use checking * here. */ if (NOINUSE_SET) return (0); /* * Skip if it is not a real disk * * There could be two kinds of strings in cur_disk_path * One starts with c?t?d?, while the other is a absolute path of a * block device file. */ if (*cur_disk_path != 'c') { struct stat stbuf; char majorname[16]; major_t majornum; (void) stat(cur_disk_path, &stbuf); majornum = major(stbuf.st_rdev); (void) modctl(MODGETNAME, majorname, sizeof (majorname), &majornum); if (strcmp(majorname, "sd")) if (strcmp(majorname, "ssd")) if (strcmp(majorname, "cmdk")) return (0); } /* * Truncate the characters following "d*", such as "s*" or "p*" */ cur_disk_path = basename(cur_disk_path); name = strrchr(cur_disk_path, 'd'); if (name) { name++; for (; (*name <= '9') && (*name >= '0'); name++) { } *name = (char)0; } /* * For format, we get basic 'in use' details from libdiskmgt. After * that we must do the appropriate checking to see if the 'in use' * details require a bit of additional work. */ dm_get_slices(cur_disk_path, &slices, &error); if (error) { /* * If ENODEV, it actually means the device is not in use. * We will return 0 without displaying error. */ if (error != ENODEV) { err_print("Error occurred with device in use" "checking: %s\n", strerror(error)); return (found); } } if (slices == NULL) return (found); for (i = 0; slices[i] != 0; i++) { /* * If we are checking the whole disk * then any and all in use data is * relevant. */ if (start == UINT_MAX64) { name = dm_get_name(slices[i], &error); if (error != 0 || !name) { err_print("Error occurred with device " "in use checking: %s\n", strerror(error)); continue; } if (dm_inuse(name, &usage, DM_WHO_FORMAT, &error) || error) { if (error != 0) { dm_free_name(name); name = NULL; err_print("Error occurred with " "device in use checking: " "%s\n", strerror(error)); continue; } dm_free_name(name); name = NULL; /* * If this is a dump device, then it is * a failure. You cannot format a slice * that is a dedicated dump device. */ if (strstr(usage, DM_USE_DUMP)) { if (print) { err_print(usage); free(usage); } dm_free_descriptors(slices); return (1); } /* * We really found a device that is in use. * Set 'found' for the return value, and set * 'check' to indicate below that we must * get the partition number to set bm_inuse * in the event we are trying to label this * device. check_label is set when we are * checking modifications for in use slices * on the device. */ found ++; check = 1; if (print) { err_print(usage); free(usage); } } } else { /* * Before getting the in use data, verify that the * current slice is within the range we are checking. */ attrs = dm_get_attributes(slices[i], &error); if (error) { err_print("Error occurred with device in use " "checking: %s\n", strerror(error)); continue; } if (attrs == NULL) { continue; } (void) nvlist_lookup_uint64(attrs, DM_START, &slice_start); (void) nvlist_lookup_uint64(attrs, DM_SIZE, &slice_size); if (start >= (slice_start + slice_size) || (end < slice_start)) { nvlist_free(attrs); attrs = NULL; continue; } name = dm_get_name(slices[i], &error); if (error != 0 || !name) { err_print("Error occurred with device " "in use checking: %s\n", strerror(error)); nvlist_free(attrs); attrs = NULL; continue; } if (dm_inuse(name, &usage, DM_WHO_FORMAT, &error) || error) { if (error != 0) { dm_free_name(name); name = NULL; err_print("Error occurred with " "device in use checking: " "%s\n", strerror(error)); nvlist_free(attrs); attrs = NULL; continue; } dm_free_name(name); name = NULL; /* * If this is a dump device, then it is * a failure. You cannot format a slice * that is a dedicated dump device. */ if (strstr(usage, DM_USE_DUMP)) { if (print) { err_print(usage); free(usage); } dm_free_descriptors(slices); nvlist_free(attrs); return (1); } /* * We really found a device that is in use. * Set 'found' for the return value, and set * 'check' to indicate below that we must * get the partition number to set bm_inuse * in the event we are trying to label this * device. check_label is set when we are * checking modifications for in use slices * on the device. */ found ++; check = 1; if (print) { err_print(usage); free(usage); } } } /* * If check is set it means we found a slice(the current slice) * on this device in use in some way. We potentially want * to check this slice when labeling is * requested. We set bm_inuse with this partition value * for use later if check_label was set when called. */ if (check) { name = dm_get_name(slices[i], &error); if (error != 0 || !name) { err_print("Error occurred with device " "in use checking: %s\n", strerror(error)); nvlist_free(attrs); attrs = NULL; continue; } part = getpartition(name); dm_free_name(name); name = NULL; if (part != -1) { bm_inuse |= 1 << part; } check = 0; } /* * If we have attributes then we have successfully * found the slice we were looking for and we also * know this means we are not searching the whole * disk so break out of the loop * now. */ if (attrs) { nvlist_free(attrs); break; } } if (slices) { dm_free_descriptors(slices); } /* * The user is trying to label the disk. We have to do special * checking here to ensure they are not trying to modify a slice * that is in use in an incompatible way. */ if (check_label && bm_inuse) { /* * !0 indicates that we found a * problem. In this case, we have overloaded * the use of checkpartitions to work for * in use devices. bm_inuse is representative * of the slice that is in use, not that * is mounted as is in the case of the normal * use of checkpartitions. * * The call to checkpartitions will return !0 if * we are trying to shrink a device that we have found * to be in use above. */ return (checkpartitions(bm_inuse)); } return (found); } /* * This routine checks to see if there are mounted partitions overlapping * a given portion of a disk. If the start parameter is < 0, it means * that the entire disk should be checked. */ int checkmount(diskaddr_t start, diskaddr_t end) { FILE *fp; int found = 0; struct dk_map32 *map; int part; struct mnttab mnt_record; struct mnttab *mp = &mnt_record; /* * If we are only checking part of the disk, the disk must * have a partition map to check against. If it doesn't, * we hope for the best. */ if (cur_parts == NULL) return (0); /* * Lock out interrupts because of the mntent protocol. */ enter_critical(); /* * Open the mount table. */ fp = fopen(MNTTAB, "r"); if (fp == NULL) { err_print("Unable to open mount table.\n"); fullabort(); } /* * Loop through the mount table until we run out of entries. */ while ((getmntent(fp, mp)) != -1) { if ((part = getpartition(mp->mnt_special)) == -1) 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 (start == UINT_MAX64) { found = -1; break; } /* * If the partition overlaps the zone we're checking, * then we found trouble. We can quit searching. */ map = &cur_parts->pinfo_map[part]; if ((start >= (int)(map->dkl_cylno * spc() + map->dkl_nblk)) || (end < (int)(map->dkl_cylno * spc()))) { continue; } found = -1; break; } /* * Close down the mount table. */ (void) fclose(fp); exit_critical(); /* * If we found trouble and we're running from a command file, * quit before doing something we really regret. */ if (found && option_f) { err_print("Operation on mounted disks must be interactive.\n"); cmdabort(SIGINT); } /* * Return the result. */ return (found); } int check_label_with_swap(void) { int i; struct swaptable *st; struct swapent *swapent; int part; int bm_swap = 0; /* * If we are only checking part of the disk, the disk must * have a partition map to check against. If it doesn't, * we hope for the best. */ if (cur_parts == NULL) return (0); /* Will be checked later */ /* * Check for swap entries */ st = getswapentries(); /* * if there are no swap entries return. */ if (st == NULL) return (0); swapent = st->swt_ent; for (i = 0; i < st->swt_n; i++, swapent++) if ((part = getpartition(swapent->ste_path)) != -1) bm_swap |= (1 << part); freeswapentries(st); return (checkpartitions(bm_swap)); } /* * Check the new label with the existing label on the disk, * to make sure that any mounted partitions are not being * affected by writing the new label. */ int check_label_with_mount(void) { FILE *fp; int part; struct mnttab mnt_record; struct mnttab *mp = &mnt_record; int bm_mounted = 0; /* * If we are only checking part of the disk, the disk must * have a partition map to check against. If it doesn't, * we hope for the best. */ if (cur_parts == NULL) return (0); /* Will be checked later */ /* * Lock out interrupts because of the mntent protocol. */ enter_critical(); /* * Open the mount table. */ fp = fopen(MNTTAB, "r"); if (fp == NULL) { err_print("Unable to open mount table.\n"); fullabort(); } /* * Loop through the mount table until we run out of entries. */ while ((getmntent(fp, mp)) != -1) { if ((part = getpartition(mp->mnt_special)) != -1) bm_mounted |= (1 << part); } /* * Close down the mount table. */ (void) fclose(fp); exit_critical(); return (checkpartitions(bm_mounted)); } /* * This Routine checks if any partitions specified * are affected by writing the new label */ static int checkpartitions(int bm_mounted) { struct dk_map32 *n; struct dk_map *o; struct dk_allmap old_map; int i, found = 0; struct partition64 o_efi; /* * Now we need to check that the current partition list and the * previous partition list (which there must be if we actually * have partitions mounted) overlap in any way on the mounted * partitions */ /* * Check if the user wants to online-label an * existing EFI label. */ if (cur_label == L_TYPE_EFI) { for (i = 0; i < EFI_NUMPAR; i++) { if (bm_mounted & (1 << i)) { o_efi.p_partno = i; if (ioctl(cur_file, DKIOCPARTITION, &o_efi) == -1) { err_print("Unable to get information " "for EFI partition %d.\n", i); return (-1); } /* * Partition can grow or remain same. */ if (o_efi.p_start == cur_parts->etoc-> efi_parts[i].p_start && o_efi.p_size <= cur_parts->etoc->efi_parts[i].p_size) { continue; } found = -1; } if (found) break; } } else { /* * Get the "real" (on-disk) version of the partition table */ if (ioctl(cur_file, DKIOCGAPART, &old_map) == -1) { err_print("Unable to get current partition map.\n"); return (-1); } for (i = 0; i < NDKMAP; i++) { if (bm_mounted & (1 << i)) { /* * This partition is mounted */ o = &old_map.dka_map[i]; n = &cur_parts->pinfo_map[i]; #ifdef DEBUG fmt_print( "checkpartitions :checking partition '%c' \n", i + PARTITION_BASE); #endif /* * If partition is identical, we're fine. * If the partition grows, we're also fine, * because the routines in partition.c check * for overflow. It will (ultimately) be up * to the routines in partition.c to warn * about creation of overlapping partitions. */ if (o->dkl_cylno == n->dkl_cylno && o->dkl_nblk <= n->dkl_nblk) { #ifdef DEBUG if (o->dkl_nblk < n->dkl_nblk) { fmt_print( "- new partition larger by %d blocks", n->dkl_nblk-o->dkl_nblk); } fmt_print("\n"); #endif continue; } #ifdef DEBUG fmt_print("- changes; old (%d,%d)->new " "(%d,%d)\n", o->dkl_cylno, o->dkl_nblk, n->dkl_cylno, n->dkl_nblk); #endif found = -1; } if (found) break; } } /* * If we found trouble and we're running from a command file, * quit before doing something we really regret. */ if (found && option_f) { err_print("Operation on mounted disks or \ disks currently being used for swapping must be interactive.\n"); cmdabort(SIGINT); } /* * Return the result. */ return (found); } /* * 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. */ #ifndef _CHECKDEV_H #define _CHECKDEV_H #ifdef __cplusplus extern "C" { #endif /* * Prototypes for ANSI C */ int checkmount(diskaddr_t start, diskaddr_t end); int checkswap(diskaddr_t start, diskaddr_t end); int check_label_with_mount(void); int check_label_with_swap(void); int checkdevinuse(char *cur_disk_path, diskaddr_t start, diskaddr_t end, int print, int check_label); #ifdef __cplusplus } #endif #endif /* _CHECKDEV_H */ /* * 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. */ /* * This file contains the routines for the IDE drive interface */ #include "global.h" #include #include #include #include #include #include #include #include #include #include #include #include #if defined(i386) #include #endif #include #include "startup.h" #include "misc.h" #include "ctlr_ata.h" #include "analyze.h" #include "param.h" #include "io.h" #include "badsec.h" #include "menu_fdisk.h" diskaddr_t altsec_offset; int wr_altsctr(); int read_altsctr(); int updatebadsec(); static int ata_ck_format(void); #ifdef i386 static int ata_ex_cur(struct defect_list *); static int ata_wr_cur(struct defect_list *); static int ata_repair(diskaddr_t, int); #endif /* i386 */ struct ctlr_ops ataops = { #if defined(sparc) ata_rdwr, ata_ck_format, 0, 0, 0, 0, 0, 0, #else ata_rdwr, ata_ck_format, 0, 0, ata_ex_cur, ata_repair, 0, ata_wr_cur, #endif /* defined(sparc) */ }; struct ctlr_ops pcmcia_ataops = { ata_rdwr, ata_ck_format, 0, 0, 0, 0, 0, 0, }; #if defined(i386) static struct dkl_partition *dpart = NULL; #endif /* defined(i386) */ extern struct badsec_lst *badsl_chain; extern int badsl_chain_cnt; extern struct badsec_lst *gbadsl_chain; extern int gbadsl_chain_cnt; extern struct alts_mempart *ap; static char *dadkrawioerrs[] = { "cmd was successful", /* DADKIO_STAT_NO_ERROR */ "device not ready", /* DADKIO_STAT_NOT_READY */ "error on medium blkno: %d", /* DADKIO_STAT_MEDIUM_ERROR */ "other hardware error", /* DADKIO_STAT_HARDWARE_ERROR */ "illegal request", /* DADKIO_STAT_ILLEGAL_REQUEST */ "illegal block address: %d", /* DADKIO_STAT_ILLEGAL_ADDRESS */ "device write-protected", /* DADKIO_STAT_WRITE_PROTECTED */ "no response from device", /* DADKIO_STAT_TIMED_OUT */ "parity error in data", /* DADKIO_STAT_PARITY */ "error on bus", /* DADKIO_STAT_BUS_ERROR */ "data recovered via ECC", /* DADKIO_STAT_SOFT_ERROR */ "no resources for cmd", /* DADKIO_STAT_NO_RESOURCES */ "device is not formatted", /* DADKIO_STAT_NOT_FORMATTED */ "device is reserved", /* DADKIO_STAT_RESERVED */ "feature not supported", /* DADKIO_STAT_NOT_SUPPORTED */ }; /*ARGSUSED6*/ _STATIC int ata_rdwr(int dir, int fd, diskaddr_t blk64, int secnt, caddr_t bufaddr, int flags, int *xfercntp) { int tmpsec; struct dadkio_rwcmd dadkio_rwcmd; blkaddr_t blkno; blkno = (blkaddr_t)blk64; bzero((caddr_t)&dadkio_rwcmd, sizeof (struct dadkio_rwcmd)); tmpsec = secnt * cur_blksz; /* Doing raw read */ dadkio_rwcmd.cmd = (dir == DIR_READ) ? DADKIO_RWCMD_READ : DADKIO_RWCMD_WRITE; dadkio_rwcmd.blkaddr = blkno; dadkio_rwcmd.buflen = tmpsec; dadkio_rwcmd.flags = flags; dadkio_rwcmd.bufaddr = bufaddr; media_error = 0; if (cur_ctype->ctype_ctype == DKC_PCMCIA_ATA) { /* * PCATA requires to use "p0" when calling * DIOCTL_RWCMD ioctl() to read/write the label */ (void) close(fd); (void) open_cur_file(FD_USE_P0_PATH); fd = cur_file; } if (ioctl(fd, DIOCTL_RWCMD, &dadkio_rwcmd) == -1) { err_print("DIOCTL_RWCMD: %s\n", strerror(errno)); return (1); } if (cur_ctype->ctype_ctype == DKC_PCMCIA_ATA) { /* Restore cur_file with cur_disk->disk_path */ (void) open_cur_file(FD_USE_CUR_DISK_PATH); } switch (dadkio_rwcmd.status.status) { case DADKIO_STAT_NOT_READY: disk_error = DISK_STAT_NOTREADY; break; case DADKIO_STAT_RESERVED: disk_error = DISK_STAT_RESERVED; break; case DADKIO_STAT_WRITE_PROTECTED: disk_error = DISK_STAT_DATA_PROTECT; break; case DADKIO_STAT_MEDIUM_ERROR: media_error = 1; break; } if (dadkio_rwcmd.status.status) { if ((flags & F_SILENT) == 0) err_print(dadkrawioerrs[dadkio_rwcmd.status.status], dadkio_rwcmd.status.failed_blk); return (1); } return (0); } int ata_ck_format(void) { char *bufaddr; int status; bufaddr = (char *)zalloc(4 * cur_blksz); status = ata_rdwr(DIR_READ, cur_file, (diskaddr_t)1, 4, (caddr_t)bufaddr, 0, NULL); free(bufaddr); return (!status); } #if defined(i386) static int get_alts_slice(void) { int i; int alts_slice = -1; if (cur_parts == NULL) { (void) fprintf(stderr, "No current partition list\n"); return (-1); } for (i = 0; i < V_NUMPAR && alts_slice == -1; i++) { if (cur_parts->vtoc.v_part[i].p_tag == V_ALTSCTR) { alts_slice = i; dpart = &cur_parts->vtoc.v_part[i]; } } if (alts_slice == -1) { (void) fprintf(stderr, "NO Alt slice\n"); return (-1); } if (!solaris_offset) if (copy_solaris_part(&cur_disk->fdisk_part)) return (-1); altsec_offset = dpart->p_start + solaris_offset; return (SUCCESS); } static int put_alts_slice(void) { int status; status = wr_altsctr(); if (status) { return (status); } if (ioctl(cur_file, DKIOCADDBAD, NULL) == -1) { (void) fprintf(stderr, "Warning: DKIOCADDBAD ioctl failed\n"); sync(); return (-1); } sync(); return (0); } static int ata_convert_list(struct defect_list *list, int list_format) { int i; struct defect_entry *new_defect; switch (list_format) { case BFI_FORMAT: if (ap->ap_tblp->alts_ent_used) { new_defect = calloc(ap->ap_tblp->alts_ent_used, sizeof (struct defect_entry)); if (new_defect == NULL) { err_print( "ata_convert_list: calloc failed\n"); fullabort(); } list->header.count = ap->ap_tblp->alts_ent_used; list->header.magicno = (uint_t)DEFECT_MAGIC; list->list = new_defect; for (i = 0; i < ap->ap_tblp->alts_ent_used; i++, new_defect++) { new_defect->cyl = bn2c((ap->ap_entp)[i].bad_start); new_defect->head = bn2h((ap->ap_entp)[i].bad_start); new_defect->bfi = UNKNOWN; new_defect->sect = bn2s((ap->ap_entp)[i].bad_start); new_defect->nbits = UNKNOWN; } } else { list->header.count = 0; list->header.magicno = (uint_t)DEFECT_MAGIC; new_defect = calloc(1, sizeof (struct defect_entry)); if (new_defect == NULL) { err_print( "ata_convert_list: calloc failed\n"); fullabort(); } list->list = new_defect; } break; default: err_print("ata_convert_list: can't deal with it\n"); exit(0); } (void) checkdefsum(list, CK_MAKESUM); return (0); } /* * NB - there used to be a ata_ex_man() which was identical to * ata_ex_cur; since it's really not a "manufacturer's list", * it's gone; if we ever want that exact functionality back, * we can add ata_ex_cur() to the ctlr_ops above. Otherwise, * if this is ever modified to support formatting of IDE drives, * we should probably add something that issues the * drive Read Defect list rather than getting the s9 info * as ata_ex_cur() does. */ static int ata_ex_cur(struct defect_list *list) { int status; status = get_alts_slice(); if (status) return (status); status = read_altsctr(dpart); if (status) { return (status); } (void) ata_convert_list(list, BFI_FORMAT); return (status); } int ata_repair(diskaddr_t bn, int flag) { int status; struct badsec_lst *blc_p; struct badsec_lst *blc_p_nxt; #ifdef lint flag++; #endif (void) get_alts_slice(); if (!gbadsl_chain) { blc_p = (struct badsec_lst *)calloc(1, BADSLSZ); if (!blc_p) { (void) fprintf(stderr, "Unable to allocate memory for additional bad sectors\n"); return (-1); } gbadsl_chain = blc_p; } for (blc_p = gbadsl_chain; blc_p->bl_nxt; ) blc_p = blc_p->bl_nxt; if (blc_p->bl_cnt == MAXBLENT) { blc_p->bl_nxt = (struct badsec_lst *)calloc(1, BADSLSZ); if (!blc_p->bl_nxt) { (void) fprintf(stderr, "Unable to allocate memory for additional bad sectors\n"); return (-1); } blc_p = blc_p->bl_nxt; } blc_p->bl_sec[blc_p->bl_cnt++] = (uint_t)bn; gbadsl_chain_cnt++; (void) updatebadsec(dpart, 0); status = put_alts_slice(); /* clear out the bad sector list chains that were generated */ if (badsl_chain) { if (badsl_chain->bl_nxt == NULL) { free(badsl_chain); } else { for (blc_p = badsl_chain; blc_p; ) { blc_p_nxt = blc_p->bl_nxt; free(blc_p); blc_p = blc_p_nxt; } } badsl_chain = NULL; badsl_chain_cnt = 0; } if (gbadsl_chain) { if (gbadsl_chain->bl_nxt == NULL) { free(gbadsl_chain); } else { for (blc_p = gbadsl_chain; blc_p; ) { blc_p_nxt = blc_p->bl_nxt; free(blc_p); blc_p = blc_p_nxt; } } gbadsl_chain = NULL; gbadsl_chain_cnt = 0; } return (status); } int ata_wr_cur(struct defect_list *list) { int status; int sec_count; int x; struct badsec_lst *blc_p; struct badsec_lst *blc_p_nxt; struct defect_entry *dlist; if (list->header.magicno != (uint_t)DEFECT_MAGIC) return (-1); sec_count = list->header.count; dlist = list->list; (void) get_alts_slice(); for (x = 0; x < sec_count; x++) { /* test for unsupported list format */ if ((dlist->bfi != UNKNOWN) || (dlist->nbits != UNKNOWN)) { (void) fprintf(stderr, "BFI unsuported format for bad sectors\n"); return (-1); } if (!gbadsl_chain) { blc_p = (struct badsec_lst *)calloc(1, BADSLSZ); if (!blc_p) { (void) fprintf(stderr, "Unable to allocate memory for additional bad sectors\n"); return (-1); } gbadsl_chain = blc_p; } for (blc_p = gbadsl_chain; blc_p->bl_nxt; ) blc_p = blc_p->bl_nxt; if (blc_p->bl_cnt == MAXBLENT) { blc_p->bl_nxt = (struct badsec_lst *)calloc(1, BADSLSZ); if (!blc_p->bl_nxt) { (void) fprintf(stderr, "Unable to allocate memory for additional bad sectors\n"); return (-1); } blc_p = blc_p->bl_nxt; } blc_p->bl_sec[blc_p->bl_cnt++] = (uint_t)chs2bn(dlist->cyl, dlist->head, dlist->sect); gbadsl_chain_cnt++; dlist++; } (void) updatebadsec(dpart, 0); status = put_alts_slice(); /* clear out the bad sector list chains that were generated */ if (badsl_chain) { if (badsl_chain->bl_nxt == NULL) { free(badsl_chain); } else { for (blc_p = badsl_chain; blc_p; ) { blc_p_nxt = blc_p->bl_nxt; free(blc_p); blc_p = blc_p_nxt; } } badsl_chain = NULL; badsl_chain_cnt = 0; } if (gbadsl_chain) { if (gbadsl_chain->bl_nxt == NULL) { free(gbadsl_chain); } else { for (blc_p = gbadsl_chain; blc_p; ) { blc_p_nxt = blc_p->bl_nxt; free(blc_p); blc_p = blc_p_nxt; } } gbadsl_chain = NULL; gbadsl_chain_cnt = 0; } return (status); } #endif /* defined(i386) */ /* * 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 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _CTLR_ATA_H #define _CTLR_ATA_H #ifdef __cplusplus extern "C" { #endif #include /* * Rounded parameter, as returned in Extended Sense information */ #define ROUNDED_PARAMETER 0x37 /* * Convert a three-byte triplet into an int */ #define TRIPLET(u, m, l) ((int)((((u))&0xff<<16) + \ (((m)&0xff)<<8) + (l&0xff))) #if defined(i386) extern diskaddr_t altsec_offset; /* Alternate sector offset */ #endif /* defined(i386) */ /* * Local prototypes for ANSI C compilers */ #if defined(i386) #define _STATIC #else #define _STATIC static #endif /* defined(i386) */ _STATIC int ata_rdwr(int, int, diskaddr_t, int, caddr_t, int, int *); int ata_ex_man(struct defect_list *); int ata_ex_grown(struct defect_list *); int ata_read_defect_data(struct defect_list *, int); int apply_chg_list(int, int, uchar_t *, uchar_t *, struct chg_list *); #ifdef __cplusplus } #endif #endif /* _CTLR_ATA_H */ /* * 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 (c) 1991, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2016 by Delphix. All rights reserved. */ /* * This file contains the routines for embedded scsi disks */ #include "global.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "startup.h" #include "scsi_com.h" #include "misc.h" #include "ctlr_scsi.h" #include "analyze.h" #include "param.h" #include "io.h" #ifndef DAD_MODE_CACHE_CCS #define DAD_MODE_CACHE_CCS 0x38 #endif /* DAD_MODE_CACHE_CCS */ /* format defect header bits */ #define FDH_FOV 0x80 #define FDH_IMMED 0x02 #define SENSE_LEN 20 #define RETRY_DELAY 5 #define PROGRESS_INDICATION_BASE 65536 static int scsi_format(uint64_t, uint64_t, struct defect_list *); static int scsi_raw_format(void); static int scsi_ms_page8(int); static int scsi_ms_page38(int); static void scsi_convert_list_to_new(struct defect_list *, struct scsi_defect_list *, int); static char *scsi_find_command_name(uint_t); static int chg_list_affects_page(struct chg_list *, int); static void scsi_printerr(struct uscsi_cmd *, struct scsi_extended_sense *, int); static diskaddr_t scsi_extract_sense_info_descr(struct scsi_descr_sense_hdr *sdsp, int rqlen); static void scsi_print_extended_sense(struct scsi_extended_sense *, int); static void scsi_print_descr_sense(struct scsi_descr_sense_hdr *, int); static int test_until_ready(int fd); static int uscsi_reserve_release(int, int); static int check_support_for_defects(void); static int scsi_format_without_defects(void); static int scsi_ms_page1(int); static int scsi_ms_page2(int); static int scsi_ms_page3(int); static int scsi_ms_page4(int); static int scsi_repair(uint64_t, int); static int scsi_read_defect_data(struct defect_list *, int); static int scsi_ck_format(void); struct ctlr_ops scsiops = { scsi_rdwr, scsi_ck_format, scsi_format, scsi_ex_man, scsi_ex_cur, scsi_repair, 0, }; #define SCMD_UNKNOWN 0xff /* * Names of commands. Must have SCMD_UNKNOWN at end of list. */ static struct scsi_command_name { uchar_t command; char *name; } scsi_command_names[] = { SCMD_FORMAT, "format", SCMD_READ, "read", SCMD_WRITE, "write", SCMD_READ|SCMD_GROUP1, "read", SCMD_WRITE|SCMD_GROUP1, "write", SCMD_INQUIRY, "inquiry", SCMD_MODE_SELECT, "mode select", SCMD_MODE_SENSE, "mode sense", SCMD_REASSIGN_BLOCK, "reassign block", SCMD_READ_DEFECT_LIST, "read defect list", SCMD_UNKNOWN, "unknown" }; /* * Strings for printing mode sense page control values */ static slist_t page_control_strings[] = { { "current", "", MODE_SENSE_PC_CURRENT }, { "changeable", "", MODE_SENSE_PC_CHANGEABLE }, { "default", "", MODE_SENSE_PC_DEFAULT }, { "saved", "", MODE_SENSE_PC_SAVED } }; /* * Strings for printing the mode select options */ static slist_t mode_select_strings[] = { { "", "", 0 }, { " (pf)", "", MODE_SELECT_PF }, { " (sp)", "", MODE_SELECT_SP }, { " (pf,sp)", "", MODE_SELECT_PF|MODE_SELECT_SP } }; static int scsi_format_revolutions = 5; static int scsi_format_timeout = 2 * 60 * 60; /* two hours */ /* * READ DEFECT DATA commands is optional as per SCSI-2 spec. * Hence check if the read_defect_data command fails with * Invalid Opcode so that we can give a more meaningful message * to the user. */ #define INVALID_OPCODE 0x20 /* * Read or write the disk. */ int scsi_rdwr(int dir, int fd, diskaddr_t blkno, int secnt, caddr_t bufaddr, int flags, int *xfercntp) { struct uscsi_cmd ucmd; union scsi_cdb cdb; int max_sectors; int rc = 0; /* * If the max xfercnt hasn't been determined start with BUF_SECTS * (currently 126 == 63K), otherwise use the xfercnt value * my caller saved from the previous invocation. */ if (xfercntp == NULL) { max_sectors = BUF_SECTS; } else if (*xfercntp == 0) { max_sectors = BUF_SECTS; *xfercntp = max_sectors; } else { max_sectors = *xfercntp; } /* * Build and execute the uscsi ioctl. We build a group0 * or group1 command as necessary, since some targets * do not support group1 commands. */ while (secnt) { int nsectors; nsectors = (max_sectors < secnt) ? max_sectors : secnt; (void) memset((char *)&ucmd, 0, sizeof (ucmd)); (void) memset((char *)&cdb, 0, sizeof (union scsi_cdb)); cdb.scc_cmd = (dir == DIR_READ) ? SCMD_READ : SCMD_WRITE; if (blkno < (2<<20) && nsectors <= 0xff) { FORMG0ADDR(&cdb, blkno); FORMG0COUNT(&cdb, nsectors); ucmd.uscsi_cdblen = CDB_GROUP0; } else { if (blkno > 0xffffffff) { FORMG4LONGADDR(&cdb, blkno); FORMG4COUNT(&cdb, nsectors); ucmd.uscsi_cdblen = CDB_GROUP4; cdb.scc_cmd |= SCMD_GROUP4; } else { FORMG1ADDR(&cdb, blkno); FORMG1COUNT(&cdb, nsectors); ucmd.uscsi_cdblen = CDB_GROUP1; cdb.scc_cmd |= SCMD_GROUP1; } } ucmd.uscsi_cdb = (caddr_t)&cdb; ucmd.uscsi_bufaddr = bufaddr; ucmd.uscsi_buflen = nsectors * cur_blksz; rc = uscsi_cmd(fd, &ucmd, flags); if (rc != 0) break; /* * check if partial DMA breakup required * if so, reduce the request size by half and retry * the last request */ if (ucmd.uscsi_resid == ucmd.uscsi_buflen) { max_sectors >>= 1; if (max_sectors <= 0) { rc = -1; break; } continue; } if (ucmd.uscsi_resid != 0) { rc = -1; break; } blkno += nsectors; secnt -= nsectors; bufaddr += nsectors * cur_blksz; } /* * If the xfercnt wasn't previously saved or if the * new value is smaller than the old value, save the * current value in my caller's save area. */ if (xfercntp != NULL && max_sectors < *xfercntp) { if (diag_msg) err_print("reducing xfercnt %d %d\n", *xfercntp, max_sectors); *xfercntp = max_sectors; } return (rc); } /* * Check to see if the disk has been formatted. * If we are able to read the first track, we conclude that * the disk has been formatted. */ static int scsi_ck_format(void) { int status; /* * Try to read the first four blocks. */ status = scsi_rdwr(DIR_READ, cur_file, (diskaddr_t)0, 4, (caddr_t)cur_buf, F_SILENT, NULL); return (!status); } /* * Format the disk, the whole disk, and nothing but the disk. */ static int scsi_format(uint64_t start __unused, uint64_t end, struct defect_list *list) { struct uscsi_cmd ucmd; union scsi_cdb cdb; int status; int flag; char rawbuf[MAX_MODE_SENSE_SIZE]; struct scsi_inquiry *inq; uint8_t fmt_prot_info; uint8_t prot_field_usage; uint8_t param_long_list = 1; uint8_t fmt_long_param_header[8]; /* * Determine if the target appears to be SCSI-2 * compliant. We handle mode sense/mode selects * a little differently, depending upon CCS/SCSI-2 */ if (uscsi_inquiry(cur_file, rawbuf, sizeof (rawbuf))) { err_print("Inquiry failed\n"); return (-1); } inq = (struct scsi_inquiry *)rawbuf; flag = (inq->inq_rdf == RDF_SCSI2); /* * Reserve the scsi disk before performing mode select and * format operations. This will keep other hosts, if any, from * touching the disk while we are here. */ if (uscsi_reserve_release(cur_file, SCMD_RESERVE)) { err_print("Reserve failed\n"); return (-1); } /* * Set up the various SCSI parameters specified before * formatting the disk. Each routine handles the * parameters relevant to a particular page. * If no parameters are specified for a page, there's * no need to do anything. Otherwise, issue a mode * sense for that page. If a specified parameter * differs from the drive's default value, and that * parameter is not fixed, then issue a mode select to * set the default value for the disk as specified * in format.dat. */ if (scsi_ms_page1(flag) || scsi_ms_page2(flag) || scsi_ms_page4(flag) || scsi_ms_page38(flag) || scsi_ms_page8(flag) || scsi_ms_page3(flag)) { (void) uscsi_reserve_release(cur_file, SCMD_RELEASE); return (-1); } /* * If we're debugging the drive, dump every page * the device supports, for thorough analysis. */ if (option_msg && diag_msg) { (void) scsi_dump_mode_sense_pages(MODE_SENSE_PC_DEFAULT); (void) scsi_dump_mode_sense_pages(MODE_SENSE_PC_CURRENT); (void) scsi_dump_mode_sense_pages(MODE_SENSE_PC_SAVED); (void) scsi_dump_mode_sense_pages(MODE_SENSE_PC_CHANGEABLE); err_print("\n"); } /* * Determine the FMTPINFO field in format cdb, and the * PROTECTION FIELD USAGE in the long parameter list, via * the protection type input by users. */ switch (prot_type) { case PROT_TYPE_0: fmt_prot_info = 0x00; prot_field_usage = 0x00; break; case PROT_TYPE_1: fmt_prot_info = 0x02; prot_field_usage = 0x00; break; case PROT_TYPE_2: fmt_prot_info = 0x03; prot_field_usage = 0x00; break; case PROT_TYPE_3: fmt_prot_info = 0x03; prot_field_usage = 0x01; break; default: fmt_print("invalid protection type\n"); return (-1); } /* * Construct the uscsi format ioctl. The form depends * upon the defect list the user extracted. If they * extracted the "original" list, we format with only * the P (manufacturer's defect) list. Otherwise, we * format with both the P and the G (grown) list. * To format with the P and G list, we set the fmtData * bit, and send an empty list. To format with the * P list only, we also set the cmpLst bit, meaning * that the (empty) list we send down is the complete * G list, thereby discarding the old G list.. */ (void) memset((char *)&ucmd, 0, sizeof (ucmd)); (void) memset((char *)&cdb, 0, sizeof (union scsi_cdb)); cdb.scc_cmd = SCMD_FORMAT; ucmd.uscsi_cdb = (caddr_t)&cdb; ucmd.uscsi_cdblen = CDB_GROUP0; cdb.cdb_opaque[1] = FPB_DATA; /* * Use the long parameter header in format command, * and set the FMTPINFO field., when type 1, 2, 3. */ cdb.cdb_opaque[1] |= (param_long_list << 5) | (fmt_prot_info << 6); (void) memset((char *)fmt_long_param_header, 0, sizeof (fmt_long_param_header)); /* * Set the PROTECTION FIELD USAGE field in the long * parameter list header, which combines with FMTINFO to * determine the protection type. * The PROTECTION INTERVAL EXPONET field is set default 0. * So only one protection information interval is used * in type 1, 2, 3. */ fmt_long_param_header[0] = prot_field_usage; fmt_long_param_header[1] = FDH_FOV | FDH_IMMED; ucmd.uscsi_bufaddr = (caddr_t)fmt_long_param_header; ucmd.uscsi_buflen = sizeof (fmt_long_param_header); if ((list->list != NULL) && ((list->flags & LIST_PGLIST) == 0)) { /* * No G list. The empty list we send down * is the complete list. */ cdb.cdb_opaque[1] |= FPB_CMPLT; } /* * Issue the format ioctl */ fmt_print("Formatting...\n"); (void) fflush(stdout); status = uscsi_cmd(cur_file, &ucmd, (option_msg && diag_msg) ? F_NORMAL : F_SILENT); /* check if format with immed was successfully accepted */ if (status == 0) { /* immed accepted poll to completion */ status = test_until_ready(cur_file); } else { /* clear FOV and try again */ (void) memset((char *)fmt_long_param_header, 0, sizeof (fmt_long_param_header)); fmt_long_param_header[0] = prot_field_usage; fmt_long_param_header[1] = FDH_IMMED; status = uscsi_cmd(cur_file, &ucmd, (option_msg && diag_msg) ? F_NORMAL : F_SILENT); if (status == 0) { /* immed accepted, poll for progress */ status = test_until_ready(cur_file); } else { /* * clear defect header and try basecase format * command will hang until format complete */ (void) memset((char *)fmt_long_param_header, 0, sizeof (fmt_long_param_header)); fmt_long_param_header[0] = prot_field_usage; status = uscsi_cmd(cur_file, &ucmd, (option_msg && diag_msg) ? F_NORMAL : F_SILENT); } } /* format failure check */ if (status != 0) { /* * formatting failed with fmtdata = 1. * Check if defects list command is supported, if it * is not supported then use fmtdata = 0. * From SCSI Spec * A FmtData bit of zero indicates, the * source of defect information is not specified. * else * proceed to format using with mode selects. */ if (!(check_support_for_defects())) { status = scsi_format_without_defects(); } if (status != 0) { fmt_print("Format failed\n"); status = scsi_raw_format(); } } (void) uscsi_reserve_release(cur_file, SCMD_RELEASE); return (status); } /* * Format without any of the standard mode selects ignoring Grown defects list. */ static int scsi_raw_format(void) { struct uscsi_cmd ucmd; union scsi_cdb cdb; struct scsi_defect_hdr defect_hdr; int status; fmt_print("\n" "Retry of formatting operation without any of the standard\n" "mode selects and ignoring disk's Grown Defects list. The\n" "disk may be able to be reformatted this way if an earlier\n" "formatting operation was interrupted by a power failure or\n" "SCSI bus reset. The Grown Defects list will be recreated\n" "by format verification and surface analysis.\n\n"); if (check("Retry format without mode selects and Grown Defects list") != 0) { return (-1); } /* * Construct the uscsi format ioctl. * To format with the P and G list, we set the fmtData * and cmpLst bits to zero. To format with just the * P list, we set the fmtData bit (meaning that we will * send down a defect list in the data phase) and the * cmpLst bit (meaning that the list we send is the * complete G list), and a defect list header with * a defect list length of zero. */ (void) memset((char *)&ucmd, 0, sizeof (ucmd)); (void) memset((char *)&cdb, 0, sizeof (union scsi_cdb)); (void) memset((char *)&defect_hdr, 0, sizeof (defect_hdr)); cdb.scc_cmd = SCMD_FORMAT; ucmd.uscsi_cdb = (caddr_t)&cdb; ucmd.uscsi_cdblen = CDB_GROUP0; /* No G list. Send empty defect list to replace it */ cdb.cdb_opaque[1] = FPB_DATA | FPB_CMPLT | FPB_BFI; ucmd.uscsi_bufaddr = (caddr_t)&defect_hdr; ucmd.uscsi_buflen = sizeof (defect_hdr); defect_hdr.descriptor = FDH_FOV | FDH_IMMED; /* * Issue the format ioctl */ fmt_print("Formatting...\n"); (void) fflush(stdout); status = uscsi_cmd(cur_file, &ucmd, F_NORMAL); /* check if format with immed was successfully accepted */ if (status == 0) { /* immed accepted pool to completion */ status = test_until_ready(cur_file); } else { /* clear defect header and try basecase format */ (void) memset((char *)&defect_hdr, 0, sizeof (defect_hdr)); status = uscsi_cmd(cur_file, &ucmd, F_NORMAL); } /* fmt_print(status ? "Format failed\n\n" : "Format ok\n\n"); */ return (status); } /* * Estimate the time required for format operation (See 1163770). * format time = (5_revs * p4_heads * p4_cylinders) / p4_rpm * 5 revolutions (correspond to format_time keyword in format.dat file) are: * 1 rev. for positioning * 2 rev. for writing the track * 1 rev. for positioning * 1 rev. for cerifying the data integrity of the track * The return value is a good estimate on the formatting time in minutes. * Caller should add 50% margin to cover defect management overhead. */ int scsi_format_time(void) { struct mode_geometry *page4; struct scsi_ms_header header; int status; int p4_cylinders, p4_heads, p4_rpm; int length; int format_time; union { struct mode_geometry page4; char rawbuf[MAX_MODE_SENSE_SIZE]; } u_page4; page4 = &u_page4.page4; (void) memset(&u_page4, 0, sizeof (u_page4)); /* * Issue a mode sense to determine the default parameters * If it fail, try to use the saved or current instead. */ status = uscsi_mode_sense(cur_file, DAD_MODE_GEOMETRY, MODE_SENSE_PC_DEFAULT, (caddr_t)page4, MAX_MODE_SENSE_SIZE, &header); if (status) { status = uscsi_mode_sense(cur_file, DAD_MODE_GEOMETRY, MODE_SENSE_PC_SAVED, (caddr_t)page4, MAX_MODE_SENSE_SIZE, &header); } if (status) { status = uscsi_mode_sense(cur_file, DAD_MODE_GEOMETRY, MODE_SENSE_PC_CURRENT, (caddr_t)page4, MAX_MODE_SENSE_SIZE, &header); } if (status) { return (0); } /* * We only need the common subset between the CCS * and SCSI-2 structures, so we can treat both * cases identically. */ length = MODESENSE_PAGE_LEN(page4); if (length < MIN_PAGE4_LEN) { return (0); } page4->rpm = BE_16(page4->rpm); p4_cylinders = (page4->cyl_ub << 16) + (page4->cyl_mb << 8) + page4->cyl_lb; p4_heads = page4->heads; p4_rpm = page4->rpm; /* * Some drives report 0 for page4->rpm, adjust it to AVG_RPM, 3600. */ if (p4_rpm < MIN_RPM || p4_rpm > MAX_RPM) { err_print("Mode sense page(4) reports rpm value as %d," " adjusting it to %d\n", p4_rpm, AVG_RPM); p4_rpm = AVG_RPM; } if (p4_cylinders <= 0 || p4_heads <= 0) return (0); format_time = ((scsi_format_revolutions * p4_heads * p4_cylinders) + p4_rpm) / p4_rpm; if (option_msg && diag_msg) { err_print(" pcyl: %d\n", p4_cylinders); err_print(" heads: %d\n", p4_heads); err_print(" rpm: %d\n", p4_rpm); err_print("format_time: %d minutes\n", format_time); } return (format_time); } /* * Check disk error recovery parameters via mode sense. * Issue a mode select if we need to change something. */ static int scsi_ms_page1(int scsi2_flag __unused) { struct mode_err_recov *page1; struct mode_err_recov *fixed; struct scsi_ms_header header; struct scsi_ms_header fixed_hdr; int status; int tmp1, tmp2; int flag; int length; int sp_flags; union { struct mode_err_recov page1; char rawbuf[MAX_MODE_SENSE_SIZE]; } u_page1, u_fixed; page1 = &u_page1.page1; fixed = &u_fixed.page1; /* * If debugging, issue mode senses on the default and * current values. */ if (option_msg && diag_msg) { (void) uscsi_mode_sense(cur_file, DAD_MODE_ERR_RECOV, MODE_SENSE_PC_DEFAULT, (caddr_t)page1, MAX_MODE_SENSE_SIZE, &header); (void) uscsi_mode_sense(cur_file, DAD_MODE_ERR_RECOV, MODE_SENSE_PC_CURRENT, (caddr_t)page1, MAX_MODE_SENSE_SIZE, &header); } /* * Issue a mode sense to determine the saved parameters * If the saved values fail, use the current instead. */ status = uscsi_mode_sense(cur_file, DAD_MODE_ERR_RECOV, MODE_SENSE_PC_SAVED, (caddr_t)page1, MAX_MODE_SENSE_SIZE, &header); if (status) { status = uscsi_mode_sense(cur_file, DAD_MODE_ERR_RECOV, MODE_SENSE_PC_CURRENT, (caddr_t)page1, MAX_MODE_SENSE_SIZE, &header); if (status) { return (0); } } /* * We only need the common subset between the CCS * and SCSI-2 structures, so we can treat both * cases identically. Whatever the drive gives * us, we return to the drive in the mode select, * delta'ed by whatever we want to change. */ length = MODESENSE_PAGE_LEN(page1); if (length < MIN_PAGE1_LEN) { return (0); } /* * Ask for changeable parameters. */ status = uscsi_mode_sense(cur_file, DAD_MODE_ERR_RECOV, MODE_SENSE_PC_CHANGEABLE, (caddr_t)fixed, MAX_MODE_SENSE_SIZE, &fixed_hdr); if (status || MODESENSE_PAGE_LEN(fixed) < MIN_PAGE1_LEN) { return (0); } /* * We need to issue a mode select only if one or more * parameters need to be changed, and those parameters * are flagged by the drive as changeable. */ flag = 0; tmp1 = page1->read_retry_count; tmp2 = page1->write_retry_count; if (cur_dtype->dtype_options & SUP_READ_RETRIES && fixed->read_retry_count != 0) { flag |= (page1->read_retry_count != cur_dtype->dtype_read_retries); page1->read_retry_count = cur_dtype->dtype_read_retries; } if (length > 8) { if (cur_dtype->dtype_options & SUP_WRITE_RETRIES && fixed->write_retry_count != 0) { flag |= (page1->write_retry_count != cur_dtype->dtype_write_retries); page1->write_retry_count = cur_dtype->dtype_write_retries; } } /* * Report any changes so far... */ if (flag && option_msg) { fmt_print("PAGE 1: read retries= %d (%d) " " write retries= %d (%d)\n", page1->read_retry_count, tmp1, page1->write_retry_count, tmp2); } /* * Apply any changes requested via the change list method */ flag |= apply_chg_list(DAD_MODE_ERR_RECOV, length, (uchar_t *)page1, (uchar_t *)fixed, cur_dtype->dtype_chglist); /* * If no changes required, do not issue a mode select */ if (flag == 0) { return (0); } /* * We always want to set the Page Format bit for mode * selects. Set the Save Page bit if the drive indicates * that it can save this page via the mode sense. */ sp_flags = MODE_SELECT_PF; if (page1->mode_page.ps) { sp_flags |= MODE_SELECT_SP; } page1->mode_page.ps = 0; header.mode_header.length = 0; header.mode_header.device_specific = 0; status = uscsi_mode_select(cur_file, DAD_MODE_ERR_RECOV, sp_flags, (caddr_t)page1, length, &header); if (status && (sp_flags & MODE_SELECT_SP)) { /* If failed, try not saving mode select params. */ sp_flags &= ~MODE_SELECT_SP; status = uscsi_mode_select(cur_file, DAD_MODE_ERR_RECOV, sp_flags, (caddr_t)page1, length, &header); } if (status && option_msg) { err_print("Warning: Using default error recovery " "parameters.\n\n"); } /* * If debugging, issue mode senses on the current and * saved values, so we can see the result of the mode * selects. */ if (option_msg && diag_msg) { (void) uscsi_mode_sense(cur_file, DAD_MODE_ERR_RECOV, MODE_SENSE_PC_CURRENT, (caddr_t)page1, MAX_MODE_SENSE_SIZE, &header); (void) uscsi_mode_sense(cur_file, DAD_MODE_ERR_RECOV, MODE_SENSE_PC_SAVED, (caddr_t)page1, MAX_MODE_SENSE_SIZE, &header); } return (0); } /* * Check disk disconnect/reconnect parameters via mode sense. * Issue a mode select if we need to change something. */ static int scsi_ms_page2(int scsi2_flag __unused) { struct mode_disco_reco *page2; struct mode_disco_reco *fixed; struct scsi_ms_header header; struct scsi_ms_header fixed_hdr; int status; int flag; int length; int sp_flags; union { struct mode_disco_reco page2; char rawbuf[MAX_MODE_SENSE_SIZE]; } u_page2, u_fixed; page2 = &u_page2.page2; fixed = &u_fixed.page2; /* * If debugging, issue mode senses on the default and * current values. */ if (option_msg && diag_msg) { (void) uscsi_mode_sense(cur_file, MODEPAGE_DISCO_RECO, MODE_SENSE_PC_DEFAULT, (caddr_t)page2, MAX_MODE_SENSE_SIZE, &header); (void) uscsi_mode_sense(cur_file, MODEPAGE_DISCO_RECO, MODE_SENSE_PC_CURRENT, (caddr_t)page2, MAX_MODE_SENSE_SIZE, &header); } /* * Issue a mode sense to determine the saved parameters * If the saved values fail, use the current instead. */ status = uscsi_mode_sense(cur_file, MODEPAGE_DISCO_RECO, MODE_SENSE_PC_SAVED, (caddr_t)page2, MAX_MODE_SENSE_SIZE, &header); if (status) { status = uscsi_mode_sense(cur_file, MODEPAGE_DISCO_RECO, MODE_SENSE_PC_CURRENT, (caddr_t)page2, MAX_MODE_SENSE_SIZE, &header); if (status) { return (0); } } /* * We only need the common subset between the CCS * and SCSI-2 structures, so we can treat both * cases identically. Whatever the drive gives * us, we return to the drive in the mode select, * delta'ed by whatever we want to change. */ length = MODESENSE_PAGE_LEN(page2); if (length < MIN_PAGE2_LEN) { return (0); } /* * Ask for changeable parameters. */ status = uscsi_mode_sense(cur_file, MODEPAGE_DISCO_RECO, MODE_SENSE_PC_CHANGEABLE, (caddr_t)fixed, MAX_MODE_SENSE_SIZE, &fixed_hdr); if (status || MODESENSE_PAGE_LEN(fixed) < MIN_PAGE2_LEN) { return (0); } /* * We need to issue a mode select only if one or more * parameters need to be changed, and those parameters * are flagged by the drive as changeable. */ flag = 0; /* * Apply any changes requested via the change list method */ flag |= apply_chg_list(MODEPAGE_DISCO_RECO, length, (uchar_t *)page2, (uchar_t *)fixed, cur_dtype->dtype_chglist); /* * If no changes required, do not issue a mode select */ if (flag == 0) { return (0); } /* * We always want to set the Page Format bit for mode * selects. Set the Save Page bit if the drive indicates * that it can save this page via the mode sense. */ sp_flags = MODE_SELECT_PF; if (page2->mode_page.ps) { sp_flags |= MODE_SELECT_SP; } page2->mode_page.ps = 0; header.mode_header.length = 0; header.mode_header.device_specific = 0; status = uscsi_mode_select(cur_file, MODEPAGE_DISCO_RECO, MODE_SELECT_SP, (caddr_t)page2, length, &header); if (status && (sp_flags & MODE_SELECT_SP)) { /* If failed, try not saving mode select params. */ sp_flags &= ~MODE_SELECT_SP; status = uscsi_mode_select(cur_file, MODEPAGE_DISCO_RECO, sp_flags, (caddr_t)page2, length, &header); } if (status && option_msg) { err_print("Warning: Using default .\n\n"); } /* * If debugging, issue mode senses on the current and * saved values, so we can see the result of the mode * selects. */ if (option_msg && diag_msg) { (void) uscsi_mode_sense(cur_file, MODEPAGE_DISCO_RECO, MODE_SENSE_PC_CURRENT, (caddr_t)page2, MAX_MODE_SENSE_SIZE, &header); (void) uscsi_mode_sense(cur_file, MODEPAGE_DISCO_RECO, MODE_SENSE_PC_SAVED, (caddr_t)page2, MAX_MODE_SENSE_SIZE, &header); } return (0); } /* * Check disk format parameters via mode sense. * Issue a mode select if we need to change something. */ static int scsi_ms_page3(int scsi2_flag __unused) { struct mode_format *page3; struct mode_format *fixed; struct scsi_ms_header header; struct scsi_ms_header fixed_hdr; int status; int tmp1, tmp2, tmp3; int tmp4, tmp5, tmp6; int flag; int length; int sp_flags; union { struct mode_format page3; char rawbuf[MAX_MODE_SENSE_SIZE]; } u_page3, u_fixed; page3 = &u_page3.page3; fixed = &u_fixed.page3; /* * If debugging, issue mode senses on the default and * current values. */ if (option_msg && diag_msg) { (void) uscsi_mode_sense(cur_file, DAD_MODE_FORMAT, MODE_SENSE_PC_DEFAULT, (caddr_t)page3, MAX_MODE_SENSE_SIZE, &header); (void) uscsi_mode_sense(cur_file, DAD_MODE_FORMAT, MODE_SENSE_PC_CURRENT, (caddr_t)page3, MAX_MODE_SENSE_SIZE, &header); } /* * Issue a mode sense to determine the saved parameters * If the saved values fail, use the current instead. */ status = uscsi_mode_sense(cur_file, DAD_MODE_FORMAT, MODE_SENSE_PC_SAVED, (caddr_t)page3, MAX_MODE_SENSE_SIZE, &header); if (status) { status = uscsi_mode_sense(cur_file, DAD_MODE_FORMAT, MODE_SENSE_PC_CURRENT, (caddr_t)page3, MAX_MODE_SENSE_SIZE, &header); if (status) { return (0); } } /* * We only need the common subset between the CCS * and SCSI-2 structures, so we can treat both * cases identically. Whatever the drive gives * us, we return to the drive in the mode select, * delta'ed by whatever we want to change. */ length = MODESENSE_PAGE_LEN(page3); if (length < MIN_PAGE3_LEN) { return (0); } /* * Ask for changeable parameters. */ status = uscsi_mode_sense(cur_file, DAD_MODE_FORMAT, MODE_SENSE_PC_CHANGEABLE, (caddr_t)fixed, MAX_MODE_SENSE_SIZE, &fixed_hdr); if (status || MODESENSE_PAGE_LEN(fixed) < MIN_PAGE3_LEN) { return (0); } /* * We need to issue a mode select only if one or more * parameters need to be changed, and those parameters * are flagged by the drive as changeable. */ tmp1 = page3->track_skew; tmp2 = page3->cylinder_skew; tmp3 = page3->sect_track; tmp4 = page3->tracks_per_zone; tmp5 = page3->alt_tracks_vol; tmp6 = page3->alt_sect_zone; flag = (page3->data_bytes_sect != cur_blksz); page3->data_bytes_sect = cur_blksz; flag |= (page3->interleave != 1); page3->interleave = 1; if (cur_dtype->dtype_options & SUP_CYLSKEW && fixed->cylinder_skew != 0) { flag |= (page3->cylinder_skew != cur_dtype->dtype_cyl_skew); page3->cylinder_skew = cur_dtype->dtype_cyl_skew; } if (cur_dtype->dtype_options & SUP_TRKSKEW && fixed->track_skew != 0) { flag |= (page3->track_skew != cur_dtype->dtype_trk_skew); page3->track_skew = cur_dtype->dtype_trk_skew; } if (cur_dtype->dtype_options & SUP_PSECT && fixed->sect_track != 0) { flag |= (page3->sect_track != psect); page3->sect_track = (ushort_t)psect; } if (cur_dtype->dtype_options & SUP_TRKS_ZONE && fixed->tracks_per_zone != 0) { flag |= (page3->tracks_per_zone != cur_dtype->dtype_trks_zone); page3->tracks_per_zone = cur_dtype->dtype_trks_zone; } if (cur_dtype->dtype_options & SUP_ASECT && fixed->alt_sect_zone != 0) { flag |= (page3->alt_sect_zone != cur_dtype->dtype_asect); page3->alt_sect_zone = cur_dtype->dtype_asect; } if (cur_dtype->dtype_options & SUP_ATRKS && fixed->alt_tracks_vol != 0) { flag |= (page3->alt_tracks_vol != cur_dtype->dtype_atrks); page3->alt_tracks_vol = cur_dtype->dtype_atrks; } /* * Notify user of any changes so far */ if (flag && option_msg) { fmt_print("PAGE 3: trk skew= %d (%d) cyl skew= %d (%d) ", page3->track_skew, tmp1, page3->cylinder_skew, tmp2); fmt_print("sects/trk= %d (%d)\n", page3->sect_track, tmp3); fmt_print(" trks/zone= %d (%d) alt trks= %d (%d) ", page3->tracks_per_zone, tmp4, page3->alt_tracks_vol, tmp5); fmt_print("alt sects/zone= %d (%d)\n", page3->alt_sect_zone, tmp6); } /* * Apply any changes requested via the change list method */ flag |= apply_chg_list(DAD_MODE_FORMAT, length, (uchar_t *)page3, (uchar_t *)fixed, cur_dtype->dtype_chglist); /* * If no changes required, do not issue a mode select */ if (flag == 0) { return (0); } /* * Issue a mode select */ /* * We always want to set the Page Format bit for mode * selects. Set the Save Page bit if the drive indicates * that it can save this page via the mode sense. */ sp_flags = MODE_SELECT_PF; if (page3->mode_page.ps) { sp_flags |= MODE_SELECT_SP; } page3->mode_page.ps = 0; header.mode_header.length = 0; header.mode_header.device_specific = 0; status = uscsi_mode_select(cur_file, DAD_MODE_FORMAT, MODE_SELECT_SP, (caddr_t)page3, length, &header); if (status && (sp_flags & MODE_SELECT_SP)) { /* If failed, try not saving mode select params. */ sp_flags &= ~MODE_SELECT_SP; status = uscsi_mode_select(cur_file, DAD_MODE_FORMAT, sp_flags, (caddr_t)page3, length, &header); } if (status && option_msg) { err_print("Warning: Using default drive format parameters.\n"); err_print("Warning: Drive format may not be correct.\n\n"); } /* * If debugging, issue mode senses on the current and * saved values, so we can see the result of the mode * selects. */ if (option_msg && diag_msg) { (void) uscsi_mode_sense(cur_file, DAD_MODE_FORMAT, MODE_SENSE_PC_CURRENT, (caddr_t)page3, MAX_MODE_SENSE_SIZE, &header); (void) uscsi_mode_sense(cur_file, DAD_MODE_FORMAT, MODE_SENSE_PC_SAVED, (caddr_t)page3, MAX_MODE_SENSE_SIZE, &header); } return (0); } /* * Check disk geometry parameters via mode sense. * Issue a mode select if we need to change something. */ static int scsi_ms_page4(int scsi2_flag __unused) { struct mode_geometry *page4; struct mode_geometry *fixed; struct scsi_ms_header header; struct scsi_ms_header fixed_hdr; int status; int tmp1, tmp2; int flag; int length; int sp_flags; union { struct mode_geometry page4; char rawbuf[MAX_MODE_SENSE_SIZE]; } u_page4, u_fixed; page4 = &u_page4.page4; fixed = &u_fixed.page4; /* * If debugging, issue mode senses on the default and * current values. */ if (option_msg && diag_msg) { (void) uscsi_mode_sense(cur_file, DAD_MODE_GEOMETRY, MODE_SENSE_PC_DEFAULT, (caddr_t)page4, MAX_MODE_SENSE_SIZE, &header); (void) uscsi_mode_sense(cur_file, DAD_MODE_GEOMETRY, MODE_SENSE_PC_CURRENT, (caddr_t)page4, MAX_MODE_SENSE_SIZE, &header); } /* * Issue a mode sense to determine the saved parameters * If the saved values fail, use the current instead. */ status = uscsi_mode_sense(cur_file, DAD_MODE_GEOMETRY, MODE_SENSE_PC_SAVED, (caddr_t)page4, MAX_MODE_SENSE_SIZE, &header); if (status) { status = uscsi_mode_sense(cur_file, DAD_MODE_GEOMETRY, MODE_SENSE_PC_CURRENT, (caddr_t)page4, MAX_MODE_SENSE_SIZE, &header); if (status) { return (0); } } /* * We only need the common subset between the CCS * and SCSI-2 structures, so we can treat both * cases identically. Whatever the drive gives * us, we return to the drive in the mode select, * delta'ed by whatever we want to change. */ length = MODESENSE_PAGE_LEN(page4); if (length < MIN_PAGE4_LEN) { return (0); } /* * Ask for changeable parameters. */ status = uscsi_mode_sense(cur_file, DAD_MODE_GEOMETRY, MODE_SENSE_PC_CHANGEABLE, (caddr_t)fixed, MAX_MODE_SENSE_SIZE, &fixed_hdr); if (status || MODESENSE_PAGE_LEN(fixed) < MIN_PAGE4_LEN) { return (0); } /* * We need to issue a mode select only if one or more * parameters need to be changed, and those parameters * are flagged by the drive as changeable. */ tmp1 = (page4->cyl_ub << 16) + (page4->cyl_mb << 8) + page4->cyl_lb; tmp2 = page4->heads; flag = 0; if ((cur_dtype->dtype_options & SUP_PHEAD) && fixed->heads != 0) { flag |= (page4->heads != phead); page4->heads = phead; } /* * Notify user of changes so far */ if (flag && option_msg) { fmt_print("PAGE 4: cylinders= %d heads= %d (%d)\n", tmp1, page4->heads, tmp2); } /* * Apply any changes requested via the change list method */ flag |= apply_chg_list(DAD_MODE_GEOMETRY, length, (uchar_t *)page4, (uchar_t *)fixed, cur_dtype->dtype_chglist); /* * If no changes required, do not issue a mode select */ if (flag == 0) { return (0); } /* * Issue a mode select */ /* * We always want to set the Page Format bit for mode * selects. Set the Save Page bit if the drive indicates * that it can save this page via the mode sense. */ sp_flags = MODE_SELECT_PF; if (page4->mode_page.ps) { sp_flags |= MODE_SELECT_SP; } page4->mode_page.ps = 0; header.mode_header.length = 0; header.mode_header.device_specific = 0; status = uscsi_mode_select(cur_file, DAD_MODE_GEOMETRY, MODE_SELECT_SP, (caddr_t)page4, length, &header); if (status && (sp_flags & MODE_SELECT_SP)) { /* If failed, try not saving mode select params. */ sp_flags &= ~MODE_SELECT_SP; status = uscsi_mode_select(cur_file, DAD_MODE_GEOMETRY, sp_flags, (caddr_t)page4, length, &header); } if (status && option_msg) { err_print("Warning: Using default drive geometry.\n\n"); } /* * If debugging, issue mode senses on the current and * saved values, so we can see the result of the mode * selects. */ if (option_msg && diag_msg) { (void) uscsi_mode_sense(cur_file, DAD_MODE_GEOMETRY, MODE_SENSE_PC_CURRENT, (caddr_t)page4, MAX_MODE_SENSE_SIZE, &header); (void) uscsi_mode_sense(cur_file, DAD_MODE_GEOMETRY, MODE_SENSE_PC_SAVED, (caddr_t)page4, MAX_MODE_SENSE_SIZE, &header); } return (0); } /* * Check SCSI-2 disk cache parameters via mode sense. * Issue a mode select if we need to change something. */ static int scsi_ms_page8(int scsi2_flag __unused) { struct mode_cache *page8; struct mode_cache *fixed; struct scsi_ms_header header; struct scsi_ms_header fixed_hdr; int status; int flag; int length; int sp_flags; union { struct mode_cache page8; char rawbuf[MAX_MODE_SENSE_SIZE]; } u_page8, u_fixed; page8 = &u_page8.page8; fixed = &u_fixed.page8; /* * Only SCSI-2 devices support this page */ if (!scsi2_flag) { return (0); } /* * If debugging, issue mode senses on the default and * current values. */ if (option_msg && diag_msg) { (void) uscsi_mode_sense(cur_file, DAD_MODE_CACHE, MODE_SENSE_PC_DEFAULT, (caddr_t)page8, MAX_MODE_SENSE_SIZE, &header); (void) uscsi_mode_sense(cur_file, DAD_MODE_CACHE, MODE_SENSE_PC_CURRENT, (caddr_t)page8, MAX_MODE_SENSE_SIZE, &header); } /* * Issue a mode sense to determine the saved parameters * If the saved values fail, use the current instead. */ status = uscsi_mode_sense(cur_file, DAD_MODE_CACHE, MODE_SENSE_PC_SAVED, (caddr_t)page8, MAX_MODE_SENSE_SIZE, &header); if (status) { status = uscsi_mode_sense(cur_file, DAD_MODE_CACHE, MODE_SENSE_PC_CURRENT, (caddr_t)page8, MAX_MODE_SENSE_SIZE, &header); if (status) { return (0); } } /* * We only need the common subset between the CCS * and SCSI-2 structures, so we can treat both * cases identically. Whatever the drive gives * us, we return to the drive in the mode select, * delta'ed by whatever we want to change. */ length = MODESENSE_PAGE_LEN(page8); if (length < MIN_PAGE8_LEN) { return (0); } /* * Ask for changeable parameters. */ status = uscsi_mode_sense(cur_file, DAD_MODE_CACHE, MODE_SENSE_PC_CHANGEABLE, (caddr_t)fixed, MAX_MODE_SENSE_SIZE, &fixed_hdr); if (status || MODESENSE_PAGE_LEN(fixed) < MIN_PAGE8_LEN) { return (0); } /* * We need to issue a mode select only if one or more * parameters need to be changed, and those parameters * are flagged by the drive as changeable. */ flag = 0; /* * Apply any changes requested via the change list method */ flag |= apply_chg_list(DAD_MODE_CACHE, length, (uchar_t *)page8, (uchar_t *)fixed, cur_dtype->dtype_chglist); /* * If no changes required, do not issue a mode select */ if (flag == 0) { return (0); } /* * Issue a mode select */ /* * We always want to set the Page Format bit for mode * selects. Set the Save Page bit if the drive indicates * that it can save this page via the mode sense. */ sp_flags = MODE_SELECT_PF; if (page8->mode_page.ps) { sp_flags |= MODE_SELECT_SP; } page8->mode_page.ps = 0; header.mode_header.length = 0; header.mode_header.device_specific = 0; status = uscsi_mode_select(cur_file, DAD_MODE_CACHE, sp_flags, (caddr_t)page8, length, &header); if (status && (sp_flags & MODE_SELECT_SP)) { /* If failed, try not saving mode select params. */ sp_flags &= ~MODE_SELECT_SP; status = uscsi_mode_select(cur_file, DAD_MODE_CACHE, sp_flags, (caddr_t)page8, length, &header); } if (status && option_msg) { err_print( "Warning: Using default SCSI-2 cache parameters.\n\n"); } /* * If debugging, issue mode senses on the current and * saved values, so we can see the result of the mode * selects. */ if (option_msg && diag_msg) { (void) uscsi_mode_sense(cur_file, DAD_MODE_CACHE, MODE_SENSE_PC_CURRENT, (caddr_t)page8, MAX_MODE_SENSE_SIZE, &header); (void) uscsi_mode_sense(cur_file, DAD_MODE_CACHE, MODE_SENSE_PC_SAVED, (caddr_t)page8, MAX_MODE_SENSE_SIZE, &header); } return (0); } /* * Check CCS disk cache parameters via mode sense. * Issue a mode select if we need to change something. */ static int scsi_ms_page38(int scsi2_flag __unused) { struct mode_cache_ccs *page38; struct mode_cache_ccs *fixed; struct scsi_ms_header header; struct scsi_ms_header fixed_hdr; int status; int tmp1, tmp2, tmp3, tmp4; int flag; int length; int sp_flags; union { struct mode_cache_ccs page38; char rawbuf[MAX_MODE_SENSE_SIZE]; } u_page38, u_fixed; /* * First, determine if we need to look at page 38 at all. * Not all devices support it. */ if (((cur_dtype->dtype_options & (SUP_CACHE | SUP_PREFETCH | SUP_CACHE_MIN | SUP_CACHE_MAX)) == 0) && (!chg_list_affects_page(cur_dtype->dtype_chglist, 0x38))) { return (0); } page38 = &u_page38.page38; fixed = &u_fixed.page38; /* * If debugging, issue mode senses on the default and * current values. */ if (option_msg && diag_msg) { (void) uscsi_mode_sense(cur_file, DAD_MODE_CACHE_CCS, MODE_SENSE_PC_DEFAULT, (caddr_t)page38, MAX_MODE_SENSE_SIZE, &header); (void) uscsi_mode_sense(cur_file, DAD_MODE_CACHE_CCS, MODE_SENSE_PC_CURRENT, (caddr_t)page38, MAX_MODE_SENSE_SIZE, &header); } /* * Issue a mode sense to determine the saved parameters * If the saved values fail, use the current instead. */ status = uscsi_mode_sense(cur_file, DAD_MODE_CACHE_CCS, MODE_SENSE_PC_SAVED, (caddr_t)page38, MAX_MODE_SENSE_SIZE, &header); if (status) { status = uscsi_mode_sense(cur_file, DAD_MODE_CACHE_CCS, MODE_SENSE_PC_CURRENT, (caddr_t)page38, MAX_MODE_SENSE_SIZE, &header); if (status) { return (0); } } /* * We only need the common subset between the CCS * and SCSI-2 structures, so we can treat both * cases identically. Whatever the drive gives * us, we return to the drive in the mode select, * delta'ed by whatever we want to change. */ length = MODESENSE_PAGE_LEN(page38); if (length < MIN_PAGE38_LEN) { return (0); } /* * Ask for changeable parameters. */ status = uscsi_mode_sense(cur_file, DAD_MODE_CACHE_CCS, MODE_SENSE_PC_CHANGEABLE, (caddr_t)fixed, MAX_MODE_SENSE_SIZE, &fixed_hdr); if (status || MODESENSE_PAGE_LEN(fixed) < MIN_PAGE38_LEN) { return (0); } /* * We need to issue a mode select only if one or more * parameters need to be changed, and those parameters * are flagged by the drive as changeable. */ tmp1 = page38->mode; tmp2 = page38->threshold; tmp3 = page38->min_prefetch; tmp4 = page38->max_prefetch; flag = 0; if ((cur_dtype->dtype_options & SUP_CACHE) && (fixed->mode & cur_dtype->dtype_cache) == cur_dtype->dtype_cache) { flag |= (page38->mode != cur_dtype->dtype_cache); page38->mode = cur_dtype->dtype_cache; } if ((cur_dtype->dtype_options & SUP_PREFETCH) && (fixed->threshold & cur_dtype->dtype_threshold) == cur_dtype->dtype_threshold) { flag |= (page38->threshold != cur_dtype->dtype_threshold); page38->threshold = cur_dtype->dtype_threshold; } if ((cur_dtype->dtype_options & SUP_CACHE_MIN) && (fixed->min_prefetch & cur_dtype->dtype_prefetch_min) == cur_dtype->dtype_prefetch_min) { flag |= (page38->min_prefetch != cur_dtype->dtype_prefetch_min); page38->min_prefetch = cur_dtype->dtype_prefetch_min; } if ((cur_dtype->dtype_options & SUP_CACHE_MAX) && (fixed->max_prefetch & cur_dtype->dtype_prefetch_max) == cur_dtype->dtype_prefetch_max) { flag |= (page38->max_prefetch != cur_dtype->dtype_prefetch_max); page38->max_prefetch = cur_dtype->dtype_prefetch_max; } /* * Notify the user of changes up to this point */ if (flag && option_msg) { fmt_print("PAGE 38: cache mode= 0x%x (0x%x)\n", page38->mode, tmp1); fmt_print(" min. prefetch multiplier= %d ", page38->min_multiplier); fmt_print("max. prefetch multiplier= %d\n", page38->max_multiplier); fmt_print(" threshold= %d (%d) ", page38->threshold, tmp2); fmt_print("min. prefetch= %d (%d) ", page38->min_prefetch, tmp3); fmt_print("max. prefetch= %d (%d)\n", page38->max_prefetch, tmp4); } /* * Apply any changes requested via the change list method */ flag |= apply_chg_list(DAD_MODE_CACHE_CCS, length, (uchar_t *)page38, (uchar_t *)fixed, cur_dtype->dtype_chglist); /* * If no changes required, do not issue a mode select */ if (flag == 0) { return (0); } /* * Issue a mode select * * We always want to set the Page Format bit for mode * selects. Set the Save Page bit if the drive indicates * that it can save this page via the mode sense. */ sp_flags = MODE_SELECT_PF; if (page38->mode_page.ps) { sp_flags |= MODE_SELECT_SP; } page38->mode_page.ps = 0; header.mode_header.length = 0; header.mode_header.device_specific = 0; status = uscsi_mode_select(cur_file, DAD_MODE_CACHE_CCS, sp_flags, (caddr_t)page38, length, &header); if (status && (sp_flags & MODE_SELECT_SP)) { /* If failed, try not saving mode select params. */ sp_flags &= ~MODE_SELECT_SP; status = uscsi_mode_select(cur_file, DAD_MODE_CACHE_CCS, sp_flags, (caddr_t)page38, length, &header); } if (status && option_msg) { err_print("Warning: Using default CCS cache parameters.\n\n"); } /* * If debugging, issue mode senses on the current and * saved values, so we can see the result of the mode * selects. */ if (option_msg && diag_msg) { (void) uscsi_mode_sense(cur_file, DAD_MODE_CACHE_CCS, MODE_SENSE_PC_CURRENT, (caddr_t)page38, MAX_MODE_SENSE_SIZE, &header); (void) uscsi_mode_sense(cur_file, DAD_MODE_CACHE_CCS, MODE_SENSE_PC_SAVED, (caddr_t)page38, MAX_MODE_SENSE_SIZE, &header); } return (0); } /* * Extract the manufacturer's defect list. */ int scsi_ex_man(struct defect_list *list) { int i; i = scsi_read_defect_data(list, DLD_MAN_DEF_LIST); if (i != 0) return (i); list->flags &= ~LIST_PGLIST; return (0); } /* * Extract the current defect list. * For embedded scsi drives, this means both the manufacturer's (P) * and the grown (G) lists. */ int scsi_ex_cur(struct defect_list *list) { int i; i = scsi_read_defect_data(list, DLD_GROWN_DEF_LIST|DLD_MAN_DEF_LIST); if (i != 0) return (i); list->flags |= LIST_PGLIST; return (0); } /* * Extract the grown list only */ int scsi_ex_grown(struct defect_list *list) { int i; i = scsi_read_defect_data(list, DLD_GROWN_DEF_LIST); if (i != 0) return (i); list->flags |= LIST_PGLIST; return (0); } static int scsi_read_defect_data(struct defect_list *list, int pglist_flags) { struct uscsi_cmd ucmd; char rqbuf[255]; union scsi_cdb cdb; struct scsi_defect_list *defects; struct scsi_defect_list def_list; struct scsi_defect_hdr *hdr; int status; int nbytes; int len; /* returned defect list length */ struct scsi_extended_sense *rq; hdr = (struct scsi_defect_hdr *)&def_list; /* * First get length of list by asking for the header only. */ (void) memset((char *)&def_list, 0, sizeof (def_list)); /* * Build and execute the uscsi ioctl */ (void) memset((char *)&ucmd, 0, sizeof (ucmd)); (void) memset((char *)&cdb, 0, sizeof (union scsi_cdb)); (void) memset((char *)rqbuf, 0, 255); cdb.scc_cmd = SCMD_READ_DEFECT_LIST; FORMG1COUNT(&cdb, sizeof (struct scsi_defect_hdr)); cdb.cdb_opaque[2] = pglist_flags | DLD_BFI_FORMAT; ucmd.uscsi_cdb = (caddr_t)&cdb; ucmd.uscsi_cdblen = CDB_GROUP1; ucmd.uscsi_bufaddr = (caddr_t)hdr; ucmd.uscsi_buflen = sizeof (struct scsi_defect_hdr); ucmd.uscsi_rqbuf = rqbuf; ucmd.uscsi_rqlen = sizeof (rqbuf); ucmd.uscsi_rqresid = sizeof (rqbuf); rq = (struct scsi_extended_sense *)ucmd.uscsi_rqbuf; status = uscsi_cmd(cur_file, &ucmd, (option_msg && diag_msg) ? F_NORMAL : F_SILENT); if (status != 0) { /* * check if read_defect_list_is_supported. */ if (ucmd.uscsi_rqstatus == STATUS_GOOD && rq->es_key == KEY_ILLEGAL_REQUEST && rq->es_add_code == INVALID_OPCODE) { err_print("\nWARNING: Current Disk does not support" " defect lists.\n"); } else if (option_msg) { err_print("No %s defect list.\n", pglist_flags & DLD_GROWN_DEF_LIST ? "grown" : "manufacturer's"); } return (-1); } /* * Read the full list the second time */ hdr->length = BE_16(hdr->length); len = hdr->length; nbytes = len + sizeof (struct scsi_defect_hdr); defects = zalloc(nbytes); *(struct scsi_defect_hdr *)defects = *(struct scsi_defect_hdr *)hdr; (void) memset((char *)&ucmd, 0, sizeof (ucmd)); (void) memset((char *)&cdb, 0, sizeof (union scsi_cdb)); cdb.scc_cmd = SCMD_READ_DEFECT_LIST; FORMG1COUNT(&cdb, nbytes); cdb.cdb_opaque[2] = pglist_flags | DLD_BFI_FORMAT; ucmd.uscsi_cdb = (caddr_t)&cdb; ucmd.uscsi_cdblen = CDB_GROUP1; ucmd.uscsi_bufaddr = (caddr_t)defects; ucmd.uscsi_buflen = nbytes; status = uscsi_cmd(cur_file, &ucmd, (option_msg && diag_msg) ? F_NORMAL : F_SILENT); if (status) { err_print("can't read defect list 2nd time"); destroy_data((char *)defects); return (-1); } defects->length = BE_16(defects->length); if (len != hdr->length) { err_print("not enough defects"); destroy_data((char *)defects); return (-1); } scsi_convert_list_to_new(list, (struct scsi_defect_list *)defects, DLD_BFI_FORMAT); destroy_data((char *)defects); return (0); } /* * Map a block. */ static int scsi_repair(uint64_t bn, int flag __unused) { struct uscsi_cmd ucmd; union scsi_cdb cdb; struct scsi_reassign_blk defect_list; /* * Build and execute the uscsi ioctl */ (void) memset((char *)&ucmd, 0, sizeof (ucmd)); (void) memset((char *)&cdb, 0, sizeof (union scsi_cdb)); (void) memset((char *)&defect_list, 0, sizeof (struct scsi_reassign_blk)); cdb.scc_cmd = SCMD_REASSIGN_BLOCK; ucmd.uscsi_cdb = (caddr_t)&cdb; ucmd.uscsi_cdblen = CDB_GROUP0; ucmd.uscsi_bufaddr = (caddr_t)&defect_list; ucmd.uscsi_buflen = sizeof (struct scsi_reassign_blk); defect_list.length = sizeof (defect_list.defect); defect_list.length = BE_16(defect_list.length); defect_list.defect = bn; defect_list.defect = BE_32(defect_list.defect); return (uscsi_cmd(cur_file, &ucmd, (option_msg && diag_msg) ? F_NORMAL : F_SILENT)); } /* * Convert a SCSI-style defect list to our generic format. * We can handle different format lists. */ static void scsi_convert_list_to_new(struct defect_list *list, struct scsi_defect_list *def_list, int list_format) { struct scsi_bfi_defect *old_defect, *old_defect1; struct defect_entry *new_defect; int len, new_len, obfi, nbfi; int i; int old_cyl, new_cyl; unsigned char *cp; switch (list_format) { case DLD_BFI_FORMAT: /* * Allocate space for the rest of the list. */ len = def_list->length / sizeof (struct scsi_bfi_defect); old_defect = def_list->list; new_defect = (struct defect_entry *) zalloc(deflist_size(cur_blksz, len) * cur_blksz); list->header.magicno = (uint_t)DEFECT_MAGIC; list->list = new_defect; for (i = 0, new_len = 0; i < len; new_defect++, new_len++) { cp = (unsigned char *)old_defect; new_defect->cyl = (cp[0] << 16 | cp[1] << 8) | cp[2]; new_defect->head = old_defect->head; new_defect->bfi = (int)old_defect->bytes_from_index; new_defect->bfi = BE_32(new_defect->bfi); new_defect->nbits = 0; /* size of defect */ old_defect1 = old_defect++; i++; /* * Since we reached the end of the list, old_defect * now points to an invalid reference, since it got * incremented in the above operation. So we don't * need to proceed further. new_len needs to be * incremented to account for the last element. */ if (i == len) { new_len++; break; } obfi = new_defect->bfi; nbfi = (int)old_defect->bytes_from_index; nbfi = BE_32(nbfi); old_cyl = new_defect->cyl; cp = (unsigned char *)old_defect; new_cyl = (cp[0] << 16 | cp[1] << 8) | cp[2]; /* * Merge adjacent contiguous defect entries into one * and update the length of the defect */ while ((i < len) && (old_cyl == new_cyl) && (old_defect->head == old_defect1->head) && (nbfi == (obfi + BITSPERBYTE))) { old_defect1 = old_defect++; cp = (unsigned char *)old_defect; new_cyl = (cp[0] << 16 | cp[1] << 8) | cp[2]; obfi = (int)old_defect1->bytes_from_index; obfi = BE_32(obfi); nbfi = (int)old_defect->bytes_from_index; nbfi = BE_32(nbfi); new_defect->nbits += (8*BITSPERBYTE); i++; } } list->header.count = new_len; break; default: err_print("scsi_convert_list_to_new: can't deal with it\n"); exit(0); /*NOTREACHED*/ } (void) checkdefsum(list, CK_MAKESUM); } /* * Execute a command and determine the result. * Uses the "uscsi" ioctl interface, which is * fully supported. * * If the user wants request sense data to be returned * in case of error then , the "uscsi_cmd" structure * should have the request sense buffer allocated in * uscsi_rqbuf. * */ int uscsi_cmd(int fd, struct uscsi_cmd *ucmd, int flags) { struct scsi_extended_sense *rq; char rqbuf[255]; int status; int rqlen; int timeout = 0; /* * Set function flags for driver. */ ucmd->uscsi_flags = USCSI_ISOLATE; if (flags & F_SILENT) { ucmd->uscsi_flags |= USCSI_SILENT; } if (flags & F_RQENABLE) { ucmd->uscsi_flags |= USCSI_RQENABLE; } /* * If this command will perform a read, set the USCSI_READ flag */ if (ucmd->uscsi_buflen > 0) { /* * uscsi_cdb is declared as a caddr_t, so any CDB * command byte with the MSB set will result in a * compiler error unless we cast to an unsigned value. */ switch ((uint8_t)ucmd->uscsi_cdb[0]) { case SCMD_READ: case SCMD_READ|SCMD_GROUP1: case SCMD_READ|SCMD_GROUP4: case SCMD_MODE_SENSE: case SCMD_INQUIRY: case SCMD_READ_DEFECT_LIST: case SCMD_READ_CAPACITY: case SCMD_SVC_ACTION_IN_G4: ucmd->uscsi_flags |= USCSI_READ; break; } } /* * Set timeout: 30 seconds for all commands except format */ switch (ucmd->uscsi_cdb[0]) { case SCMD_FORMAT: if (ucmd->uscsi_timeout == 0) { ucmd->uscsi_timeout = scsi_format_timeout; /* * Get the timeout value computed using page4 geometry. * add 50% margin to cover defect management overhead. * add another 50% margin to have a safe timeout. * If it exceeds 2 hours then use this value. */ if ((timeout = scsi_format_time()) > 0) { timeout *= 60; /* convert to seconds */ timeout += timeout; /* * formatting drives with huge capacity * will cause these heuristics to come * up with times that overflow ~9 hours */ if (timeout > SHRT_MAX) timeout = SHRT_MAX; if (timeout > scsi_format_timeout) ucmd->uscsi_timeout = timeout; } } if (option_msg && diag_msg) { err_print("format_timeout set to %d seconds, %d" " required\n", ucmd->uscsi_timeout, timeout); } break; default: ucmd->uscsi_timeout = 30; /* 30 seconds */ break; } /* * Set up Request Sense buffer */ ucmd->uscsi_flags |= USCSI_RQENABLE; if (ucmd->uscsi_rqbuf == NULL) { ucmd->uscsi_rqbuf = rqbuf; ucmd->uscsi_rqlen = sizeof (rqbuf); ucmd->uscsi_rqresid = sizeof (rqbuf); } ucmd->uscsi_rqstatus = IMPOSSIBLE_SCSI_STATUS; /* * Clear global error state */ media_error = 0; /* * Execute the ioctl */ status = ioctl(fd, USCSICMD, ucmd); if (status == 0 && ucmd->uscsi_status == 0) { return (status); } /* * Check the status and return appropriate errors if the disk is * unavailable (could be formatting) or reserved (by other host). * In either case we can not talk to the disk now. */ if (status == -1 && errno == EAGAIN) { disk_error = DISK_STAT_UNAVAILABLE; return (DSK_UNAVAILABLE); } if ((ucmd->uscsi_status & STATUS_MASK) == STATUS_RESERVATION_CONFLICT) { disk_error = DISK_STAT_RESERVED; return (DSK_RESERVED); } /* * Check for physically removed or completely unresponsive drive */ if (status == -1 && !ucmd->uscsi_status && errno == EIO) { disk_error = DISK_STAT_UNAVAILABLE; return (DSK_UNAVAILABLE); } /* * If an automatic Request Sense gave us valid * info about the error, we may be able to use * that to print a reasonable error msg. */ if (ucmd->uscsi_rqstatus == IMPOSSIBLE_SCSI_STATUS) { if (option_msg && diag_msg) { err_print("No request sense for command %s\n", scsi_find_command_name(ucmd->uscsi_cdb[0])); } return (-1); } if (ucmd->uscsi_rqstatus != STATUS_GOOD) { if (option_msg && diag_msg) { err_print("Request sense status for command %s: 0x%x\n", scsi_find_command_name(ucmd->uscsi_cdb[0]), ucmd->uscsi_rqstatus); } return (-1); } rq = (struct scsi_extended_sense *)ucmd->uscsi_rqbuf; rqlen = ucmd->uscsi_rqlen - ucmd->uscsi_rqresid; if ((((int)rq->es_add_len) + 8) < MIN_REQUEST_SENSE_LEN || rq->es_class != CLASS_EXTENDED_SENSE || rqlen < MIN_REQUEST_SENSE_LEN) { if (option_msg) { err_print("Request sense for command %s failed\n", scsi_find_command_name(ucmd->uscsi_cdb[0])); } if (option_msg && diag_msg) { err_print("Sense data:\n"); dump("", (caddr_t)rqbuf, rqlen, HEX_ONLY); } if (errno == EIO) { disk_error = DISK_STAT_UNAVAILABLE; return (DSK_UNAVAILABLE); } else { return (-1); } } /* * If the failed command is a Mode Select, and the * target is indicating that it has rounded one of * the mode select parameters, as defined in the SCSI-2 * specification, then we should accept the command * as successful. */ if (ucmd->uscsi_cdb[0] == SCMD_MODE_SELECT) { if (rq->es_key == KEY_RECOVERABLE_ERROR && rq->es_add_code == ROUNDED_PARAMETER && rq->es_qual_code == 0) { return (0); } } switch (rq->es_key) { case KEY_NOT_READY: disk_error = DISK_STAT_NOTREADY; break; case KEY_DATA_PROTECT: disk_error = DISK_STAT_DATA_PROTECT; break; } if (flags & F_ALLERRS) { media_error = (rq->es_key == KEY_MEDIUM_ERROR); } if (!(flags & F_SILENT) || option_msg) { scsi_printerr(ucmd, rq, rqlen); } if ((rq->es_key != KEY_RECOVERABLE_ERROR) || (flags & F_ALLERRS)) { return (-1); } if (status == -1 && errno == EIO) { disk_error = DISK_STAT_UNAVAILABLE; return (DSK_UNAVAILABLE); } return (0); } /* * Execute a uscsi mode sense command. * This can only be used to return one page at a time. * Return the mode header/block descriptor and the actual * page data separately - this allows us to support * devices which return either 0 or 1 block descriptors. * Whatever a device gives us in the mode header/block descriptor * will be returned to it upon subsequent mode selects. */ int uscsi_mode_sense(int fd, int page_code, int page_control, caddr_t page_data, int page_size, struct scsi_ms_header *header) { caddr_t mode_sense_buf; struct mode_header *hdr; struct mode_page *pg; int nbytes; struct uscsi_cmd ucmd; union scsi_cdb cdb; int status; int maximum; assert(page_size >= 0 && page_size < 256); assert(page_control == MODE_SENSE_PC_CURRENT || page_control == MODE_SENSE_PC_CHANGEABLE || page_control == MODE_SENSE_PC_DEFAULT || page_control == MODE_SENSE_PC_SAVED); /* * Allocate a buffer for the mode sense headers * and mode sense data itself. */ nbytes = sizeof (struct block_descriptor) + sizeof (struct mode_header) + page_size; nbytes = page_size; if ((mode_sense_buf = malloc((uint_t)nbytes)) == NULL) { err_print("cannot malloc %d bytes\n", nbytes); return (-1); } /* * Build and execute the uscsi ioctl */ (void) memset(mode_sense_buf, 0, nbytes); (void) memset((char *)&ucmd, 0, sizeof (ucmd)); (void) memset((char *)&cdb, 0, sizeof (union scsi_cdb)); cdb.scc_cmd = SCMD_MODE_SENSE; FORMG0COUNT(&cdb, (uchar_t)nbytes); cdb.cdb_opaque[2] = page_control | page_code; ucmd.uscsi_cdb = (caddr_t)&cdb; ucmd.uscsi_cdblen = CDB_GROUP0; ucmd.uscsi_bufaddr = mode_sense_buf; ucmd.uscsi_buflen = nbytes; status = uscsi_cmd(fd, &ucmd, (option_msg && diag_msg) ? F_NORMAL : F_SILENT); if (status) { if (option_msg) { err_print("Mode sense page 0x%x failed\n", page_code); } free(mode_sense_buf); return (-1); } /* * Verify that the returned data looks reasonabled, * find the actual page data, and copy it into the * user's buffer. Copy the mode_header and block_descriptor * into the header structure, which can then be used to * return the same data to the drive when issuing a mode select. */ hdr = (struct mode_header *)mode_sense_buf; (void) memset((caddr_t)header, 0, sizeof (struct scsi_ms_header)); if (hdr->bdesc_length != sizeof (struct block_descriptor) && hdr->bdesc_length != 0) { if (option_msg) { err_print("\nMode sense page 0x%x: block " "descriptor length %d incorrect\n", page_code, hdr->bdesc_length); if (diag_msg) dump("Mode sense: ", mode_sense_buf, nbytes, HEX_ONLY); } free(mode_sense_buf); return (-1); } (void) memcpy((caddr_t)header, mode_sense_buf, sizeof (struct mode_header) + hdr->bdesc_length); pg = (struct mode_page *)((ulong_t)mode_sense_buf + sizeof (struct mode_header) + hdr->bdesc_length); if (pg->code != page_code) { if (option_msg) { err_print("\nMode sense page 0x%x: incorrect page " "code 0x%x\n", page_code, pg->code); if (diag_msg) dump("Mode sense: ", mode_sense_buf, nbytes, HEX_ONLY); } free(mode_sense_buf); return (-1); } /* * Accept up to "page_size" bytes of mode sense data. * This allows us to accept both CCS and SCSI-2 * structures, as long as we request the greater * of the two. */ maximum = page_size - sizeof (struct mode_page) - hdr->bdesc_length; if (((int)pg->length) > maximum) { if (option_msg) { err_print("Mode sense page 0x%x: incorrect page " "length %d - expected max %d\n", page_code, pg->length, maximum); if (diag_msg) dump("Mode sense: ", mode_sense_buf, nbytes, HEX_ONLY); } free(mode_sense_buf); return (-1); } (void) memcpy(page_data, (caddr_t)pg, MODESENSE_PAGE_LEN(pg)); if (option_msg && diag_msg) { char *pc = find_string(page_control_strings, page_control); err_print("\nMode sense page 0x%x (%s):\n", page_code, pc != NULL ? pc : ""); dump("header: ", (caddr_t)header, sizeof (struct scsi_ms_header), HEX_ONLY); dump("data: ", page_data, MODESENSE_PAGE_LEN(pg), HEX_ONLY); } free(mode_sense_buf); return (0); } /* * Execute a uscsi mode select command. */ int uscsi_mode_select(int fd, int page_code, int options, caddr_t page_data, int page_size, struct scsi_ms_header *header) { caddr_t mode_select_buf; int nbytes; struct uscsi_cmd ucmd; union scsi_cdb cdb; int status; assert(((struct mode_page *)page_data)->ps == 0); assert(header->mode_header.length == 0); assert(header->mode_header.device_specific == 0); assert((options & ~(MODE_SELECT_SP|MODE_SELECT_PF)) == 0); /* * Allocate a buffer for the mode select header and data */ nbytes = sizeof (struct block_descriptor) + sizeof (struct mode_header) + page_size; if ((mode_select_buf = malloc((uint_t)nbytes)) == NULL) { err_print("cannot malloc %d bytes\n", nbytes); return (-1); } /* * Build the mode select data out of the header and page data * This allows us to support devices which return either * 0 or 1 block descriptors. */ (void) memset(mode_select_buf, 0, nbytes); nbytes = sizeof (struct mode_header); if (header->mode_header.bdesc_length == sizeof (struct block_descriptor)) { nbytes += sizeof (struct block_descriptor); } /* * Dump the structures if anyone's interested */ if (option_msg && diag_msg) { char *s; s = find_string(mode_select_strings, options & (MODE_SELECT_SP|MODE_SELECT_PF)); err_print("\nMode select page 0x%x%s:\n", page_code, s != NULL ? s : ""); dump("header: ", (caddr_t)header, nbytes, HEX_ONLY); dump("data: ", (caddr_t)page_data, page_size, HEX_ONLY); } /* * Fix the code for byte ordering */ switch (page_code) { case DAD_MODE_ERR_RECOV: { struct mode_err_recov *pd; pd = (struct mode_err_recov *)(void *)page_data; pd->recovery_time_limit = BE_16(pd->recovery_time_limit); break; } case MODEPAGE_DISCO_RECO: { struct mode_disco_reco *pd; pd = (struct mode_disco_reco *)(void *)page_data; pd->bus_inactivity_limit = BE_16(pd->bus_inactivity_limit); pd->disconect_time_limit = BE_16(pd->disconect_time_limit); pd->connect_time_limit = BE_16(pd->connect_time_limit); pd->max_burst_size = BE_16(pd->max_burst_size); break; } case DAD_MODE_FORMAT: { struct mode_format *pd; pd = (struct mode_format *)(void *)page_data; pd->tracks_per_zone = BE_16(pd->tracks_per_zone); pd->alt_sect_zone = BE_16(pd->alt_sect_zone); pd->alt_tracks_zone = BE_16(pd->alt_tracks_zone); pd->alt_tracks_vol = BE_16(pd->alt_tracks_vol); pd->sect_track = BE_16(pd->sect_track); pd->data_bytes_sect = BE_16(pd->data_bytes_sect); pd->interleave = BE_16(pd->interleave); pd->track_skew = BE_16(pd->track_skew); pd->cylinder_skew = BE_16(pd->cylinder_skew); break; } case DAD_MODE_GEOMETRY: { struct mode_geometry *pd; pd = (struct mode_geometry *)(void *)page_data; pd->step_rate = BE_16(pd->step_rate); pd->rpm = BE_16(pd->rpm); break; } case DAD_MODE_CACHE: { struct mode_cache *pd; pd = (struct mode_cache *)(void *)page_data; pd->dis_prefetch_len = BE_16(pd->dis_prefetch_len); pd->min_prefetch = BE_16(pd->min_prefetch); pd->max_prefetch = BE_16(pd->max_prefetch); pd->prefetch_ceiling = BE_16(pd->prefetch_ceiling); break; } case MODEPAGE_PDEVICE: { struct mode_pdevice *pd; pd = (struct mode_pdevice *)(void *)page_data; pd->if_ident = BE_16(pd->if_ident); break; } case MODEPAGE_CTRL_MODE: { struct mode_control *pd; pd = (struct mode_control *)(void *)page_data; pd->ready_aen_holdoff = BE_16(pd->ready_aen_holdoff); break; } } /* * Put the header and data together */ (void) memcpy(mode_select_buf, (caddr_t)header, nbytes); (void) memcpy(mode_select_buf + nbytes, page_data, page_size); nbytes += page_size; /* * Build and execute the uscsi ioctl */ (void) memset((char *)&ucmd, 0, sizeof (ucmd)); (void) memset((char *)&cdb, 0, sizeof (union scsi_cdb)); cdb.scc_cmd = SCMD_MODE_SELECT; FORMG0COUNT(&cdb, (uchar_t)nbytes); cdb.cdb_opaque[1] = (uchar_t)options; ucmd.uscsi_cdb = (caddr_t)&cdb; ucmd.uscsi_cdblen = CDB_GROUP0; ucmd.uscsi_bufaddr = mode_select_buf; ucmd.uscsi_buflen = nbytes; status = uscsi_cmd(fd, &ucmd, (option_msg && diag_msg) ? F_NORMAL : F_SILENT); if (status && option_msg) { err_print("Mode select page 0x%x failed\n", page_code); } free(mode_select_buf); return (status); } /* * Execute a uscsi inquiry command and return the * resulting data. */ int uscsi_inquiry(int fd, caddr_t inqbuf, int inqbufsiz) { struct uscsi_cmd ucmd; union scsi_cdb cdb; struct scsi_inquiry *inq; int n; int status; assert(inqbufsiz >= sizeof (struct scsi_inquiry) && inqbufsiz < 256); /* * Build and execute the uscsi ioctl */ (void) memset((char *)inqbuf, 0, inqbufsiz); (void) memset((char *)&ucmd, 0, sizeof (ucmd)); (void) memset((char *)&cdb, 0, sizeof (union scsi_cdb)); cdb.scc_cmd = SCMD_INQUIRY; FORMG0COUNT(&cdb, (uchar_t)inqbufsiz); ucmd.uscsi_cdb = (caddr_t)&cdb; ucmd.uscsi_cdblen = CDB_GROUP0; ucmd.uscsi_bufaddr = (caddr_t)inqbuf; ucmd.uscsi_buflen = inqbufsiz; status = uscsi_cmd(fd, &ucmd, (option_msg && diag_msg) ? F_NORMAL : F_SILENT); if (status) { if (option_msg) { err_print("Inquiry failed\n"); } } else if (option_msg && diag_msg) { /* * Dump the inquiry data if anyone's interested */ inq = (struct scsi_inquiry *)inqbuf; n = (int)inq->inq_len + 4; n = min(n, inqbufsiz); err_print("Inquiry:\n"); dump("", (caddr_t)inqbuf, n, HEX_ASCII); } return (status); } /* * Execute a uscsi inquiry command with page code 86h */ int uscsi_inquiry_page_86h(int fd, caddr_t inqbuf, int inqbufsiz) { struct uscsi_cmd ucmd; union scsi_cdb cdb; int status; assert(inqbuf); assert(inqbufsiz >= sizeof (struct scsi_inquiry) && inqbufsiz < 256); /* * Build and execute uscsi ioctl */ (void) memset((char *)inqbuf, 0, inqbufsiz); (void) memset((char *)&ucmd, 0, sizeof (ucmd)); (void) memset((char *)&cdb, 0, sizeof (cdb)); cdb.scc_cmd = SCMD_INQUIRY; FORMG0COUNT(&cdb, (uchar_t)inqbufsiz); cdb.cdb_opaque[1] |= 0x01; cdb.cdb_opaque[2] = 0x86; ucmd.uscsi_cdb = (caddr_t)&cdb; ucmd.uscsi_cdblen = CDB_GROUP0; ucmd.uscsi_bufaddr = (caddr_t)inqbuf; ucmd.uscsi_buflen = inqbufsiz; status = uscsi_cmd(fd, &ucmd, (option_msg && diag_msg) ? F_NORMAL : F_SILENT); if (status) { if (option_msg) { err_print("Inquriy with page_86h failed\n"); } } return (status); } /* * Return the Read Capacity information */ int uscsi_read_capacity_16(int fd, struct scsi_capacity_16 *capacity) { struct uscsi_cmd ucmd; union scsi_cdb cdb; int status; (void) memset((char *)capacity, 0, sizeof (struct scsi_capacity_16)); (void) memset((char *)&ucmd, 0, sizeof (ucmd)); (void) memset((char *)&cdb, 0, sizeof (union scsi_cdb)); ucmd.uscsi_cdb = (caddr_t)&cdb; ucmd.uscsi_cdblen = CDB_GROUP4; ucmd.uscsi_bufaddr = (caddr_t)capacity; ucmd.uscsi_buflen = sizeof (struct scsi_capacity_16); /* * Read Capacity (16) is a Service Action In command. One * command byte (0x9E) is overloaded for multiple operations, * with the second CDB byte specifying the desired operation */ cdb.scc_cmd = SCMD_SVC_ACTION_IN_G4; cdb.cdb_opaque[1] = SSVC_ACTION_READ_CAPACITY_G4; /* * Fill in allocation length field */ cdb.cdb_opaque[10] = (uchar_t)((ucmd.uscsi_buflen & 0xff000000) >> 24); cdb.cdb_opaque[11] = (uchar_t)((ucmd.uscsi_buflen & 0x00ff0000) >> 16); cdb.cdb_opaque[12] = (uchar_t)((ucmd.uscsi_buflen & 0x0000ff00) >> 8); cdb.cdb_opaque[13] = (uchar_t)(ucmd.uscsi_buflen & 0x000000ff); status = uscsi_cmd(fd, &ucmd, (option_msg && diag_msg) ? F_NORMAL : F_SILENT); if (status) { if (option_msg) { err_print("Read capacity 16 failed\n"); } } else if (option_msg && diag_msg) { /* * Dump the capacity data if anyone's interested */ dump("Capacity: ", (caddr_t)capacity, sizeof (struct scsi_capacity_16), HEX_ONLY); } capacity->sc_capacity = BE_64(capacity->sc_capacity); capacity->sc_lbasize = BE_32(capacity->sc_lbasize); return (status); } int uscsi_read_capacity(int fd, struct scsi_capacity_16 *capacity) { struct uscsi_cmd ucmd; union scsi_cdb cdb; int status; struct scsi_capacity cap_old; /* * Build and execute the uscsi ioctl */ (void) memset((char *)capacity, 0, sizeof (struct scsi_capacity_16)); (void) memset((char *)&cap_old, 0, sizeof (struct scsi_capacity)); (void) memset((char *)&ucmd, 0, sizeof (ucmd)); (void) memset((char *)&cdb, 0, sizeof (union scsi_cdb)); cdb.scc_cmd = SCMD_READ_CAPACITY; ucmd.uscsi_cdb = (caddr_t)&cdb; ucmd.uscsi_cdblen = CDB_GROUP1; ucmd.uscsi_bufaddr = (caddr_t)&cap_old; ucmd.uscsi_buflen = sizeof (struct scsi_capacity); status = uscsi_cmd(fd, &ucmd, (option_msg && diag_msg) ? F_NORMAL : F_SILENT); if (cap_old.capacity == UINT_MAX32) { /* * A capacity of 0xffffffff in response to a * READ CAPACITY 10 indicates that the lun * is too large to report the size in a 32 bit * value, and a READ CAPACITY 16 is required * to get the correct size. */ (void) memset((char *)&ucmd, 0, sizeof (ucmd)); (void) memset((char *)&cdb, 0, sizeof (union scsi_cdb)); ucmd.uscsi_cdb = (caddr_t)&cdb; ucmd.uscsi_cdblen = CDB_GROUP4; ucmd.uscsi_bufaddr = (caddr_t)capacity; ucmd.uscsi_buflen = sizeof (struct scsi_capacity_16); /* * Read Capacity (16) is a Service Action In command. One * command byte (0x9E) is overloaded for multiple operations, * with the second CDB byte specifying the desired operation */ cdb.scc_cmd = SCMD_SVC_ACTION_IN_G4; cdb.cdb_opaque[1] = SSVC_ACTION_READ_CAPACITY_G4; /* * Fill in allocation length field */ cdb.cdb_opaque[10] = (uchar_t)((ucmd.uscsi_buflen & 0xff000000) >> 24); cdb.cdb_opaque[11] = (uchar_t)((ucmd.uscsi_buflen & 0x00ff0000) >> 16); cdb.cdb_opaque[12] = (uchar_t)((ucmd.uscsi_buflen & 0x0000ff00) >> 8); cdb.cdb_opaque[13] = (uchar_t)(ucmd.uscsi_buflen & 0x000000ff); status = uscsi_cmd(fd, &ucmd, (option_msg && diag_msg) ? F_NORMAL : F_SILENT); } if (status) { if (option_msg) { /* * Indicate which of the commands failed */ if (cdb.scc_cmd == SCMD_READ_CAPACITY) { err_print("Read capacity failed\n"); } else { err_print("Read capacity 16 failed\n"); } } } else if (option_msg && diag_msg) { /* * Dump the capacity data if anyone's interested */ if (cap_old.capacity == UINT_MAX32) { dump("Capacity: ", (caddr_t)capacity, sizeof (struct scsi_capacity_16), HEX_ONLY); } else { dump("Capacity: ", (caddr_t)&cap_old, sizeof (struct scsi_capacity), HEX_ONLY); } } if (cap_old.capacity == UINT_MAX32) { capacity->sc_capacity = BE_64(capacity->sc_capacity); capacity->sc_lbasize = BE_32(capacity->sc_lbasize); } else { capacity->sc_capacity = (uint64_t)BE_32(cap_old.capacity); capacity->sc_lbasize = BE_32(cap_old.lbasize); } return (status); } /* * Reserve the current disk */ static int uscsi_reserve_release(int fd __maybe_unused, int cmd __maybe_unused) { int status = 0; #ifdef sparc struct uscsi_cmd ucmd; union scsi_cdb cdb; /* * Build and execute the uscsi ioctl */ (void) memset((char *)&ucmd, 0, sizeof (ucmd)); (void) memset((char *)&cdb, 0, sizeof (union scsi_cdb)); cdb.scc_cmd = (cmd == SCMD_RESERVE) ? SCMD_RESERVE : SCMD_RELEASE; ucmd.uscsi_cdb = (caddr_t)&cdb; ucmd.uscsi_cdblen = CDB_GROUP0; status = uscsi_cmd(fd, &ucmd, (option_msg && diag_msg) ? F_NORMAL : F_SILENT); if (status) { /* * Reserve/Release(6) failed. * Try Reserve/Release(10) , if it succeeds then * return success. */ (void) memset((char *)&ucmd, 0, sizeof (ucmd)); (void) memset((char *)&cdb, 0, sizeof (union scsi_cdb)); ucmd.uscsi_cdb = (caddr_t)&cdb; cdb.scc_cmd = (cmd == SCMD_RESERVE) ? SCMD_RESERVE_G1 : SCMD_RELEASE_G1; ucmd.uscsi_cdblen = CDB_GROUP1; status = uscsi_cmd(fd, &ucmd, (option_msg && diag_msg) ? F_NORMAL : F_SILENT); if (status) { if (option_msg) { err_print("%s failed\n", (cmd == SCMD_RESERVE) ? "Reserve" : "Release"); } } } #endif /* sparc */ return (status); } int scsi_dump_mode_sense_pages(int page_control) { struct uscsi_cmd ucmd; union scsi_cdb cdb; char *msbuf; int nbytes; char *pc_str; int status; struct mode_header *mh; char *p; struct mode_page *mp; int n; char s[16]; int result = 0; pc_str = find_string(page_control_strings, page_control); /* * Allocate memory for the mode sense buffer. */ nbytes = 255; msbuf = (char *)zalloc(nbytes); /* * Build and execute the uscsi ioctl */ (void) memset(msbuf, 0, nbytes); (void) memset((char *)&ucmd, 0, sizeof (ucmd)); (void) memset((char *)&cdb, 0, sizeof (union scsi_cdb)); cdb.scc_cmd = SCMD_MODE_SENSE; FORMG0COUNT(&cdb, (uchar_t)nbytes); cdb.cdb_opaque[2] = page_control | 0x3f; ucmd.uscsi_cdb = (caddr_t)&cdb; ucmd.uscsi_cdblen = CDB_GROUP0; ucmd.uscsi_bufaddr = msbuf; ucmd.uscsi_buflen = nbytes; status = uscsi_cmd(cur_file, &ucmd, (option_msg && diag_msg) ? F_NORMAL : F_SILENT); if (status) { err_print("\nMode sense page 0x3f (%s) failed\n", pc_str); result = 1; } else { err_print("\nMode sense pages (%s):\n", pc_str); mh = (struct mode_header *)msbuf; nbytes = mh->length - sizeof (struct mode_header) - mh->bdesc_length + 1; p = msbuf + sizeof (struct mode_header) + mh->bdesc_length; dump(" ", msbuf, sizeof (struct mode_header) + (int)mh->bdesc_length, HEX_ONLY); while (nbytes > 0) { mp = (struct mode_page *)p; n = mp->length + sizeof (struct mode_page); nbytes -= n; if (nbytes < 0) break; (void) sprintf(s, " %3x: ", mp->code); dump(s, p, n, HEX_ONLY); p += n; } if (nbytes < 0) { err_print(" Sense data formatted incorrectly:\n"); dump(" ", msbuf, (int)mh->length + 1, HEX_ONLY); result = 1; } err_print("\n"); } free(msbuf); return (result); } static void scsi_printerr(struct uscsi_cmd *ucmd, struct scsi_extended_sense *rq, int rqlen) { diskaddr_t blkno; struct scsi_descr_sense_hdr *sdsp = (struct scsi_descr_sense_hdr *)rq; switch (rq->es_key) { case KEY_NO_SENSE: err_print("No sense error"); break; case KEY_RECOVERABLE_ERROR: err_print("Recoverable error"); break; case KEY_NOT_READY: err_print("Not ready error"); break; case KEY_MEDIUM_ERROR: err_print("Medium error"); break; case KEY_HARDWARE_ERROR: err_print("Hardware error"); break; case KEY_ILLEGAL_REQUEST: err_print("Illegal request"); break; case KEY_UNIT_ATTENTION: err_print("Unit attention error"); break; case KEY_WRITE_PROTECT: err_print("Write protect error"); break; case KEY_BLANK_CHECK: err_print("Blank check error"); break; case KEY_VENDOR_UNIQUE: err_print("Vendor unique error"); break; case KEY_COPY_ABORTED: err_print("Copy aborted error"); break; case KEY_ABORTED_COMMAND: err_print("Aborted command"); break; case KEY_EQUAL: err_print("Equal error"); break; case KEY_VOLUME_OVERFLOW: err_print("Volume overflow"); break; case KEY_MISCOMPARE: err_print("Miscompare error"); break; case KEY_RESERVED: err_print("Reserved error"); break; default: err_print("Unknown error"); break; } err_print(" during %s", scsi_find_command_name(ucmd->uscsi_cdb[0])); /* * Get asc, ascq and info field from sense data. There are two * possible formats (fixed sense data and descriptor sense data) * depending on the value of es_code. */ switch (rq->es_code) { case CODE_FMT_DESCR_CURRENT: case CODE_FMT_DESCR_DEFERRED: blkno = (diskaddr_t)scsi_extract_sense_info_descr(sdsp, rqlen); if (blkno != (diskaddr_t)-1) { err_print(": block %lld (0x%llx) (", blkno, blkno); pr_dblock(err_print, blkno); err_print(")"); } err_print("\n"); err_print("ASC: 0x%x ASCQ: 0x%x\n", sdsp->ds_add_code, sdsp->ds_qual_code); break; case CODE_FMT_FIXED_CURRENT: case CODE_FMT_FIXED_DEFERRED: default: if (rq->es_valid) { blkno = (rq->es_info_1 << 24) | (rq->es_info_2 << 16) | (rq->es_info_3 << 8) | rq->es_info_4; err_print(": block %lld (0x%llx) (", blkno, blkno); pr_dblock(err_print, blkno); err_print(")"); } err_print("\n"); if (rq->es_add_len >= 6) { err_print("ASC: 0x%x ASCQ: 0x%x\n", rq->es_add_code, rq->es_qual_code); } break; } if (option_msg && diag_msg) { if (rq->es_key == KEY_ILLEGAL_REQUEST) { dump("cmd: ", (caddr_t)ucmd, sizeof (struct uscsi_cmd), HEX_ONLY); dump("cdb: ", (caddr_t)ucmd->uscsi_cdb, ucmd->uscsi_cdblen, HEX_ONLY); } dump("sense: ", (caddr_t)rq, rqlen, HEX_ONLY); } if (option_msg) { switch (rq->es_code) { case CODE_FMT_DESCR_CURRENT: case CODE_FMT_DESCR_DEFERRED: scsi_print_descr_sense(sdsp, rqlen); break; case CODE_FMT_FIXED_CURRENT: case CODE_FMT_FIXED_DEFERRED: default: scsi_print_extended_sense(rq, rqlen); break; } } } /* * Retrieve "information" field from descriptor format * sense data. Iterates through each sense descriptor * looking for the information descriptor and returns * the information field from that descriptor. */ static diskaddr_t scsi_extract_sense_info_descr(struct scsi_descr_sense_hdr *sdsp, int rqlen) { diskaddr_t result; uint8_t *descr_offset; int valid_sense_length; struct scsi_information_sense_descr *isd; /* * Initialize result to -1 indicating there is no information * descriptor */ result = (diskaddr_t)-1; /* * The first descriptor will immediately follow the header */ descr_offset = (uint8_t *)(sdsp+1); /* Pointer arithmetic */ /* * Calculate the amount of valid sense data */ valid_sense_length = min((sizeof (struct scsi_descr_sense_hdr) + sdsp->ds_addl_sense_length), rqlen); /* * Iterate through the list of descriptors, stopping when we * run out of sense data */ while ((descr_offset + sizeof (struct scsi_information_sense_descr)) <= (uint8_t *)sdsp + valid_sense_length) { /* * Check if this is an information descriptor. We can * use the scsi_information_sense_descr structure as a * template sense the first two fields are always the * same */ isd = (struct scsi_information_sense_descr *)descr_offset; if (isd->isd_descr_type == DESCR_INFORMATION) { /* * Found an information descriptor. Copy the * information field. There will only be one * information descriptor so we can stop looking. */ result = (((diskaddr_t)isd->isd_information[0] << 56) | ((diskaddr_t)isd->isd_information[1] << 48) | ((diskaddr_t)isd->isd_information[2] << 40) | ((diskaddr_t)isd->isd_information[3] << 32) | ((diskaddr_t)isd->isd_information[4] << 24) | ((diskaddr_t)isd->isd_information[5] << 16) | ((diskaddr_t)isd->isd_information[6] << 8) | ((diskaddr_t)isd->isd_information[7])); break; } /* * Get pointer to the next descriptor. The "additional * length" field holds the length of the descriptor except * for the "type" and "additional length" fields, so * we need to add 2 to get the total length. */ descr_offset += (isd->isd_addl_length + 2); } return (result); } /* * Return a pointer to a string telling us the name of the command. */ static char * scsi_find_command_name(uint_t cmd) { struct scsi_command_name *c; for (c = scsi_command_names; c->command != SCMD_UNKNOWN; c++) if (c->command == cmd) break; return (c->name); } /* * Return true if we support a particular mode page */ int scsi_supported_page(int page) { return (page == 1 || page == 2 || page == 3 || page == 4 || page == 8 || page == 0x38); } int apply_chg_list(int pageno, int pagsiz, uchar_t *curbits, uchar_t *chgbits, struct chg_list *chglist) { uchar_t c; int i; int m; int delta; int changed = 0; while (chglist != NULL) { if (chglist->pageno == pageno && chglist->byteno < pagsiz) { i = chglist->byteno; c = curbits[i]; switch (chglist->mode) { case CHG_MODE_SET: c |= (uchar_t)chglist->value; break; case CHG_MODE_CLR: c &= (uchar_t)chglist->value; break; case CHG_MODE_ABS: c = (uchar_t)chglist->value; break; } /* * Figure out which bits changed, and * are marked as changeable. If this * result actually differs from the * current value, update the current * value, and note that a mode select * should be done. */ delta = c ^ curbits[i]; for (m = 0x01; m < 0x100; m <<= 1) { if ((delta & m) && (chgbits[i] & m)) { curbits[i] ^= m; changed = 1; } } } chglist = chglist->next; } return (changed); } /* * Return whether a given page is affected by an item on * the change list. */ static int chg_list_affects_page(struct chg_list *chglist, int pageno) { while (chglist != NULL) { if (chglist->pageno == pageno) { return (1); } chglist = chglist->next; } return (0); } /* * Labels for the various fields of the scsi_extended_sense structure */ static char *scsi_extended_sense_labels[] = { "Request sense valid: ", "Error class and code: ", "Segment number: ", "Filemark: ", "End-of-medium: ", "Incorrect length indicator: ", "Sense key: ", "Information field: ", "Additional sense length: ", "Command-specific information: ", "Additional sense code: ", "Additional sense code qualifier: ", "Field replaceable unit code: ", "Sense-key specific: ", "Additional sense bytes: " }; /* * Display the full scsi_extended_sense as returned by the device */ static void scsi_print_extended_sense(struct scsi_extended_sense *rq, int rqlen) { char **p; p = scsi_extended_sense_labels; if (rqlen < (sizeof (*rq) - 2) || !rq->es_valid) { /* * target should be capable of returning at least 18 * bytes of data, i.e upto rq->es_skey_specific field. * The additional sense bytes (2 or more ...) are optional. */ return; } fmt_print("\n%s%s\n", *p++, rq->es_valid ? "yes" : "no"); fmt_print("%s0x%02x\n", *p++, (rq->es_class << 4) + rq->es_code); fmt_print("%s%d\n", *p++, rq->es_segnum); fmt_print("%s%s\n", *p++, rq->es_filmk ? "yes" : "no"); fmt_print("%s%s\n", *p++, rq->es_eom ? "yes" : "no"); fmt_print("%s%s\n", *p++, rq->es_ili ? "yes" : "no"); fmt_print("%s%d\n", *p++, rq->es_key); fmt_print("%s0x%02x 0x%02x 0x%02x 0x%02x\n", *p++, rq->es_info_1, rq->es_info_2, rq->es_info_3, rq->es_info_4); fmt_print("%s%d\n", *p++, rq->es_add_len); fmt_print("%s0x%02x 0x%02x 0x%02x 0x%02x\n", *p++, rq->es_cmd_info[0], rq->es_cmd_info[1], rq->es_cmd_info[2], rq->es_cmd_info[3]); fmt_print("%s0x%02x = %d\n", *p++, rq->es_add_code, rq->es_add_code); fmt_print("%s0x%02x = %d\n", *p++, rq->es_qual_code, rq->es_qual_code); fmt_print("%s%d\n", *p++, rq->es_fru_code); fmt_print("%s0x%02x 0x%02x 0x%02x\n", *p++, rq->es_skey_specific[0], rq->es_skey_specific[1], rq->es_skey_specific[2]); if (rqlen >= sizeof (*rq)) { fmt_print("%s0x%02x 0x%02x%s\n", *p, rq->es_add_info[0], rq->es_add_info[1], (rqlen > sizeof (*rq)) ? " ..." : ""); } fmt_print("\n"); } /* * Labels for the various fields of the scsi_descr_sense_hdr structure */ static char *scsi_descr_sense_labels[] = { "Error class and code: ", "Sense key: ", "Additional sense length: ", "Additional sense code: ", "Additional sense code qualifier: ", "Additional sense bytes: " }; /* * Display the full descriptor sense data as returned by the device */ static void scsi_print_descr_sense(struct scsi_descr_sense_hdr *rq, int rqlen) { char **p; uint8_t *descr_offset; int valid_sense_length; struct scsi_information_sense_descr *isd; p = scsi_descr_sense_labels; if (rqlen < sizeof (struct scsi_descr_sense_hdr)) { /* * target must return at least 8 bytes of data */ return; } /* Print descriptor sense header */ fmt_print("%s0x%02x\n", *p++, (rq->ds_class << 4) + rq->ds_code); fmt_print("%s%d\n", *p++, rq->ds_key); fmt_print("%s%d\n", *p++, rq->ds_addl_sense_length); fmt_print("%s0x%02x = %d\n", *p++, rq->ds_add_code, rq->ds_add_code); fmt_print("%s0x%02x = %d\n", *p++, rq->ds_qual_code, rq->ds_qual_code); fmt_print("\n"); /* * Now print any sense descriptors. The first descriptor will * immediately follow the header */ descr_offset = (uint8_t *)(rq+1); /* Pointer arithmetic */ /* * Calculate the amount of valid sense data */ valid_sense_length = min((sizeof (struct scsi_descr_sense_hdr) + rq->ds_addl_sense_length), rqlen); /* * Iterate through the list of descriptors, stopping when we * run out of sense data. Descriptor format is: * * ... */ while ((descr_offset + *(descr_offset + 1)) <= (uint8_t *)rq + valid_sense_length) { /* * Determine descriptor type. We can use the * scsi_information_sense_descr structure as a * template since the first two fields are always the * same. */ isd = (struct scsi_information_sense_descr *)descr_offset; switch (isd->isd_descr_type) { case DESCR_INFORMATION: { uint64_t information; information = (((uint64_t)isd->isd_information[0] << 56) | ((uint64_t)isd->isd_information[1] << 48) | ((uint64_t)isd->isd_information[2] << 40) | ((uint64_t)isd->isd_information[3] << 32) | ((uint64_t)isd->isd_information[4] << 24) | ((uint64_t)isd->isd_information[5] << 16) | ((uint64_t)isd->isd_information[6] << 8) | ((uint64_t)isd->isd_information[7])); fmt_print("Information field: " "%0llx\n", information); break; } case DESCR_COMMAND_SPECIFIC: { struct scsi_cmd_specific_sense_descr *c = (struct scsi_cmd_specific_sense_descr *)isd; uint64_t cmd_specific; cmd_specific = (((uint64_t)c->css_cmd_specific_info[0] << 56) | ((uint64_t)c->css_cmd_specific_info[1] << 48) | ((uint64_t)c->css_cmd_specific_info[2] << 40) | ((uint64_t)c->css_cmd_specific_info[3] << 32) | ((uint64_t)c->css_cmd_specific_info[4] << 24) | ((uint64_t)c->css_cmd_specific_info[5] << 16) | ((uint64_t)c->css_cmd_specific_info[6] << 8) | ((uint64_t)c->css_cmd_specific_info[7])); fmt_print("Command-specific information: " "%0llx\n", cmd_specific); break; } case DESCR_SENSE_KEY_SPECIFIC: { struct scsi_sk_specific_sense_descr *ssd = (struct scsi_sk_specific_sense_descr *)isd; uint8_t *sk_spec_ptr = (uint8_t *)&ssd->sss_data; fmt_print("Sense-key specific: " "0x%02x 0x%02x 0x%02x\n", sk_spec_ptr[0], sk_spec_ptr[1], sk_spec_ptr[2]); break; } case DESCR_FRU: { struct scsi_fru_sense_descr *fsd = (struct scsi_fru_sense_descr *)isd; fmt_print("Field replaceable unit code: " "%d\n", fsd->fs_fru_code); break; } case DESCR_BLOCK_COMMANDS: { struct scsi_block_cmd_sense_descr *bsd = (struct scsi_block_cmd_sense_descr *)isd; fmt_print("Incorrect length indicator: " "%s\n", bsd->bcs_ili ? "yes" : "no"); break; } default: /* Ignore */ break; } /* * Get pointer to the next descriptor. The "additional * length" field holds the length of the descriptor except * for the "type" and "additional length" fields, so * we need to add 2 to get the total length. */ descr_offset += (isd->isd_addl_length + 2); } fmt_print("\n"); } /* * Function checks if READ DEFECT DATA command is supported * on the current disk. */ static int check_support_for_defects(void) { struct uscsi_cmd ucmd; union scsi_cdb cdb; struct scsi_defect_list def_list; struct scsi_defect_hdr *hdr; int status; char rqbuf[255]; struct scsi_extended_sense *rq; hdr = (struct scsi_defect_hdr *)&def_list; /* * First get length of list by asking for the header only. */ (void) memset((char *)&def_list, 0, sizeof (def_list)); /* * Build and execute the uscsi ioctl */ (void) memset((char *)&ucmd, 0, sizeof (ucmd)); (void) memset((char *)&cdb, 0, sizeof (union scsi_cdb)); (void) memset((char *)rqbuf, 0, 255); cdb.scc_cmd = SCMD_READ_DEFECT_LIST; FORMG1COUNT(&cdb, sizeof (struct scsi_defect_hdr)); cdb.cdb_opaque[2] = DLD_MAN_DEF_LIST | DLD_BFI_FORMAT; ucmd.uscsi_cdb = (caddr_t)&cdb; ucmd.uscsi_cdblen = CDB_GROUP1; ucmd.uscsi_bufaddr = (caddr_t)hdr; ucmd.uscsi_buflen = sizeof (struct scsi_defect_hdr); ucmd.uscsi_rqbuf = rqbuf; ucmd.uscsi_rqlen = sizeof (rqbuf); ucmd.uscsi_rqresid = sizeof (rqbuf); rq = (struct scsi_extended_sense *)ucmd.uscsi_rqbuf; status = uscsi_cmd(cur_file, &ucmd, (option_msg && diag_msg) ? F_NORMAL : F_SILENT); if (status != 0) { /* * check if read_defect_list_is_supported. */ if (ucmd.uscsi_rqstatus == STATUS_GOOD && rq->es_key == KEY_ILLEGAL_REQUEST && rq->es_add_code == INVALID_OPCODE) return (0); } return (1); } /* * Format the disk, the whole disk, and nothing but the disk. * Function will be called only for disks * which do not support read defect list command. */ static int scsi_format_without_defects(void) { struct uscsi_cmd ucmd; union scsi_cdb cdb; struct scsi_defect_hdr defect_hdr; int status; /* * Construct the uscsi format ioctl. * Use fmtdata = 0 , indicating the no source of * defects information is provided . * Function will be called only for disks * which do not support read defect list command. */ (void) memset((char *)&ucmd, 0, sizeof (ucmd)); (void) memset((char *)&cdb, 0, sizeof (union scsi_cdb)); (void) memset((char *)&defect_hdr, 0, sizeof (defect_hdr)); cdb.scc_cmd = SCMD_FORMAT; ucmd.uscsi_cdb = (caddr_t)&cdb; ucmd.uscsi_cdblen = CDB_GROUP0; ucmd.uscsi_bufaddr = (caddr_t)&defect_hdr; ucmd.uscsi_buflen = sizeof (defect_hdr); cdb.cdb_opaque[1] = 0; /* * Issue the format ioctl */ status = uscsi_cmd(cur_file, &ucmd, (option_msg && diag_msg) ? F_NORMAL : F_SILENT); return (status); } /* * Name: test_until_ready * * Description: This function is used by scsi_format and * scsi_format_raw to poll the device while the FORMAT * UNIT cdb in in progress. * * Parameters: * file descriptor to poll * * Returns: * 0 - good status * !0 - bad status */ static int test_until_ready(int fd) { int status = 1; struct uscsi_cmd ucmd; union scsi_cdb cdb; struct scsi_extended_sense sense; time_t start, check, time_left; uint16_t progress; int hour, min, sec; (void) memset((char *)&ucmd, 0, sizeof (ucmd)); (void) memset((char *)&cdb, 0, sizeof (union scsi_cdb)); ucmd.uscsi_cdb = (caddr_t)&cdb; ucmd.uscsi_cdblen = CDB_GROUP0; ucmd.uscsi_rqbuf = (caddr_t)&sense; ucmd.uscsi_rqlen = SENSE_LEN; start = check = time((time_t *)0); /* Loop sending TEST UNIT READY until format is complete */ while (status) { /* clear last request sense data */ ucmd.uscsi_rqstatus = 0; ucmd.uscsi_rqresid = 0; (void) memset((char *)&sense, 0, SENSE_LEN); /* issue test unit ready */ status = uscsi_cmd(fd, &ucmd, F_SILENT | F_RQENABLE); check = time((time_t *)0); /* If device returns not ready we get EIO */ if (status != 0 && errno == EIO) { /* Check SKSV if progress indication is avail */ if (sense.es_skey_specific[0] == 0x80) { /* Store progress indication */ progress = ((uint16_t)sense. es_skey_specific[1]) << 8; progress |= (uint16_t)sense. es_skey_specific[2]; progress = (uint16_t)(((float)progress / (float)PROGRESS_INDICATION_BASE)*100); fmt_print("\015"); /* * check to see if we can estimate * time remaining - wait until the format * is at least 5 percent complete to avoid * wildly-fluctuating time estimates */ if ((check - start) <= 0 || progress <= 5) { /* unable to estimate */ fmt_print(" %02d%% complete ", progress); } else { /* display with estimated time */ time_left = (time_t)(((float)(check - start) / (float)progress) * (float)(100 - progress)); sec = time_left % 60; min = (time_left / 60) % 60; hour = time_left / 3600; fmt_print(" %02d%% complete " "(%02d:%02d:%02d remaining) ", progress, hour, min, sec); } /* flush or the screen will not update */ (void) fflush(stdout); } } else { /* format not in progress */ if (option_msg) { fmt_print("\nRequest Sense ASC=0x%x ASCQ=0x%x", sense.es_add_code, sense.es_qual_code); } break; } /* delay so we don't waste cpu time */ (void) sleep(RETRY_DELAY); } return (status); } /* * Get the current protection type from the PROT_EN and P_TYPE */ uint8_t get_cur_protection_type(struct scsi_capacity_16 *capacity) { uint8_t cp13; uint8_t prot_en; uint8_t p_type; cp13 = ((capacity->sc_rsvd0 & 0x3f) << 2) | ((capacity->sc_prot_en & 0x01) << 1) | (capacity->sc_rto_en & 0x01); prot_en = cp13 & 0x01; if (prot_en == 0) { p_type = 0; } else { p_type = (cp13 << 4) >> 5; p_type += 1; } return (p_type); } /* * 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 (c) 1993, 2010, Oracle and/or its affiliates. All rights reserved. */ #ifndef _CTLR_SCSI_H #define _CTLR_SCSI_H #ifdef __cplusplus extern "C" { #endif #include #include /* * Rounded parameter, as returned in Extended Sense information */ #define ROUNDED_PARAMETER 0x37 /* * Timeout Value during formatting */ #ifndef SHRT_MAX #define SHRT_MAX 32767 #endif /* * Mode sense/select page header information */ struct scsi_ms_header { struct mode_header mode_header; struct block_descriptor block_descriptor; }; /* * Mode Sense Page Control */ #define MODE_SENSE_PC_CURRENT (0 << 6) #define MODE_SENSE_PC_CHANGEABLE (1 << 6) #define MODE_SENSE_PC_DEFAULT (2 << 6) #define MODE_SENSE_PC_SAVED (3 << 6) /* * Mode Select options */ #define MODE_SELECT_SP 0x01 #define MODE_SELECT_PF 0x10 /* * Minimum length of Request Sense data that we can accept */ #define MIN_REQUEST_SENSE_LEN 18 /* * "impossible" status value */ #define IMPOSSIBLE_SCSI_STATUS 0xff /* * Convert a three-byte triplet into an int */ #define TRIPLET(u, m, l) ((int)((((u))&0xff<<16) + \ (((m)&0xff)<<8) + (l&0xff))) /* * Define the amount of slop we can tolerate on a SCSI-2 mode sense. * Usually we try to deal with just the common subset between the * the SCSI-2 structure and the CCS structure. The length of the * data returned can vary between targets, so being tolerant gives * gives us a higher chance of success. */ #define PAGE1_SLOP 5 #define PAGE2_SLOP 6 #define PAGE3_SLOP 3 #define PAGE4_SLOP 8 #define PAGE8_SLOP 8 #define PAGE38_SLOP 8 /* * Minimum lengths of a particular SCSI-2 mode sense page that * we can deal with. We must reject anything less than this. */ #define MIN_PAGE1_LEN (sizeof (struct mode_err_recov)-PAGE1_SLOP) #define MIN_PAGE2_LEN (sizeof (struct mode_disco_reco)-PAGE2_SLOP) #define MIN_PAGE3_LEN (sizeof (struct mode_format)-PAGE3_SLOP) #define MIN_PAGE4_LEN (sizeof (struct mode_geometry)-PAGE4_SLOP) #define MIN_PAGE8_LEN (sizeof (struct mode_cache)-PAGE8_SLOP) #define MIN_PAGE38_LEN (sizeof (struct mode_cache_ccs)-PAGE38_SLOP) /* * Macro to extract the length of a mode sense page * as returned by a target. */ #define MODESENSE_PAGE_LEN(p) (((int)((struct mode_page *)p)->length) + \ sizeof (struct mode_page)) /* * Request this number of bytes for all mode senses. Since the * data returned is self-defining, we can accept anywhere from * the minimum for a particular page, up to this maximum. * Whatever the drive gives us, we return to the drive, delta'ed * by whatever we want to change. */ #define MAX_MODE_SENSE_SIZE 255 #ifdef __STDC__ /* * Local prototypes for ANSI C compilers */ int scsi_rdwr(int, int, diskaddr_t, int, caddr_t, int, int *); int scsi_ex_man(struct defect_list *); int scsi_ex_cur(struct defect_list *); int scsi_ex_grown(struct defect_list *); int uscsi_cmd(int, struct uscsi_cmd *, int); int uscsi_mode_sense(int, int, int, caddr_t, int, struct scsi_ms_header *); int uscsi_mode_select(int, int, int, caddr_t, int, struct scsi_ms_header *); int uscsi_inquiry(int, caddr_t, int); int uscsi_inquiry_page_86h(int, caddr_t, int); int uscsi_read_capacity(int, struct scsi_capacity_16 *); int uscsi_read_capacity_16(int, struct scsi_capacity_16 *); int scsi_translate(int, struct scsi_bfi_defect *); int scsi_dump_mode_sense_pages(int); int scsi_supported_page(int); int apply_chg_list(int, int, uchar_t *, uchar_t *, struct chg_list *); int scsi_format_time(void); uint8_t get_cur_protection_type(struct scsi_capacity_16 *); #else #ifdef sparc int scsi_ms_page1(); int scsi_ms_page2(); int scsi_ms_page3(); int scsi_ms_page4(); int scsi_read_defect_data(); int scsi_repair(); #endif /* sparc */ int scsi_rdwr(); int scsi_ck_format(); int scsi_ex_man(); int scsi_ex_cur(); int scsi_ex_grown(); int uscsi_cmd(); int uscsi_mode_sense(); int uscsi_mode_select(); int uscsi_inquiry(); int uscsi_read_capacity(); int scsi_translate(); int scsi_dump_mode_sense_pages(); int scsi_supported_page(); int apply_chg_list(); int scsi_format_time(); #endif /* __STDC__ */ #ifdef __cplusplus } #endif #endif /* _CTLR_SCSI_H */ /* * 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. */ /* * This file contains routines that manipulate the defect list. */ #include "global.h" #include #include #if defined(sparc) #include #endif /* defined(sparc) */ #include #include #include #include #include #include #include #if defined(sparc) #include #endif /* defined(sparc) */ #include "misc.h" #include "param.h" #if defined(sparc) /* * This structure is the bad block table for the current disk if * the disk uses bad-144 defect mapping. */ struct dkbad badmap; #endif /* defined(sparc) */ /* * This routine reads the defect list off the disk. It also reads in the * bad block table if the disk is a BAD144 type. The defect list is * located on the first 2 tracks of the 2nd alternate cylinder of all * disks. The bad block map is located on the first 5 even sectors of * the last track of the last cylinder. */ void read_list(struct defect_list *list) { int size, head; #if defined(sparc) int sec, status; struct bt_bad *bt; #endif /* defined(sparc) */ assert(!EMBEDDED_SCSI); /* * This flags has been introduced only for Sparc ATA IDE. * This indicates that no list manipulation is done in this controller * and hence return without any other checking. */ if (cur_ctype->ctype_flags & CF_NOWLIST) { return; } /* * Panther's working list is maintained by the controller */ if (cur_ctype->ctype_flags & CF_WLIST) { if (*cur_ops->op_ex_cur != NULL && ((*cur_ops->op_ex_cur)(list)) == 0) { if (list->header.magicno != DEFECT_MAGIC) { fmt_print("Defect list BAD\n"); } else { fmt_print("Controller working list found\n"); } return; } if (*cur_ops->op_ex_man != NULL && ((*cur_ops->op_ex_man)(list)) == 0) { if (list->header.magicno != DEFECT_MAGIC) { fmt_print("Defect list BAD\n"); } else { fmt_print("MANUFACTURER's list found\n"); } return; } fmt_print("No defect list found\n"); return; } /* * Loop for each copy of the defect list until we get a good one. */ for (head = 0; head < LISTCOUNT; head++) { /* * Try to read the list header. */ if ((*cur_ops->op_rdwr)(DIR_READ, cur_file, (diskaddr_t)chs2bn(ncyl + 1, head, 0), 1, (char *)&list->header, NULL), F_NORMAL) continue; /* * If the magic number is wrong, this copy is corrupt. */ if (list->header.magicno != DEFECT_MAGIC) continue; /* * Allocate space for the rest of the list. */ size = deflist_size(cur_blksz, list->header.count); list->list = (struct defect_entry *)zalloc(size * cur_blksz); /* * Try to read in the rest of the list. If there is an * error, or the checksum is wrong, this copy is corrupt. */ if ((*cur_ops->op_rdwr)(DIR_READ, cur_file, (diskaddr_t)chs2bn(ncyl + 1, head, 1), size, (char *)list->list, F_NORMAL, NULL) || checkdefsum(list, CK_CHECKSUM)) { /* * Destroy the list and go on. */ kill_deflist(list); continue; } /* * Got a good copy, stop searching. */ break; } #if defined(sparc) if (!(cur_ctlr->ctlr_flags & DKI_BAD144)) return; /* * The disk uses BAD144, read in the bad-block table. */ for (sec = 0; ((sec < BAD_LISTCNT * 2) && (sec < nsect)); sec += 2) { status = (*cur_ops->op_rdwr)(DIR_READ, cur_file, (diskaddr_t)chs2bn(ncyl + acyl - 1, nhead - 1, sec), 1, &badmap, F_NORMAL, NULL); if (status) continue; /* * Do a sanity check on the list read in. If it passes, * stop searching. */ if (badmap.bt_mbz != 0) continue; for (bt = badmap.bt_bad; bt - badmap.bt_bad < NDKBAD; bt++) { if (bt->bt_cyl < 0) break; if (bt->bt_trksec < 0) continue; head = bt->bt_trksec >> 8; if ((bt->bt_cyl >= pcyl) || (head >= nhead) || ((bt->bt_trksec & 0xff) >= sectors(head))) { status = -1; break; } } if (status) continue; return; } /* * If we couldn't find the bad block table, initialize it to * zero entries. */ for (bt = badmap.bt_bad; bt - badmap.bt_bad < NDKBAD; bt++) bt->bt_cyl = bt->bt_trksec = -1; badmap.bt_mbz = badmap.bt_csn = badmap.bt_flag = 0; #endif /* defined(sparc) */ } /* * This routine either checks or calculates the checksum for a defect * list, depending on the mode parameter. In check mode, it returns * whether or not the checksum is correct. */ int checkdefsum(struct defect_list *list, int mode) { register int *lp, i, sum = 0; /* * Perform the rolling xor to get what the checksum should be. */ lp = (int *)list->list; for (i = 0; i < (list->header.count * sizeof (struct defect_entry) / sizeof (int)); i++) sum ^= *(lp + i); /* * If in check mode, return whether header checksum was correct. */ if (mode == CK_CHECKSUM) return (sum != list->header.cksum); /* * If in create mode, set the header checksum. */ else { list->header.cksum = sum; return (0); } } /* * This routine prints a single defect to stdout in a readable format. */ void pr_defect(struct defect_entry *def, int num) { /* * Make defect numbering look 1 relative. */ ++num; /* * Print out common values. */ fmt_print("%4d%8d%7d", num, def->cyl, def->head); /* * The rest of the values may be unknown. If they are, just * print blanks instead. Also, only print length only if bfi is * known, and assume that a known bfi implies an unknown sect. */ if (def->bfi != UNKNOWN) { fmt_print("%8d", def->bfi); if (def->nbits != UNKNOWN) fmt_print("%8d", def->nbits); } else { fmt_print(" "); fmt_print("%8d", def->sect); fmt_print("%8llu", chs2bn(def->cyl, def->head, def->sect)); } fmt_print("\n"); } /* * This routine calculates where in a defect list a given defect should * be sorted. It returns the index that the defect should become. The * algorithm used sorts all bfi based defects by cylinder/head/bfi, and * adds all logical sector defects to the end of the list. This is * necessary because the ordering of logical sector defects is significant * when sector slipping is employed. */ int sort_defect(struct defect_entry *def, struct defect_list *list) { struct defect_entry *ptr; /* * If it's a logical sector defect, return the entry at the end * of the list. */ if (def->bfi == UNKNOWN) return (list->header.count); /* * It's a bfi defect. Loop through the defect list. */ for (ptr = list->list; ptr - list->list < list->header.count; ptr++) { /* * If we get to a logical sector defect, put this defect * right before it. */ if (ptr->bfi == UNKNOWN) goto found; /* * If we get to a defect that is past this one in * cylinder/head/bfi, put this defect right before it. */ if (def->cyl < ptr->cyl) goto found; if (def->cyl != ptr->cyl) continue; if (def->head < ptr->head) goto found; if (def->head != ptr->head) continue; if (def->bfi < ptr->bfi) goto found; } found: /* * Return the index to put the defect at. */ return (ptr - list->list); } /* * This routine writes the defect list on the back on the disk. It also * writes the bad block table to disk if bad-144 mapping applies to the * current disk. */ void write_deflist(struct defect_list *list) { int size, head, status; #if defined(sparc) int sec; caddr_t bad_ptr = (caddr_t)&badmap; #endif /* defined(sparc) */ assert(!EMBEDDED_SCSI); /* * Sparc ATA IDE. * This indicates that no list manipulation is done in this controller * and hence return without any other checking. */ if (cur_ctype->ctype_flags & CF_NOWLIST) { return; } /* * Panther's working list is maintained by the controller */ if (cur_ctype->ctype_flags & CF_WLIST) { (*cur_ops->op_wr_cur)(list); return; } /* * If the list is null, there is nothing to write. */ if (list->list != NULL) { /* * calculate how many sectors the defect list will occupy. */ size = deflist_size(cur_blksz, list->header.count); /* * Loop for each copy of the list to be written. Write * out the header of the list followed by the data. */ for (head = 0; head < LISTCOUNT; head++) { status = (*cur_ops->op_rdwr)(DIR_WRITE, cur_file, (diskaddr_t)chs2bn(ncyl + 1, head, 0), 1, (char *)&list->header, F_NORMAL, NULL); if (status) { err_print( "Warning: error saving defect list.\n"); continue; } status = (*cur_ops->op_rdwr)(DIR_WRITE, cur_file, (diskaddr_t)chs2bn(ncyl + 1, head, 1), size, (char *)list->list, F_NORMAL, NULL); if (status) err_print( "Warning: error saving defect list.\n"); } } if (!(cur_ctlr->ctlr_flags & DKI_BAD144)) return; #if defined(sparc) /* * Current disk uses bad-144 mapping. Loop for each copy of the * bad block table to be written and write it out. */ for (sec = 0; ((sec < BAD_LISTCNT * 2) && (sec < nsect)); sec += 2) { status = (*cur_ops->op_rdwr)(DIR_WRITE, cur_file, (diskaddr_t)chs2bn(ncyl + acyl - 1, nhead - 1, sec), 1, &badmap, F_NORMAL, NULL); if (status) { err_print( "Warning: error saving bad block map table.\n"); continue; } } /* * Execute an ioctl to tell unix about the new bad block table. */ if (ioctl(cur_file, HDKIOCSBAD, &bad_ptr)) err_print( "Warning: error telling SunOS bad block map table.\n"); #endif /* defined(sparc) */ } /* * This routine adds a logical sector to the given defect list. */ void add_ldef(diskaddr_t blkno, struct defect_list *list) { struct defect_entry def; int index; /* * Calculate the fields for the defect struct. */ def.cyl = bn2c(blkno); def.head = bn2h(blkno); def.sect = bn2s(blkno); /* * Initialize the unknown fields. */ def.bfi = def.nbits = UNKNOWN; /* * Calculate the index into the list that the defect belongs at. */ index = sort_defect(&def, list); /* * Add the defect to the list. */ add_def(&def, list, index); } /* * This routine adds the given defect struct to the defect list at * a precalculated index. */ void add_def(struct defect_entry *def, struct defect_list *list, int index) { int count, i; /* * If adding this defect makes the list overflow into another * sector, allocate the necessary space. */ count = list->header.count; if (deflist_size(cur_blksz, count + 1) > deflist_size(cur_blksz, count)) list->list = (struct defect_entry *)rezalloc((void *)list->list, deflist_size(cur_blksz, count + 1) * cur_blksz); /* * Slip all the defects after this one down one slot in the list. */ for (i = count; i > index; i--) *(list->list + i) = *(list->list + i - 1); /* * Fill in the created hole with this defect. */ *(list->list + i) = *def; /* * Increment the count and calculate a new checksum. */ list->header.count++; (void) checkdefsum(list, CK_MAKESUM); } /* * This routine sets the given defect list back to null. */ void kill_deflist(struct defect_list *list) { /* * If it's already null, we're done. */ if (list->list == NULL) return; /* * Free the malloc'd space it's using. */ destroy_data((char *)list->list); /* * Mark it as null, and clear any flags. */ list->list = NULL; list->flags = 0; } /* * This routine returns the defect list size * according to the sector size. */ int deflist_size(int secsz, int sz) { int rval; if (secsz == 0) { secsz = SECSIZE; } rval = sz ? ((sz * sizeof (struct defect_entry) + secsz - 1) / secsz) : 1; return (rval); } /* * 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. */ #ifndef _DEFECT_H #define _DEFECT_H #ifdef __cplusplus extern "C" { #endif /* * This file contains definitions related to the defect list. */ extern struct defect_list work_list; extern struct dkbad badmap; /* * This is the structure of the header of a defect list. It is always * the first sector on a track containing a defect list. */ struct defectHeader { uint_t magicno; int count; int cksum; int save[125]; }; /* * This is the structure of a defect. Defects are stored on the disk * as an array of these structures following the defect header. */ struct defect_entry { short cyl; short head; short sect; short nbits; int bfi; }; /* * This is the internal representation of a defect list. We store * the header statically, but dynamically allocate space for the * actual defects, since their number may vary. The flags field is * used to keep track of whether the list has been modified. */ struct defect_list { struct defectHeader header; struct defect_entry *list; int flags; }; /* * This defines the number of copies of the defect list kept on the disk. * They are stored 1/track, starting at track 0 of the second alternate cyl. */ #define LISTCOUNT 2 /* * These defines are the flags for the defect list. */ #define LIST_DIRTY 0x01 /* List needs to be synced */ #define LIST_RELOAD 0x02 /* Reload list after formatting (SCSI) */ #define LIST_PGLIST 0x04 /* embedded SCSI - both manufacturer's (P) */ /* and grown (G) list */ /* * Miscellaneous defines. */ #define DEFECT_MAGIC 0x89898989 /* magic no for defect lists */ #define NO_CHECKSUM 0x1 /* magic no for no checksum in */ /* defect list */ #define UNKNOWN (-1) /* value used in defect fields */ #define DEF_PRINTHEADER " num cyl hd bfi len sec blk\n" /* * This defines the number of copies of the bad block table kept on the * disk. They are stored in the first 5 even sectors on the last track * of the disk. Note: this also defines the number of backup labels, * which are kept in the first 5 odd sectors of the appropriate * track. */ #define BAD_LISTCNT 5 /* * Prototypes for ANSI C compilers */ void read_list(struct defect_list *list); int makebfi(struct defect_list *list, struct defect_entry *def); void calc_bfi(struct defect_list *list, struct defect_entry *def, struct defect_entry *end, int skew); int makelsect(struct defect_list *list); int checkdefsum(struct defect_list *list, int mode); void pr_defect(struct defect_entry *def, int num); int sort_defect(struct defect_entry *def, struct defect_list *list); void write_deflist(struct defect_list *list); void add_ldef(diskaddr_t blkno, struct defect_list *list); void add_def(struct defect_entry *def, struct defect_list *list, int index); void kill_deflist(struct defect_list *list); /* * This defines the size (in sectors) of the defect array given the number * of defects in the array. It must be rounded to a sector boundary since * that is the atomic disk size. We make a zero length list use up a * sector because it is convenient to have malloc'd space in every * non-null list. */ int deflist_size(int secsz, int sz); #ifdef __cplusplus } #endif #endif /* _DEFECT_H */ /* * 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. */ /* * This file contains functions that implement the fdisk menu commands. */ #include "global.h" #include #include #include #include #include #include #include #include #include #include "main.h" #include "analyze.h" #include "menu.h" #include "menu_command.h" #include "menu_defect.h" #include "menu_partition.h" #if defined(_FIRMWARE_NEEDS_FDISK) #include "menu_fdisk.h" #endif /* defined(_FIRMWARE_NEEDS_FDISK) */ #include "param.h" #include "misc.h" #include "label.h" #include "startup.h" #include "partition.h" #include "prompts.h" #include "checkdev.h" #include "io.h" #include "ctlr_scsi.h" #include "auto_sense.h" static int generic_ck_format(void); static int generic_rdwr(int dir, int fd, diskaddr_t blkno, int secnt, caddr_t bufaddr, int flags, int *xfercntp); struct ctlr_ops genericops = { generic_rdwr, generic_ck_format, 0, 0, 0, 0, 0, }; /* * Check to see if the disk has been formatted. * If we are able to read the first track, we conclude that * the disk has been formatted. */ static int generic_ck_format(void) { int status; /* * Try to read the first four blocks. */ status = generic_rdwr(DIR_READ, cur_file, 0, 4, (caddr_t)cur_buf, F_SILENT, NULL); return (!status); } /* * Read or write the disk. * Temporary interface until IOCTL interface finished. */ /*ARGSUSED*/ static int generic_rdwr(int dir, int fd, diskaddr_t blkno, int secnt, caddr_t bufaddr, int flags, int *xfercntp) { offset_t tmpsec, status, tmpblk; int ret; tmpsec = (offset_t)secnt * cur_blksz; tmpblk = (offset_t)blkno * cur_blksz; #if defined(_FIRMWARE_NEEDS_FDISK) /* Use "p0" file to seek/read the data */ (void) open_cur_file(FD_USE_P0_PATH); #endif if (dir == DIR_READ) { status = llseek(fd, tmpblk, SEEK_SET); if (status != tmpblk) { ret = (int)status; goto out; } status = read(fd, bufaddr, (size_t)tmpsec); if (status != tmpsec) ret = (int)tmpsec; else ret = 0; } else { status = llseek(fd, tmpblk, SEEK_SET); if (status != tmpblk) { ret = (int)status; goto out; } status = write(fd, bufaddr, (size_t)tmpsec); if (status != tmpsec) ret = (int)tmpsec; else ret = 0; } out: #if defined(_FIRMWARE_NEEDS_FDISK) /* Restore cur_file with cur_disk->disk_path */ (void) open_cur_file(FD_USE_CUR_DISK_PATH); #endif return (ret); } # # Copyright 2005 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # 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 # # Data file for the 'format' program. This file defines the known # disks, disk types, and partition maps. # # # Following is a list of sample embedded SCSI disk configurations. # disk_type = "Quantum ProDrive 80S" \ : ctlr = SCSI : fmt_time = 1 \ : cache = 0x07 : trks_zone = 6 : atrks = 0 : asect = 1 \ : ncyl = 832 : acyl = 2 : pcyl = 834 : nhead = 6 : nsect = 34 \ : rpm = 3662 : bpt = 16896 #disk_type = "CDC Wren VII 94601-12G" \ # : ctlr = SCSI : fmt_time = 4 \ # : cache = 0x11 : trks_zone = 15 : asect = 5 : atrks = 30 \ # : ncyl = 1703 : acyl = 2 : pcyl = 1931 : nhead = 15 : nsect = 80 \ # : rpm = 3597 : bpt = 41301 # # An early CDC Wren IV, shipped with the 386i # #disk_type = "CDC Wren IV 94171-327" \ # : ctlr = SCSI : fmt_time = 4 \ # : cache = 0x11 : trks_zone = 9 : asect = 3 \ # : ncyl = 1520 : acyl = 2 : pcyl = 1549 : nhead = 9 : nsect = 46 \ # : rpm = 3600 : bpt = 20833 # # This is the list of Sun supported disks for embedded SCSI. # disk_type = "Quantum ProDrive 105S" \ : ctlr = SCSI : fmt_time = 1 \ : cache = 0x07 : trks_zone = 6 : atrks = 0 : asect = 1 \ : ncyl = 974 : acyl = 2 : pcyl = 1019 : nhead = 6 : nsect = 35 \ : rpm = 3662 : bpt = 16896 disk_type = "CDC Wren IV 94171-344" \ : ctlr = SCSI : fmt_time = 4 \ : cache = 0x11 : trks_zone = 9 : asect = 3 \ : ncyl = 1545 : acyl = 2 : pcyl = 1549 : nhead = 9 : nsect = 46 \ : rpm = 3600 : bpt = 20833 disk_type = "SUN0104" \ : ctlr = SCSI : fmt_time = 1 \ : trks_zone = 6 : atrks = 0 : asect = 1 \ : ncyl = 974 : acyl = 2 : pcyl = 1019 : nhead = 6 : nsect = 35 \ : rpm = 3662 : bpt = 16896 disk_type = "SUN0207" \ : ctlr = SCSI \ : trks_zone = 9 : atrks = 2: asect = 4 \ : ncyl = 1254 : acyl = 2 : pcyl = 1272 : nhead = 9 : nsect = 36 \ : rpm = 3600 : bpt = 18432 disk_type = "SUN0327" \ : ctlr = SCSI : fmt_time = 4 \ : cache = 0x11 : trks_zone = 9 : asect = 3 \ : ncyl = 1545 : acyl = 2 : pcyl = 1549 : nhead = 9 : nsect = 46 \ : rpm = 3600 : bpt = 20833 disk_type = "SUN0340" \ : ctlr = SCSI : fmt_time = 2 \ : trks_zone = 6 \ : ncyl = 1538 : acyl = 2 : pcyl = 1544 : nhead = 6 : nsect = 72 \ : rpm = 4200 disk_type = "SUN0424" \ : ctlr = SCSI : fmt_time = 4 \ : trks_zone = 9 : asect = 2 \ : ncyl = 1151 : acyl = 2 : pcyl = 2500 : nhead = 9 : nsect = 80 \ : rpm = 4400 : bpt = 26000 disk_type = "SUN0535" \ : ctlr = SCSI : fmt_time = 4 \ : ncyl = 1866 : acyl = 2 : pcyl = 2500 : nhead = 7 : nsect = 80 \ : rpm = 5400 disk_type = "SUN0669" \ : ctlr = SCSI : fmt_time = 4 \ : trks_zone = 15 : asect = 5 : atrks = 30 \ : ncyl = 1614 : acyl = 2 : pcyl = 1632 : nhead = 15 : nsect = 54 \ : rpm = 3600 : bpt = 31410 disk_type = "SUN1.0G" \ : ctlr = SCSI : fmt_time = 4 \ : trks_zone = 15 : asect = 5 : atrks = 30 \ : ncyl = 1703 : acyl = 2 : pcyl = 1931 : nhead = 15 : nsect = 80 \ : rpm = 3597 : bpt = 41301 disk_type = "SUN1.05" \ : ctlr = SCSI : fmt_time = 4 \ : ncyl = 2036 : acyl = 2 : pcyl = 2038 : nhead = 14 : nsect = 72 \ : rpm = 5400 disk_type = "SUN1.3G" \ : ctlr = SCSI : fmt_time = 4 \ : trks_zone = 17 : asect = 6 : atrks = 17 \ : ncyl = 1965 : acyl = 2 : pcyl = 3500 : nhead = 17 : nsect = 80 \ : rpm = 5400 : bpt = 44823 disk_type = "SUN2.1G" \ : ctlr = SCSI : fmt_time = 4 \ : ncyl = 2733 : acyl = 2 : pcyl = 3500 : nhead = 19 : nsect = 80 \ : rpm = 5400 : bpt = 44823 disk_type = "SUN2.9G" \ : ctlr = SCSI : fmt_time = 4 \ : ncyl = 2734 : acyl = 2 : pcyl = 3500 : nhead = 21 : nsect = 99 \ : rpm = 5400 disk_type = "Zip 100" \ : ctlr = SCSI \ : ncyl = 95 : acyl = 0 : pcyl = 95 : nhead = 64 : nsect = 32 \ : rpm = 2941 : bpt = 16384 disk_type = "Zip 250" \ : ctlr = SCSI : ncyl = 6117 : acyl = 2 : pcyl = 6119 \ : nhead = 2 : nsect = 40 : rpm = 3600 disk_type = "Peerless 10GB" \ : ctlr = SCSI : ncyl = 9583 : acyl = 2 : pcyl = 9585 \ : nhead = 64 : nsect = 32 : rpm = 3600 # # Following is a list of sample partition tables for embedded SCSI disks. # partition = "Quantum ProDrive 80S" \ : disk = "Quantum ProDrive 80S" : ctlr = SCSI \ : 0 = 0, 16384 : 1 = 64, 32512 : 2 = 0, 198144 : 6 = 191, 149248 #partition = "CDC Wren VII 94601-12G" \ # : disk = "CDC Wren VII 94601-12G" : ctlr = SCSI \ # : 0 = 0, 32400 : 1 = 27, 64800 : 2 = 0, 2043600 : 6 = 81, 1946400 #partition = "CDC Wren IV 94171-327" \ # : disk = "CDC Wren IV 94171-327" : ctlr = SCSI \ # : 0 = 0, 31878 : 1 = 77, 59616 : 2 = 0, 629280 : 6 = 221, 527786 # # This is the list of partition tables for embedded SCSI controllers. # partition = "Quantum ProDrive 105S" \ : disk = "Quantum ProDrive 105S" : ctlr = SCSI \ : 0 = 0, 16170 : 1 = 77, 28140 : 2 = 0, 204540 : 6 = 211, 160230 partition = "CDC Wren IV 94171-344, small root" \ : disk = "CDC Wren IV 94171-344" : ctlr = SCSI \ : 0 = 0, 16974 : 1 = 41, 33948 : 2 = 0, 639630 : 6 = 123, 588708 partition = "CDC Wren IV 94171-344" \ : disk = "CDC Wren IV 94171-344" : ctlr = SCSI \ : 0 = 0, 31878 : 1 = 77, 59616 : 2 = 0, 639630 : 6 = 221, 548136 partition = "SUN0104" \ : disk = "SUN0104" : ctlr = SCSI \ : 0 = 0, 16170 : 1 = 77, 28140 : 2 = 0, 204540 : 6 = 211, 160230 partition = "SUN0207" \ : disk = "SUN0207" : ctlr = SCSI \ : 0 = 0, 33048 : 1 = 102, 65448 : 2 = 0, 406296 : 6 = 304, 307800 partition = "SUN0327" \ : disk = "SUN0327" : ctlr = SCSI \ : 0 = 0, 31878 : 1 = 77, 59616 : 2 = 0, 639630 : 6 = 221, 548136 partition = "SUN0340" \ : disk = "SUN0340" : ctlr = SCSI \ : 0 = 0, 86400 : 1 = 200, 66960 : 2 = 0, 664416 : 6 = 355, 446256 \ : 7 = 1388, 64800 partition = "SUN0424" \ : disk = "SUN0424" : ctlr = SCSI \ : 0 = 0, 33120 : 1 = 46, 65520 : 2 = 0, 828720 : 6 = 137, 730080 partition = "SUN0535" \ : disk = "SUN0535" : ctlr = SCSI \ : 0 = 0, 64400 : 1 = 115, 103600 : 2 = 0, 1044960 : 6 = 300, 876960 partition = "SUN0669" \ : disk = "SUN0669" : ctlr = SCSI \ : 0 = 0, 32400 : 1 = 40, 64800 : 2 = 0, 1307340 : 6 = 120, 1210140 partition = "SUN1.0G" \ : disk = "SUN1.0G" : ctlr = SCSI \ : 0 = 0, 32400 : 1 = 27, 64800 : 2 = 0, 2043600 : 6 = 81, 1946400 partition = "SUN1.05" \ : disk = "SUN1.05" : ctlr = SCSI \ : 0 = 0, 66528 : 1 = 66, 133056 : 2 = 0, 2052288 : 6 = 198, 1852704 partition = "SUN1.3G" \ : disk = "SUN1.3G" : ctlr = SCSI \ : 0 = 0, 34000 : 1 = 25, 133280 : 2 = 0, 2672400 : 6 = 123, 2505120 partition = "SUN2.1G" \ : disk = "SUN2.1G" : ctlr = SCSI \ : 0 = 0, 62320 : 1 = 41, 197600 : 2 = 0, 4154160 : 6 = 171, 3894240 partition = "SUN2.9G" \ : disk = "SUN2.9G" : ctlr = SCSI \ : 0 = 0, 195426 : 1 = 94, 390852 : 2 = 0, 5683986 : 6 = 282, 5097708 partition = "Zip 100" \ : disk = "Zip 100" : ctlr = SCSI \ : 0 = 0, 194560 : 2 = 0, 194560 partition = "Zip 250" \ : disk = "Zip 250" : ctlr = SCSI \ : 0 = 0, 489360 : 2 = 0, 489360 partition = "Peerless 10GB" \ : disk = "Peerless 10GB" : ctlr = SCSI \ : 0 = 0, 19625984 : 2 = 0, 19625984 /* * 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 (c) 1993, 2010, Oracle and/or its affiliates. All rights reserved. */ #ifndef _GLOBAL_H #define _GLOBAL_H #ifdef __cplusplus extern "C" { #endif /* * Definitions for Label types: L_TYPE_SOLORIS is the default Sun label * a.k.a VTOC. L_TYPE_EFI is the EFI label type. */ #define L_TYPE_SOLARIS 0 #define L_TYPE_EFI 1 #ifndef UINT_MAX64 #define UINT_MAX64 0xffffffffffffffffULL #endif #ifndef UINT_MAX32 #define UINT_MAX32 0xffffffffU #endif #if !defined(_EXTVTOC) #define _EXTVTOC /* extented vtoc (struct extvtoc) format is used */ #endif /* * This file contains global definitions and declarations. It is intended * to be included by everyone. */ #include #include #include #include #include #include #include #include #include #include #include #include "hardware_structs.h" #include "defect.h" #include "io.h" #include #include /* * These declarations are global state variables. */ extern struct disk_info *disk_list; /* list of found disks */ extern struct ctlr_info *ctlr_list; /* list of found ctlrs */ extern char cur_menu; /* current menu level */ extern char last_menu; /* last menu level */ extern char option_msg; /* extended message options */ extern char diag_msg; /* extended diagnostic msgs */ extern char option_s; /* silent mode option */ extern char *option_f; /* input redirect option */ extern char *option_l; /* log file option */ extern FILE *log_file; /* log file pointer */ extern char *option_d; /* forced disk option */ extern char *option_t; /* forced disk type option */ extern char *option_p; /* forced partition table option */ extern char *option_x; /* data file redirection option */ extern FILE *data_file; /* data file pointer */ extern char *file_name; /* current data file name */ /* for useful error messages */ extern int expert_mode; /* enable for expert mode */ /* commands */ extern int need_newline; /* for correctly formatted output */ extern int dev_expert; /* enable for developer mode */ /* commands */ /* * These declarations are used for quick access to information about * the disk being worked on. */ extern int cur_file; /* file descriptor for current disk */ extern int cur_flags; /* flags for current disk */ extern int cur_label; /* current label type */ extern uint_t cur_blksz; /* currect disk block size */ extern struct disk_info *cur_disk; /* current disk */ extern struct disk_type *cur_dtype; /* current dtype */ extern struct ctlr_info *cur_ctlr; /* current ctlr */ extern struct ctlr_type *cur_ctype; /* current ctype */ extern struct ctlr_ops *cur_ops; /* current ctlr's ops vector */ extern struct partition_info *cur_parts; /* current disk's partitioning */ extern struct defect_list cur_list; /* current disk's defect list */ extern void *cur_buf; /* current disk's I/O buffer */ extern void *pattern_buf; /* current disk's pattern buffer */ extern uint_t pcyl; /* # physical cyls */ extern uint_t ncyl; /* # data cyls */ extern uint_t acyl; /* # alt cyls */ extern uint_t nhead; /* # heads */ extern uint_t phead; /* # physical heads */ extern uint_t nsect; /* # data sects/track */ extern uint_t psect; /* # physical sects/track */ extern uint_t apc; /* # alternates/cyl */ extern uint_t solaris_offset; /* Solaris offset, this value is zero */ /* for non-fdisk machines. */ extern int prot_type; /* protection type to format disk */ #if defined(_SUNOS_VTOC_16) extern uint_t bcyl; /* # other cyls */ #endif /* defined(_SUNOS_VTOC_16) */ extern struct mboot boot_sec; /* fdisk partition info */ extern uint_t xstart; /* solaris partition start */ extern char x86_devname[MAXNAMELEN]; /* saved device name for fdisk */ /* information accesses */ extern struct mctlr_list *controlp; /* master controller list ptr */ /* * These defines are used to manipulate the physical characteristics of * the current disk. */ #define sectors(h) ((h) == nhead - 1 ? nsect - apc : nsect) #define spc() (nhead * nsect - apc) #define chs2bn(c, h, s) (((diskaddr_t)(c) * spc() + (h) * nsect + (s))) #define bn2c(bn) (uint_t)((diskaddr_t)(bn) / spc()) #define bn2h(bn) (uint_t)(((diskaddr_t)(bn) % spc()) / nsect) #define bn2s(bn) (uint_t)(((diskaddr_t)(bn) % spc()) % nsect) #define datasects() (ncyl * spc()) #define totalsects() ((ncyl + acyl) * spc()) #define physsects() (pcyl * spc()) /* * Macro to convert a device number into a partition number */ #define PARTITION(dev) (minor(dev) & 0x07) /* * These values define flags for the current disk (cur_flags). */ #define DISK_FORMATTED 0x01 /* disk is formatted */ #define LABEL_DIRTY 0x02 /* label has been scribbled */ /* * These flags are for the controller type flags field. */ #define CF_NONE 0x0000 /* NO FLAGS */ #define CF_BLABEL 0x0001 /* backup labels in funny place */ #define CF_DEFECTS 0x0002 /* disk has manuf. defect list */ #define CF_APC 0x0004 /* ctlr uses alternates per cyl */ #define CF_SMD_DEFS 0x0008 /* ctlr does smd defect handling */ #define CF_SCSI 0x0040 /* ctlr is for SCSI disks */ #define CF_EMBEDDED 0x0080 /* ctlr is for embedded SCSI disks */ #define CF_IPI 0x0100 /* ctlr is for IPI disks */ #define CF_WLIST 0x0200 /* ctlt handles working list */ #define CF_NOFORMAT 0x0400 /* Manufacture formatting only */ /* * This flag has been introduced only for SPARC ATA. Which has been approved * at that time with the agreement in the next fix it will be removed and the * format will be revamped with controller Ops structure not to have * any operation to be NULL. As it makes things more modular. * * This flag is also used for PCMCIA pcata driver. * The flag prevents reading or writing a defect list on the disk * testing and console error reporting still work normally. * This is appropriate for the PCMCIA disks which often have MS/DOS filesystems * and have not allocated any space for alternate cylinders to keep * the bab block lists. */ #define CF_NOWLIST 0x0800 /* Ctlr doesnot handle working list */ /* * Do not require confirmation to extract defect lists on SCSI * and IPI drives, since this operation is instantaneous */ #define CF_CONFIRM (CF_SCSI|CF_IPI) /* * Macros to make life easier */ #define SMD (cur_ctype->ctype_flags & CF_SMD_DEFS) #define SCSI (cur_ctype->ctype_flags & CF_SCSI) #define EMBEDDED_SCSI ((cur_ctype->ctype_flags & (CF_SCSI|CF_EMBEDDED)) == \ (CF_SCSI|CF_EMBEDDED)) /* * These flags are for the disk type flags field. */ #define DT_NEED_SPEFS 0x01 /* specifics fields are uninitialized */ /* * These defines are used to access the ctlr specific * disk type fields (based on ctlr flags). */ #define dtype_bps dtype_specifics[0] /* bytes/sector */ #define dtype_dr_type dtype_specifics[1] /* drive type */ #define dtype_dr_type_data dtype_specifics[2] /* drive type in data file */ /* * These flags are for the disk info flags field. */ #define DSK_LABEL 0x01 /* disk is currently labelled */ #define DSK_LABEL_DIRTY 0x02 /* disk auto-sensed, but not */ /* labeled yet. */ #define DSK_AUTO_CONFIG 0x04 /* disk was auto-configured */ #define DSK_RESERVED 0x08 /* disk is reserved by other host */ #define DSK_UNAVAILABLE 0x10 /* disk not available, could be */ /* currently formatting */ /* * These flags are used to control disk command execution. */ #define F_NORMAL 0x00 /* normal operation */ #define F_SILENT 0x01 /* no error msgs at all */ #define F_ALLERRS 0x02 /* return any error, not just fatal */ #define F_RQENABLE 0x04 /* no error msgs at all */ /* * Directional parameter for the op_rdwr controller op. */ #define DIR_READ 0 #define DIR_WRITE 1 /* * These defines are the mode parameter for the checksum routines. */ #define CK_CHECKSUM 0 /* check checksum */ #define CK_MAKESUM 1 /* generate checksum */ /* * This is the base character for partition identifiers */ #define PARTITION_BASE '0' /* * Base pathname for devfs names to be stripped from physical name. */ #define DEVFS_PREFIX "/devices" /* * Protection type by SCSI-3 */ #define PROT_TYPE_0 0 #define PROT_TYPE_1 1 #define PROT_TYPE_2 2 #define PROT_TYPE_3 3 #define NUM_PROT_TYPE 4 /* * Function prototypes ... Both for ANSI and non-ANSI C compilers */ #ifdef __STDC__ int copy_solaris_part(struct ipart *); int good_fdisk(void); int fdisk_physical_name(char *); #else /* __STDC__ */ int copy_solaris_part(); int good_fdisk(); int fdisk_physical_name(); #endif /* __STDC__ */ #ifdef __cplusplus } #endif #endif /* _GLOBAL_H */ /* * 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. * Copyright 2015 Nexenta Systems, Inc. All rights reserved. * Copyright (c) 2016 by Delphix. All rights reserved. */ #ifndef _HARDWARE_STRUCTS_H #define _HARDWARE_STRUCTS_H #ifdef __cplusplus extern "C" { #endif #include #include #include #include /* * This file contains definitions of data structures pertaining to disks * and controllers. */ /* * This structure describes a specific disk. These structures are in a * linked list because they are malloc'd as disks are found during the * initial search. */ struct disk_info { int label_type; /* EFI or non-EFI disk */ struct dk_cinfo disk_dkinfo; /* controller config info */ struct disk_type *disk_type; /* ptr to physical info */ struct partition_info *disk_parts; /* ptr to partition info */ struct dk_gpt *efi_parts; /* ptr to partition info */ struct ctlr_info *disk_ctlr; /* ptr to disk's ctlr */ struct disk_info *disk_next; /* ptr to next disk */ struct ipart fdisk_part; /* fdisk partition info */ int disk_flags; /* misc gotchas */ char *disk_name; /* name of the disk */ char *disk_path; /* pathname to device */ char *devfs_name; /* devfs name for device */ char v_volume[LEN_DKL_VVOL]; /* volume name from label */ /* (no terminating null) */ uint_t disk_lbasize; /* disk block size */ }; #define NSPECIFICS 8 /* * This structure describes a type (model) of drive. It is malloc'd * and filled in as the data file is read and when a type 'other' disk * is selected. The link is used to make a list of all drive types * supported by a ctlr type. */ struct disk_type { char *dtype_asciilabel; /* drive identifier */ int dtype_flags; /* flags for disk type */ ulong_t dtype_options; /* flags for options */ uint_t dtype_fmt_time; /* format time */ uint_t dtype_bpt; /* # bytes per track */ uint_t dtype_ncyl; /* # of data cylinders */ uint_t dtype_acyl; /* # of alternate cylinders */ uint_t dtype_pcyl; /* # of physical cylinders */ uint_t dtype_nhead; /* # of heads */ uint_t dtype_phead; /* # of physical heads */ uint_t dtype_nsect; /* # of data sectors/track */ uint_t dtype_psect; /* # physical sectors/track */ uint_t dtype_rpm; /* rotations per minute */ int dtype_cyl_skew; /* cylinder skew */ int dtype_trk_skew; /* track skew */ uint_t dtype_trks_zone; /* # tracks per zone */ uint_t dtype_atrks; /* # alt. tracks */ uint_t dtype_asect; /* # alt. sectors */ int dtype_cache; /* cache control */ int dtype_threshold; /* cache prefetch threshold */ int dtype_read_retries; /* read retries */ int dtype_write_retries; /* write retries */ int dtype_prefetch_min; /* cache min. prefetch */ int dtype_prefetch_max; /* cache max. prefetch */ uint_t dtype_specifics[NSPECIFICS]; /* ctlr specific drive info */ struct chg_list *dtype_chglist; /* mode sense/select */ /* change list - scsi */ struct partition_info *dtype_plist; /* possible partitions */ struct disk_type *dtype_next; /* ptr to next drive type */ /* * Added so that we can print a useful diagnostic if * inconsistent definitions found in multiple files. */ char *dtype_filename; /* filename where defined */ int dtype_lineno; /* line number in file */ char *vendor; char *product; char *revision; uint64_t capacity; }; struct efi_info { char *vendor; char *product; char *revision; uint64_t capacity; struct dk_gpt *e_parts; }; /* * This structure describes a specific ctlr. These structures are in * a linked list because they are malloc'd as ctlrs are found during * the initial search. */ struct ctlr_info { char ctlr_cname[DK_DEVLEN+1]; /* name of ctlr */ char ctlr_dname[DK_DEVLEN+1]; /* name of disks */ ushort_t ctlr_flags; /* flags for ctlr */ short ctlr_num; /* number of ctlr */ int ctlr_addr; /* address of ctlr */ uint_t ctlr_space; /* bus space it occupies */ int ctlr_prio; /* interrupt priority */ int ctlr_vec; /* interrupt vector */ struct ctlr_type *ctlr_ctype; /* ptr to ctlr type info */ struct ctlr_info *ctlr_next; /* ptr to next ctlr */ }; /* * This structure describes a type (model) of ctlr. All supported ctlr * types are built into the program statically, they cannot be added by * the user. */ struct ctlr_type { ushort_t ctype_ctype; /* type of ctlr */ char *ctype_name; /* name of ctlr type */ struct ctlr_ops *ctype_ops; /* ptr to ops vector */ int ctype_flags; /* flags for gotchas */ struct disk_type *ctype_dlist; /* list of disk types */ }; /* * This structure is the operation vector for a controller type. It * contains pointers to all the functions a controller type can support. */ struct ctlr_ops { int (*op_rdwr)(); /* read/write - mandatory */ int (*op_ck_format)(); /* check format - mandatory */ int (*op_format)(); /* format - mandatory */ int (*op_ex_man)(); /* get manufacturer's list - optional */ int (*op_ex_cur)(); /* get current list - optional */ int (*op_repair)(); /* repair bad sector - optional */ int (*op_create)(); /* create original manufacturers */ /* defect list. - optional */ int (*op_wr_cur)(); /* write current list - optional */ }; /* * This structure describes a specific partition layout. It is malloc'd * when the data file is read and whenever the user creates their own * partition layout. The link is used to make a list of possible * partition layouts for each drive type. */ struct partition_info { char *pinfo_name; /* name of layout */ struct dk_map32 pinfo_map[NDKMAP]; /* layout info */ struct dk_vtoc vtoc; /* SVr4 vtoc additions */ struct partition_info *pinfo_next; /* ptr to next layout */ char *pinfo_filename; /* filename where defined */ int pinfo_lineno; /* line number in file */ struct dk_gpt *etoc; /* EFI partition info */ }; /* * This structure describes a change to be made to a particular * SCSI mode sense page, before issuing a mode select on that * page. This changes are specified in format.dat, and one * such structure is created for each specification, linked * into a list, in the order specified. */ struct chg_list { int pageno; /* mode sense page no. */ int byteno; /* byte within page */ int mode; /* see below */ int value; /* desired value */ struct chg_list *next; /* ptr to next */ }; /* * Change list modes */ #define CHG_MODE_UNDEFINED (-1) /* undefined value */ #define CHG_MODE_SET 0 /* set bits by or'ing */ #define CHG_MODE_CLR 1 /* clr bits by and'ing */ #define CHG_MODE_ABS 2 /* set absolute value */ /* * This is the structure that creates a dynamic list of controllers * that we know about. This structure will point to the items that * use to be statically created in the format program but will now allow * dynamic creation of the list so that we can do 3'rd party generic * disk/controller support. */ struct mctlr_list { struct mctlr_list *next; struct ctlr_type *ctlr_type; }; #ifdef __cplusplus } #endif #endif /* _HARDWARE_STRUCTS_H */ /* * 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 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * This file defines the known controller types. To add a new controller * type, simply add a new line to the array and define the necessary * ops vector in a 'driver' file. */ #include "global.h" #include extern struct ctlr_ops scsiops; extern struct ctlr_ops ataops; extern struct ctlr_ops pcmcia_ataops; extern struct ctlr_ops genericops; /* * This array defines the supported controller types */ struct ctlr_type ctlr_types[] = { { DKC_DIRECT, "ata", &ataops, CF_NOFORMAT | CF_WLIST }, { DKC_SCSI_CCS, "SCSI", &scsiops, CF_SCSI | CF_EMBEDDED }, { DKC_PCMCIA_ATA, "pcmcia", &pcmcia_ataops, CF_NOFORMAT | CF_NOWLIST }, { DKC_VBD, "virtual-dsk", &genericops, CF_NOWLIST }, { DKC_BLKDEV, "generic-block-device", &genericops, CF_NOWLIST } }; /* * This variable is used to count the entries in the array so its * size is not hard-wired anywhere. */ int nctypes = sizeof (ctlr_types) / sizeof (struct ctlr_type); /* * 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 1996,1998,2000,2002 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * This file defines the known controller types. To add a new controller * type, simply add a new line to the array and define the necessary * ops vector in a 'driver' file. */ #include "global.h" extern struct ctlr_ops scsiops; extern struct ctlr_ops ataops; extern struct ctlr_ops pcmcia_ataops; /* * This array defines the supported controller types */ struct ctlr_type ctlr_types[] = { { DKC_SCSI_CCS, "SCSI", &scsiops, CF_SCSI | CF_EMBEDDED }, { DKC_DIRECT, "ata", &ataops, CF_NOFORMAT | CF_NOWLIST }, { DKC_PCMCIA_ATA, "pcmcia", &pcmcia_ataops, CF_NOFORMAT | CF_NOWLIST }, }; /* * This variable is used to count the entries in the array so its * size is not hard-wired anywhere. */ int nctypes = sizeof (ctlr_types) / sizeof (struct ctlr_type); /* * 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 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * This file contains the declarations of menus for the program. To add * a new command/menu, simply add it to the appropriate table and define * the function that executes it. */ #include #include "global.h" #include "menu.h" #include "menu_partition.h" #include "menu_command.h" #include "menu_analyze.h" #include "menu_defect.h" #include "add_definition.h" #include "menu_scsi.h" #include "menu_developer.h" #include "menu_cache.h" #include "menu_fdisk.h" /* * This declaration is for the command menu. It is the menu first * encountered upon entering the program. */ struct menu_item menu_command[] = { { "disk - select a disk", c_disk, true }, { "type - select (define) a disk type", c_type, true }, { "partition - select (define) a partition table", c_partition, true }, { "current - describe the current disk", c_current, true }, { "format - format and analyze the disk", c_format, true }, { "fdisk - run the fdisk program", c_fdisk, support_fdisk_on_sparc }, { "repair - repair a defective sector", c_repair, true }, { "show - translate a disk address", c_show, not_scsi }, { "label - write label to the disk", c_label, true }, { "analyze - surface analysis", c_analyze, true }, { "defect - defect list management", c_defect, true }, { "backup - search for backup labels", c_backup, true }, { "verify - read and display labels", c_verify, true }, { "save - save new disk/partition definitions", add_definition, not_efi }, { "inquiry - show vendor, product and revision", c_inquiry, scsi }, { "scsi - independent SCSI mode selects", c_scsi, scsi_expert }, { "cache - enable, disable or query SCSI disk cache", c_cache, scsi_expert }, { "volname - set 8-character volume name", c_volname, true }, { "developer - dump developer things", c_developer, developer }, { "! - execute , then return", execute_shell, true}, { NULL } }; /* * This declaration is for the partition menu. It is used to create * and maintain partition tables. */ struct menu_item menu_partition[] = { { "0 - change `0' partition", p_apart, true }, { "1 - change `1' partition", p_bpart, true }, { "2 - change `2' partition", p_cpart, true }, { "3 - change `3' partition", p_dpart, true }, { "4 - change `4' partition", p_epart, true }, { "5 - change `5' partition", p_fpart, true }, { "6 - change `6' partition", p_gpart, true }, { "7 - change `7' partition", p_hpart, disp_expert_change_expert_efi }, { "8 - change '8' partition", p_ipart, disp_all_change_expert_efi }, #if defined(i386) { "9 - change `9' partition", p_jpart, expert }, #endif { "expand - expand label to use whole disk", p_expand, disp_expand_efi }, { "select - select a predefined table", p_select, true }, { "modify - modify a predefined partition table", p_modify, true }, { "name - name the current table", p_name, true }, { "print - display the current table", p_print, true }, { "label - write partition map and label to the disk", c_label, true }, { "! - execute , then return", execute_shell, true}, { NULL } }; /* * This declaration is for the analysis menu. It is used to set up * and execute surface analysis of a disk. */ struct menu_item menu_analyze[] = { { "read - read only test (doesn't harm SunOS)", a_read, true }, { "refresh - read then write (doesn't harm data)", a_refresh, true }, { "test - pattern testing (doesn't harm data)", a_test, true }, { "write - write then read (corrupts data)", a_write, true }, { "compare - write, read, compare (corrupts data)", a_compare, true }, { "purge - write, read, write (corrupts data)", a_purge, true }, { "verify - write entire disk, then verify (corrupts data)", a_verify, true }, { "print - display data buffer", a_print, true }, { "setup - set analysis parameters", a_setup, true }, { "config - show analysis parameters", a_config, true }, { "! - execute , then return", execute_shell, true}, { NULL } }; /* * This declaration is for the defect menu. It is used to manipulate * the defect list for a disk. */ struct menu_item menu_defect[] = { { "restore - set working list = current list", d_restore, not_embedded_scsi }, { "original - extract manufacturer's list from disk", d_original, not_embedded_scsi }, { "extract - extract working list from disk", d_extract, not_embedded_scsi }, { "primary - extract manufacturer's defect list", d_primary, embedded_scsi }, { "grown - extract the grown defects list", d_grown, embedded_scsi }, { "both - extract both primary and grown defects lists", d_both, embedded_scsi }, { "add - add defects to working list", d_add, not_embedded_scsi }, { "delete - delete a defect from working list", d_delete, not_embedded_scsi }, { "print - display defect list", d_print, embedded_scsi }, { "dump - dump defect list to file", d_dump, embedded_scsi }, { "print - display working list", d_print, not_embedded_scsi }, { "dump - dump working list to file", d_dump, not_embedded_scsi }, { "load - load working list from file", d_load, not_embedded_scsi }, { "commit - set current list = working list", d_commit, not_embedded_scsi }, { "create - recreates maufacturer's defect list on disk", d_create, not_embedded_scsi }, { "! - execute , then return", execute_shell, true}, { NULL } }; /* * This declaration is for the developer menu. */ struct menu_item menu_developer[] = { { "dump_disk - dump disk entries", dv_disk, true }, { "dump_cont - dump controller entries", dv_cont, true }, { "dump_c_chain - dump controller chain entries", dv_cont_chain, true }, { "dev_params - dump device parameters", dv_params, true }, { "! - execute , then return", execute_shell, true}, { NULL } }; /* * This declaration is for the cache menu. */ struct menu_item menu_cache[] = { { "write_cache - display or modify write cache settings", ca_write_cache, scsi_expert }, { "read_cache - display or modify read cache settings", ca_read_cache, scsi_expert }, { "! - execute , then return", execute_shell, true}, { NULL } }; /* * This declaration is for the write_cache menu. */ struct menu_item menu_write_cache[] = { { "display - display current setting of write cache", ca_write_display, scsi_expert }, { "enable - enable write cache", ca_write_enable, scsi_expert }, { "disable - disable write cache", ca_write_disable, scsi_expert }, { "! - execute , then return", execute_shell, true}, { NULL } }; /* * This declaration is for the read_cache menu. */ struct menu_item menu_read_cache[] = { { "display - display current setting of read cache", ca_read_display, scsi_expert }, { "enable - enable read cache", ca_read_enable, scsi_expert }, { "disable - disable read cache", ca_read_disable, scsi_expert }, { "! - execute , then return", execute_shell, true}, { NULL } }; /* * 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. * Copyright 2015 Nexenta Systems, Inc. All rights reserved. */ /* * This file contains I/O related functions. */ #include "global.h" #include #include #include #include #include #include #include #include #include #include #include "startup.h" #include "misc.h" #include "menu_partition.h" #include "param.h" #include "menu.h" extern int data_lineno; extern char *space2str(uint_t); /* * This variable is used to determine whether a token is present in the pipe * already. */ static char token_present = 0; /* * This variable always gives us access to the most recent token type */ int last_token_type = 0; static int sup_get_token(char *); static void pushchar(int c); static int checkeof(void); static void flushline(void); static int strcnt(char *s1, char *s2); static int getbn(char *str, diskaddr_t *iptr); static void print_input_choices(int type, u_ioparam_t *param); static int slist_widest_str(slist_t *slist); static void ljust_print(char *str, int width); static int sup_inputchar(void); static void sup_pushchar(int c); static int geti64(char *str, uint64_t *iptr, uint64_t *wild); /* * This routine pushes the given character back onto the input stream. */ static void pushchar(int c) { (void) ungetc(c, stdin); } /* * This routine checks the input stream for an eof condition. */ static int checkeof(void) { return (feof(stdin)); } /* * This routine gets the next token off the input stream. A token is * basically any consecutive non-white characters. */ char * gettoken(char *inbuf) { char *ptr = inbuf; int c, quoted = 0; retoke: /* * Remove any leading white-space. */ while ((isspace(c = getchar())) && (c != '\n')) ; /* * If we are at the beginning of a line and hit the comment character, * flush the line and start again. */ if (!token_present && c == COMMENT_CHAR) { token_present = 1; flushline(); goto retoke; } /* * Loop on each character until we hit unquoted white-space. */ while (!isspace(c) || quoted && (c != '\n')) { /* * If we hit eof, get out. */ if (checkeof()) return (NULL); /* * If we hit a double quote, change the state of quotedness. */ if (c == '"') quoted = !quoted; /* * If there's room in the buffer, add the character to the end. */ else if (ptr - inbuf < TOKEN_SIZE) *ptr++ = (char)c; /* * Get the next character. */ c = getchar(); } /* * Null terminate the token. */ *ptr = '\0'; /* * Peel off white-space still in the pipe. */ while (isspace(c) && (c != '\n')) c = getchar(); /* * If we hit another token, push it back and set state. */ if (c != '\n') { pushchar(c); token_present = 1; } else token_present = 0; /* * Return the token. */ return (inbuf); } /* * This routine removes the leading and trailing spaces from a token. */ void clean_token(char *cleantoken, char *token) { char *ptr; /* * Strip off leading white-space. */ for (ptr = token; isspace(*ptr); ptr++) ; /* * Copy it into the clean buffer. */ (void) strcpy(cleantoken, ptr); /* * Strip off trailing white-space. */ for (ptr = cleantoken + strlen(cleantoken) - 1; isspace(*ptr) && (ptr >= cleantoken); ptr--) { *ptr = '\0'; } } /* * This routine checks if a token is already present on the input line */ int istokenpresent(void) { return (token_present); } /* * This routine flushes the rest of an input line if there is known * to be data in it. The flush has to be qualified because the newline * may have already been swallowed by the last gettoken. */ static void flushline(void) { if (token_present) { /* * Flush the pipe to eol or eof. */ while ((getchar() != '\n') && !checkeof()) ; /* * Mark the pipe empty. */ token_present = 0; } } /* * This routine returns the number of characters that are identical * between s1 and s2, stopping as soon as a mismatch is found. */ static int strcnt(char *s1, char *s2) { int i = 0; while ((*s1 != '\0') && (*s1++ == *s2++)) i++; return (i); } /* * This routine converts the given token into an integer. The token * must convert cleanly into an integer with no unknown characters. * If the token is the wildcard string, and the wildcard parameter * is present, the wildcard value will be returned. */ int geti(char *str, int *iptr, int *wild) { char *str2; /* * If there's a wildcard value and the string is wild, return the * wildcard value. */ if (wild != NULL && strcmp(str, WILD_STRING) == 0) *iptr = *wild; else { /* * Conver the string to an integer. */ *iptr = (int)strtol(str, &str2, 0); /* * If any characters didn't convert, it's an error. */ if (*str2 != '\0') { err_print("`%s' is not an integer.\n", str); return (-1); } } return (0); } /* * This routine converts the given token into a long long. The token * must convert cleanly into a 64-bit integer with no unknown characters. * If the token is the wildcard string, and the wildcard parameter * is present, the wildcard value will be returned. */ static int geti64(char *str, uint64_t *iptr, uint64_t *wild) { char *str2; /* * If there's a wildcard value and the string is wild, return the * wildcard value. */ if ((wild != NULL) && (strcmp(str, WILD_STRING)) == 0) { *iptr = *wild; } else { /* * Conver the string to an integer. */ *iptr = (uint64_t)strtoll(str, &str2, 0); /* * If any characters didn't convert, it's an error. */ if (*str2 != '\0') { err_print("`%s' is not an integer.\n", str); return (-1); } } return (0); } /* * This routine converts the given string into a block number on the * current disk. The format of a block number is either a self-based * number, or a series of self-based numbers separated by slashes. * Any number preceeding the first slash is considered a cylinder value. * Any number succeeding the first slash but preceeding the second is * considered a head value. Any number succeeding the second slash is * considered a sector value. Any of these numbers can be wildcarded * to the highest possible legal value. */ static int getbn(char *str, diskaddr_t *iptr) { char *cptr, *hptr, *sptr; int cyl, head, sect; int wild; diskaddr_t wild64; TOKEN buf; /* * Set cylinder pointer to beginning of string. */ cptr = str; /* * Look for the first slash. */ while ((*str != '\0') && (*str != '/')) str++; /* * If there wasn't one, convert string to an integer and return it. */ if (*str == '\0') { wild64 = physsects() - 1; if (geti64(cptr, iptr, &wild64)) return (-1); return (0); } /* * Null out the slash and set head pointer just beyond it. */ *str++ = '\0'; hptr = str; /* * Look for the second slash. */ while ((*str != '\0') && (*str != '/')) str++; /* * If there wasn't one, sector pointer points to a . */ if (*str == '\0') sptr = str; /* * If there was, null it out and set sector point just beyond it. */ else { *str++ = '\0'; sptr = str; } /* * Convert the cylinder part to an integer and store it. */ clean_token(buf, cptr); wild = ncyl + acyl - 1; if (geti(buf, &cyl, &wild)) return (-1); if ((cyl < 0) || (cyl >= (ncyl + acyl))) { err_print("`%d' is out of range [0-%u].\n", cyl, ncyl + acyl - 1); return (-1); } /* * Convert the head part to an integer and store it. */ clean_token(buf, hptr); wild = nhead - 1; if (geti(buf, &head, &wild)) return (-1); if ((head < 0) || (head >= nhead)) { err_print("`%d' is out of range [0-%u].\n", head, nhead - 1); return (-1); } /* * Convert the sector part to an integer and store it. */ clean_token(buf, sptr); wild = sectors(head) - 1; if (geti(buf, §, &wild)) return (-1); if ((sect < 0) || (sect >= sectors(head))) { err_print("`%d' is out of range [0-%u].\n", sect, sectors(head) - 1); return (-1); } /* * Combine the pieces into a block number and return it. */ *iptr = chs2bn(cyl, head, sect); return (0); } /* * This routine is the basis for all input into the program. It * understands the semantics of a set of input types, and provides * consistent error messages for all input. It allows for default * values and prompt strings. */ uint64_t input(int type, char *promptstr, int delim, u_ioparam_t *param, int *deflt, int cmdflag) { int interactive, help, i, length, index, tied; blkaddr_t bn; diskaddr_t bn64; char **str, **strings; TOKEN token, cleantoken; TOKEN token2, cleantoken2; char *arg; struct bounds *bounds; char *s; int value; int cyls, cylno; uint64_t blokno; float nmegs; float ngigs; char shell_argv[MAXPATHLEN]; part_deflt_t *part_deflt; efi_deflt_t *efi_deflt; /* * Optional integer input has been added as a hack. * Function result is 1 if user typed anything. * Whatever they typed is returned in *deflt. * This permits us to distinguish between "no value", * and actually entering in some value, for instance. */ if (type == FIO_OPINT) { assert(deflt != NULL); } reprompt: help = interactive = 0; /* * If we are inputting a command, flush any current input in the pipe. */ if (cmdflag == CMD_INPUT) flushline(); /* * Note whether the token is already present. */ if (!token_present) interactive = 1; /* * Print the prompt. */ fmt_print(promptstr); /* * If there is a default value, print it in a format appropriate * for the input type. */ if (deflt != NULL) { switch (type) { case FIO_BN: #if !defined(lint) /* caller has aligned the pointer specifying FIO_BN */ fmt_print("[%llu, ", *(diskaddr_t *)deflt); pr_dblock(fmt_print, *(diskaddr_t *)deflt); fmt_print("]"); #endif break; case FIO_INT: fmt_print("[%d]", *deflt); break; case FIO_INT64: #if defined(lint) /* caller is longlong aligned specifying FIO_INT64 */ efi_deflt = NULL; #else efi_deflt = (efi_deflt_t *)deflt; #endif fmt_print("[%llu]", efi_deflt->start_sector); break; case FIO_CSTR: case FIO_MSTR: strings = (char **)param->io_charlist; for (i = 0, str = strings; i < *deflt; i++, str++) ; fmt_print("[%s]", *str); break; case FIO_OSTR: fmt_print("[\"%s\"]", (char *)deflt); break; case FIO_SLIST: /* * Search for a string matching the default * value. If found, use it. Otherwise * assume the default value is actually * an illegal choice, and default to * the first item in the list. */ s = find_string(param->io_slist, *deflt); if (s == NULL) { s = (param->io_slist)->str; } fmt_print("[%s]", s); break; case FIO_CYL: /* * Old-style partition size input, used to * modify complete partition tables */ blokno = *(blkaddr32_t *)deflt; fmt_print("[%llub, %uc, %1.2fmb, %1.2fgb]", blokno, bn2c(blokno), bn2mb(blokno), bn2gb(blokno)); break; case FIO_ECYL: /* * set up pointer to partition defaults * structure */ part_deflt = (part_deflt_t *)deflt; /* * Build print format specifier. We use the * starting cylinder number which was entered * before this call to input(), in case the * user has changed it from the value in the * cur_parts->pinfo_map[].dkl_cylno * field for the current parition */ /* * Determine the proper default end cylinder: * Start Cyl Default Size End Cylinder * 0 0 0 * >0 0 Start Cyl * 0 >0 Default Size * (Cyls) - 1 * >0 >0 (Start + * Default Size * (Cyls)) -1 */ if (part_deflt->deflt_size == 0) { cylno = part_deflt->start_cyl; } else if (part_deflt->start_cyl == 0) { cylno = bn2c(part_deflt->deflt_size) - 1; } else { cylno = (bn2c(part_deflt->deflt_size) + part_deflt->start_cyl) - 1; } fmt_print("[%ub, %uc, %de, %1.2fmb, %1.2fgb]", part_deflt->deflt_size, bn2c(part_deflt->deflt_size), cylno, bn2mb(part_deflt->deflt_size), bn2gb(part_deflt->deflt_size)); break; case FIO_EFI: #if defined(lint) /* caller is longlong aligned when specifying FIO_EFI */ efi_deflt = NULL; #else efi_deflt = (efi_deflt_t *)deflt; #endif fmt_print("[%llub, %llue, %llumb, %llugb, %llutb]", efi_deflt->end_sector, efi_deflt->start_sector + efi_deflt->end_sector - 1, (efi_deflt->end_sector * cur_blksz) / (1024 * 1024), (efi_deflt->end_sector * cur_blksz) / (1024 * 1024 * 1024), (efi_deflt->end_sector * cur_blksz) / ((uint64_t)1024 * 1024 * 1024 * 1024)); break; case FIO_OPINT: /* no default value for optional input type */ fmt_print("[default]"); break; default: err_print("Error: unknown input type.\n"); fullabort(); } } /* * Print the delimiter character. */ fmt_print("%c ", delim); /* * Get the token. If we hit eof, exit the program gracefully. */ if (gettoken(token) == NULL) fullabort(); /* * check if the user has issued (!) , escape to shell */ if ((cmdflag == CMD_INPUT) && (token[0] == '!')) { /* get the list of arguments to shell command */ (void) memset(shell_argv, 0, sizeof (shell_argv)); /* initialize to the first token... */ arg = &token[1]; /* * ... and then collect all tokens until the end of * the line as arguments */ do { /* skip empty tokens. */ if (*arg == '\0') continue; /* * If either of the following two strlcat() * operations overflows, report an error and * exit gracefully. */ if ((strlcat(shell_argv, arg, sizeof (shell_argv)) >= sizeof (shell_argv)) || (strlcat(shell_argv, " ", sizeof (shell_argv)) >= sizeof (shell_argv))) { err_print("Error: Command line too long.\n"); fullabort(); } } while (token_present && (arg = gettoken(token)) != NULL); /* execute the shell command */ (void) execute_shell(shell_argv, sizeof (shell_argv)); redisplay_menu_list((char **)param->io_charlist); if (interactive) { goto reprompt; } } /* * Certain commands accept up to two tokens * Unfortunately, this is kind of a hack. */ token2[0] = 0; cleantoken2[0] = 0; if (type == FIO_CYL || type == FIO_ECYL) { if (token_present) { if (gettoken(token2) == NULL) fullabort(); clean_token(cleantoken2, token2); } } /* * Echo the token back to the user if it was in the pipe or we * are running out of a command file. */ if (!interactive || option_f) { if (token2[0] == 0) { fmt_print("%s\n", token); } else { fmt_print("%s %s\n", token, token2); } } /* * If we are logging, echo the token to the log file. The else * is necessary here because the above printf will also put the * token in the log file. */ else if (log_file) { log_print("%s %s\n", token, token2); } /* * If the token was not in the pipe and it wasn't a command, flush * the rest of the line to keep things in sync. */ if (interactive && cmdflag != CMD_INPUT) flushline(); /* * Scrub off the white-space. */ clean_token(cleantoken, token); /* * If the input was a blank line and we weren't prompting * specifically for a blank line... */ if ((strcmp(cleantoken, "") == 0) && (type != FIO_BLNK)) { /* * If there's a default, return it. */ if (deflt != NULL) { if (type == FIO_OSTR) { /* * Duplicate and return the default string */ return ((int)alloc_string((char *)deflt)); } else if (type == FIO_SLIST) { /* * If we can find a match for the default * value in the list, return the default * value. If there's no match for the * default value, it's an illegal * choice. Return the first value in * the list. */ s = find_string(param->io_slist, *deflt); if ((cur_label == L_TYPE_EFI) && (s == NULL)) { return (*deflt); } if (s == NULL) { return ((param->io_slist)->value); } else { return (*deflt); } } else if (type == FIO_OPINT) { /* * The user didn't enter anything */ return (0); } else if (type == FIO_ECYL) { return (part_deflt->deflt_size); } else if (type == FIO_INT64) { return (efi_deflt->start_sector); } else if (type == FIO_EFI) { return (efi_deflt->end_sector); } else { return (*deflt); } } /* * If the blank was not in the pipe, just reprompt. */ if (interactive) { goto reprompt; } /* * If the blank was in the pipe, it's an error. */ err_print("No default for this entry.\n"); cmdabort(SIGINT); } /* * If token is a '?' or a 'h', it is a request for help. */ if ((strcmp(cleantoken, "?") == 0) || (strcmp(cleantoken, "h") == 0) || (strcmp(cleantoken, "help") == 0)) { help = 1; } /* * Switch on the type of input expected. */ switch (type) { /* * Expecting a disk block number. */ case FIO_BN: /* * Parameter is the bounds of legal block numbers. */ bounds = (struct bounds *)¶m->io_bounds; /* * Print help message if required. */ if (help) { fmt_print("Expecting a block number from %llu (", bounds->lower); pr_dblock(fmt_print, bounds->lower); fmt_print(") to %llu (", bounds->upper); pr_dblock(fmt_print, bounds->upper); fmt_print(")\n"); break; } /* * Convert token to a disk block number. */ if (cur_label == L_TYPE_EFI) { if (geti64(cleantoken, (uint64_t *)&bn64, NULL)) break; } else { if (getbn(cleantoken, &bn64)) break; } /* * Check to be sure it is within the legal bounds. */ if ((bn64 < bounds->lower) || (bn64 > bounds->upper)) { err_print("`"); pr_dblock(err_print, bn64); err_print("' is out of range [%llu-%llu].\n", bounds->lower, bounds->upper); break; } /* * It's ok, return it. */ return (bn64); /* * Expecting an integer. */ case FIO_INT: /* * Parameter is the bounds of legal integers. */ bounds = (struct bounds *)¶m->io_bounds; /* * Print help message if required. */ if (help) { fmt_print("Expecting an integer from %llu", bounds->lower); fmt_print(" to %llu\n", bounds->upper); break; } /* * Convert the token into an integer. */ if (geti(cleantoken, (int *)&bn, NULL)) break; /* * Check to be sure it is within the legal bounds. */ if ((bn < bounds->lower) || (bn > bounds->upper)) { err_print("`%lu' is out of range [%llu-%llu].\n", bn, bounds->lower, bounds->upper); break; } /* * If it's ok, return it. */ return (bn); case FIO_INT64: /* * Parameter is the bounds of legal integers. */ bounds = (struct bounds *)¶m->io_bounds; /* * Print help message if required. */ if (help) { fmt_print("Expecting an integer from %llu", bounds->lower); fmt_print(" to %llu\n", bounds->upper); break; } /* * Convert the token into an integer. */ if (geti64(cleantoken, (uint64_t *)&bn64, NULL)) { break; } /* * Check to be sure it is within the legal bounds. */ if ((bn64 < bounds->lower) || (bn64 > bounds->upper)) { err_print("`%llu' is out of range [%llu-%llu].\n", bn64, bounds->lower, bounds->upper); break; } /* * If it's ok, return it. */ return (bn64); /* * Expecting an integer, or no input. */ case FIO_OPINT: /* * Parameter is the bounds of legal integers. */ bounds = (struct bounds *)¶m->io_bounds; /* * Print help message if required. */ if (help) { fmt_print("Expecting an integer from %llu", bounds->lower); fmt_print(" to %llu, or no input\n", bounds->upper); break; } /* * Convert the token into an integer. */ if (geti(cleantoken, (int *)&bn, NULL)) break; /* * Check to be sure it is within the legal bounds. */ if ((bn < bounds->lower) || (bn > bounds->upper)) { err_print("`%lu' is out of range [%llu-%llu].\n", bn, bounds->lower, bounds->upper); break; } /* * For optional case, return 1 indicating that * the user actually did enter something. */ if (!deflt) *deflt = bn; return (1); /* * Expecting a closed string. This means that the input * string must exactly match one of the strings passed in * as the parameter. */ case FIO_CSTR: /* * The parameter is a null terminated array of character * pointers, each one pointing to a legal input string. */ strings = (char **)param->io_charlist; /* * Walk through the legal strings, seeing if any of them * match the token. If a match is made, return the index * of the string that was matched. */ for (str = strings; *str != NULL; str++) if (strcmp(cleantoken, *str) == 0) return (str - strings); /* * Print help message if required. */ if (help) { print_input_choices(type, param); } else { err_print("`%s' is not expected.\n", cleantoken); } break; /* * Expecting a matched string. This means that the input * string must either match one of the strings passed in, * or be a unique abbreviation of one of them. */ case FIO_MSTR: /* * The parameter is a null terminated array of character * pointers, each one pointing to a legal input string. */ strings = (char **)param->io_charlist; length = index = tied = 0; /* * Loop through the legal input strings. */ for (str = strings; *str != NULL; str++) { /* * See how many characters of the token match * this legal string. */ i = strcnt(cleantoken, *str); /* * If it's not the whole token, then it's not a match. */ if ((uint_t)i < strlen(cleantoken)) continue; /* * If it ties with another input, remember that. */ if (i == length) tied = 1; /* * If it matches the most so far, record that. */ if (i > length) { index = str - strings; tied = 0; length = i; } } /* * Print help message if required. */ if (length == 0) { if (help) { print_input_choices(type, param); } else { err_print("`%s' is not expected.\n", cleantoken); } break; } /* * If the abbreviation was non-unique, it's an error. */ if (tied) { err_print("`%s' is ambiguous.\n", cleantoken); break; } /* * We matched one. Return the index of the string we matched. */ return (index); /* * Expecting an open string. This means that any string is legal. */ case FIO_OSTR: /* * Print a help message if required. */ if (help) { fmt_print("Expecting a string\n"); break; } /* * alloc a copy of the string and return it */ return ((int)alloc_string(token)); /* * Expecting a blank line. */ case FIO_BLNK: /* * We are always in non-echo mode when we are inputting * this type. We echo the newline as a carriage return * only so the prompt string will be covered over. */ nolog_print("\015"); /* * If we are logging, send a newline to the log file. */ if (log_file) log_print("\n"); /* * There is no value returned for this type. */ return (0); /* * Expecting one of the entries in a string list. * Accept unique abbreviations. * Return the value associated with the matched string. */ case FIO_SLIST: i = find_value((slist_t *)param->io_slist, cleantoken, &value); if (i == 1) { return (value); } else { /* * Print help message if required. */ if (help) { print_input_choices(type, param); } else { if (i == 0) err_print("`%s' not expected.\n", cleantoken); else err_print("`%s' is ambiguous.\n", cleantoken); } } break; /* * Cylinder size input when modifying a complete partition map */ case FIO_CYL: /* * Parameter is the bounds of legal block numbers. */ bounds = (struct bounds *)¶m->io_bounds; assert(bounds->lower == 0); /* * Print help message if required. */ if (help) { fmt_print("Expecting up to %llu blocks,", bounds->upper); fmt_print(" %u cylinders, ", bn2c(bounds->upper)); fmt_print(" %1.2f megabytes, ", bn2mb(bounds->upper)); fmt_print("or %1.2f gigabytes\n", bn2gb(bounds->upper)); break; } /* * Parse the first token: try to find 'b', 'c' or 'm' */ s = cleantoken; while (*s && (isdigit(*s) || (*s == '.') || (*s == '$'))) { s++; } /* * If we found a conversion specifier, second token is unused * Otherwise, the second token should supply it. */ if (*s != 0) { value = *s; *s = 0; } else { value = cleantoken2[0]; } /* * If the token is the wild card, simply supply the max * This order allows the user to specify the maximum in * either blocks/cyls/megabytes - a convenient fiction. */ if (strcmp(cleantoken, WILD_STRING) == 0) { return (bounds->upper); } /* * Allow the user to specify zero with no units, * by just defaulting to cylinders. */ if (strcmp(cleantoken, "0") == 0) { value = 'c'; } /* * If there's a decimal point, but no unit specification, * let's assume megabytes. */ if ((value == 0) && (strchr(cleantoken, '.') != NULL)) { value = 'm'; } /* * Handle each unit type we support */ switch (value) { case 'b': /* * Convert token to a disk block number. */ if (geti64(cleantoken, &bn64, &bounds->upper)) break; /* * Check to be sure it is within the legal bounds. */ if ((bn64 < bounds->lower) || (bn64 > bounds->upper)) { err_print( "`%llub' is out of the range %llu " "to %llu\n", bn64, bounds->lower, bounds->upper); break; } /* * Verify the block lies on a cylinder boundary */ if ((bn64 % spc()) != 0) { err_print( "partition size must be a multiple of " "%u blocks to lie on a cylinder boundary\n", spc()); err_print( "%llu blocks is approximately %u cylinders," " %1.2f megabytes or %1.2f gigabytes\n", bn64, bn2c(bn64), bn2mb(bn64), bn2gb(bn64)); break; } return (bn64); case 'c': /* * Convert token from a number of cylinders to * a number of blocks. */ i = bn2c(bounds->upper); if (geti(cleantoken, &cyls, &i)) break; /* * Check the bounds - cyls is number of cylinders */ if (cyls > (bounds->upper / spc())) { err_print("`%dc' is out of range [0-%llu]\n", cyls, bounds->upper / spc()); break; } /* * Convert cylinders to blocks and return */ return (cyls * spc()); case 'm': /* * Convert token from megabytes to a block number. */ if (sscanf(cleantoken, "%f2", &nmegs) != 1) { err_print("`%s' is not recognized\n", cleantoken); break; } /* * Check the bounds */ if (nmegs > bn2mb(bounds->upper)) { err_print("`%1.2fmb' is out of range " "[0-%1.2f]\n", nmegs, bn2mb(bounds->upper)); break; } /* * Convert to blocks */ bn64 = mb2bn(nmegs); /* * Round value up to nearest cylinder */ i = spc(); bn64 = ((bn64 + (i-1)) / i) * i; return (bn64); case 'g': /* * Convert token from gigabytes to a block number. */ if (sscanf(cleantoken, "%f2", &ngigs) != 1) { err_print("`%s' is not recognized\n", cleantoken); break; } /* * Check the bounds */ if (ngigs > bn2gb(bounds->upper)) { err_print("`%1.2fgb' is out of range " "[0-%1.2f]\n", ngigs, bn2gb(bounds->upper)); break; } /* * Convert to blocks */ bn64 = gb2bn(ngigs); /* * Round value up to nearest cylinder */ i = spc(); bn64 = ((bn64 + (i-1)) / i) * i; return (bn64); default: err_print( "Please specify units in either b(blocks), c(cylinders), m(megabytes) \ or g(gigabytes)\n"); break; } break; case FIO_ECYL: /* * Parameter is the bounds of legal block numbers. */ bounds = (struct bounds *)¶m->io_bounds; assert(bounds->lower == 0); /* * Print help message if required. */ if (help) { fmt_print("Expecting up to %llu blocks,", bounds->upper); fmt_print(" %u cylinders, ", bn2c(bounds->upper)); fmt_print(" %u end cylinder, ", (uint_t)(bounds->upper / spc())); fmt_print(" %1.2f megabytes, ", bn2mb(bounds->upper)); fmt_print("or %1.2f gigabytes\n", bn2gb(bounds->upper)); break; } /* * Parse the first token: try to find 'b', 'c', 'e' * or 'm' */ s = cleantoken; while (*s && (isdigit(*s) || (*s == '.') || (*s == '$'))) { s++; } /* * If we found a conversion specifier, second token is * unused Otherwise, the second token should supply it. */ if (*s != 0) { value = *s; *s = 0; } else { value = cleantoken2[0]; } /* * If the token is the wild card, simply supply the max * This order allows the user to specify the maximum in * either blocks/cyls/megabytes - a convenient fiction. */ if (strcmp(cleantoken, WILD_STRING) == 0) { return (bounds->upper); } /* * Allow the user to specify zero with no units, * by just defaulting to cylinders. */ if (value != 'e' && strcmp(cleantoken, "0") == 0) { value = 'c'; } /* * If there's a decimal point, but no unit * specification, let's assume megabytes. */ if ((value == 0) && (strchr(cleantoken, '.') != NULL)) { value = 'm'; } /* * Handle each unit type we support */ switch (value) { case 'b': /* * Convert token to a disk block number. */ if (geti64(cleantoken, &bn64, &bounds->upper)) break; /* * Check to be sure it is within the * legal bounds. */ if ((bn64 < bounds->lower) || (bn64 > bounds->upper)) { err_print( "`%llub' is out of the range %llu to %llu\n", bn64, bounds->lower, bounds->upper); break; } /* * Verify the block lies on a cylinder * boundary */ if ((bn64 % spc()) != 0) { err_print( "partition size must be a multiple of %u " "blocks to lie on a cylinder boundary\n", spc()); err_print( "%llu blocks is approximately %u cylinders," " %1.2f megabytes or %1.2f gigabytes\n", bn64, bn2c(bn64), bn2mb(bn64), bn2gb(bn64)); break; } return (bn64); case 'e': /* * Token is ending cylinder */ /* convert token to integer */ if (geti(cleantoken, &cylno, NULL)) { break; } /* * check that input cylno isn't before the current * starting cylinder number. Note that we are NOT * using the starting cylinder from * cur_parts->pinfo_map[].dkl_cylno! */ if (cylno < part_deflt->start_cyl) { err_print( "End cylinder must fall on or after start cylinder %u\n", part_deflt->start_cyl); break; } /* * calculate cylinder number of upper boundary, and * verify that our input is within range */ i = (bn2c(bounds->upper) + part_deflt->start_cyl - 1); if (cylno > i) { err_print( "End cylinder %d is beyond max cylinder %d\n", cylno, i); break; } /* * calculate number of cylinders based on input */ cyls = ((cylno - part_deflt->start_cyl) + 1); return (cyls * spc()); case 'c': /* * Convert token from a number of * cylinders to a number of blocks. */ i = bn2c(bounds->upper); if (geti(cleantoken, &cyls, &i)) break; /* * Check the bounds - cyls is number of * cylinders */ if (cyls > (bounds->upper / spc())) { err_print("`%dc' is out of range [0-%llu]\n", cyls, bounds->upper / spc()); break; } /* * Convert cylinders to blocks and * return */ return (cyls * spc()); case 'm': /* * Convert token from megabytes to a * block number. */ if (sscanf(cleantoken, "%f2", &nmegs) != 1) { err_print("`%s' is not recognized\n", cleantoken); break; } /* * Check the bounds */ if (nmegs > bn2mb(bounds->upper)) { err_print("`%1.2fmb' is out of range " "[0-%1.2f]\n", nmegs, bn2mb(bounds->upper)); break; } /* * Convert to blocks */ bn64 = mb2bn(nmegs); /* * Round value up to nearest cylinder */ i = spc(); bn64 = ((bn64 + (i-1)) / i) * i; return (bn64); case 'g': /* * Convert token from gigabytes to a * block number. */ if (sscanf(cleantoken, "%f2", &ngigs) != 1) { err_print("`%s' is not recognized\n", cleantoken); break; } /* * Check the bounds */ if (ngigs > bn2gb(bounds->upper)) { err_print("`%1.2fgb' is out of range " "[0-%1.2f]\n", ngigs, bn2gb(bounds->upper)); break; } /* * Convert to blocks */ bn64 = gb2bn(ngigs); /* * Round value up to nearest cylinder */ i = spc(); bn64 = ((bn64 + (i-1)) / i) * i; return (bn64); default: err_print( "Please specify units in either b(blocks), c(cylinders), e(end cylinder),\n"); err_print("m(megabytes) or g(gigabytes)\n"); break; } break; case FIO_EFI: /* * Parameter is the bounds of legal block numbers. */ bounds = (struct bounds *)¶m->io_bounds; /* * Print help message if required. */ if (help) { fmt_print("Expecting up to %llu sectors,", cur_parts->etoc->efi_last_u_lba); fmt_print("or %llu megabytes,", (cur_parts->etoc->efi_last_u_lba * cur_blksz) / (1024 * 1024)); fmt_print("or %llu gigabytes\n", (cur_parts->etoc->efi_last_u_lba * cur_blksz) / (1024 * 1024 * 1024)); fmt_print("or %llu terabytes\n", (cur_parts->etoc->efi_last_u_lba * cur_blksz) / ((uint64_t)1024 * 1024 * 1024 * 1024)); break; } /* * Parse the first token: try to find 'b', 'c', 'e' * or 'm' */ s = cleantoken; while (*s && (isdigit(*s) || (*s == '.') || (*s == '$'))) { s++; } /* * If we found a conversion specifier, second token is * unused Otherwise, the second token should supply it. */ if (*s != 0) { value = *s; *s = 0; } else { value = cleantoken2[0]; } /* * If the token is the wild card, simply supply the max * This order allows the user to specify the maximum in * either blocks/cyls/megabytes - a convenient fiction. */ if (strcmp(cleantoken, WILD_STRING) == 0) { uint64_t reserved; reserved = efi_reserved_sectors(cur_parts->etoc); return (bounds->upper - reserved - efi_deflt->start_sector + 1); } /* * Allow the user to specify zero with no units, * by just defaulting to sectors. */ if (value != 'e' && strcmp(cleantoken, "0") == 0) { value = 'm'; } /* * If there's a decimal point, but no unit * specification, let's assume megabytes. */ if ((value == 0) && (strchr(cleantoken, '.') != NULL)) { value = 'm'; } /* * Handle each unit type we support */ switch (value) { case 'b': /* * Token is number of blocks */ if (geti64(cleantoken, &blokno, NULL)) { break; } if (blokno > bounds->upper) { err_print("Number of blocks must be less that " "the total available blocks.\n"); break; } return (blokno); case 'e': /* * Token is ending block number */ /* convert token to integer */ if (geti64(cleantoken, &blokno, NULL)) { break; } /* * Some sanity check */ if (blokno < efi_deflt->start_sector) { err_print("End Sector must fall on or after " "start sector %llu\n", efi_deflt->start_sector); break; } /* * verify that our input is within range */ if (blokno > cur_parts->etoc->efi_last_u_lba) { err_print("End Sector %llu is beyond max " "Sector %llu\n", blokno, cur_parts->etoc->efi_last_u_lba); break; } /* * calculate number of blocks based on input */ return (blokno - efi_deflt->start_sector + 1); case 'm': /* * Convert token from megabytes to a * block number. */ if (sscanf(cleantoken, "%f2", &nmegs) != 1) { err_print("`%s' is not recognized\n", cleantoken); break; } /* * Check the bounds */ if (nmegs > bn2mb(bounds->upper - bounds->lower)) { err_print("`%1.2fmb' is out of range " "[0-%1.2f]\n", nmegs, bn2mb(bounds->upper - bounds->lower)); break; } return (mb2bn(nmegs)); case 'g': if (sscanf(cleantoken, "%f2", &nmegs) != 1) { err_print("`%s' is not recognized\n", cleantoken); break; } if (nmegs > bn2gb(bounds->upper - bounds->lower)) { err_print("`%1.2fgb' is out of range " "[0-%1.2f]\n", nmegs, bn2gb(bounds->upper - bounds->lower)); break; } return (gb2bn(nmegs)); case 't': if (sscanf(cleantoken, "%f2", &nmegs) != 1) { err_print("`%s' is not recognized\n", cleantoken); break; } if (nmegs > bn2tb(bounds->upper - bounds->lower)) { err_print("`%1.2ftb' is out of range " "[0-%1.2f]\n", nmegs, bn2tb(bounds->upper - bounds->lower)); break; } return (uint64_t)((float)nmegs * 1024.0 * 1024.0 * 1024.0 * 1024.0 / cur_blksz); default: err_print("Please specify units in either " "b(number of blocks), e(end sector),\n"); err_print(" g(gigabytes), m(megabytes)"); err_print(" or t(terabytes)\n"); break; } break; /* * If we don't recognize the input type, it's bad news. */ default: err_print("Error: unknown input type.\n"); fullabort(); } /* * If we get here, it's because some error kept us from accepting * the token. If we are running out of a command file, gracefully * leave the program. If we are interacting with the user, simply * reprompt. If the token was in the pipe, abort the current command. */ if (option_f) fullabort(); else if (interactive) goto reprompt; else cmdabort(SIGINT); /* * Never actually reached. */ return (-1); } /* * Print input choices */ static void print_input_choices(int type, u_ioparam_t *param) { char **sp; slist_t *lp; int width; int col; int ncols; switch (type) { case FIO_CSTR: fmt_print("Expecting one of the following:\n"); goto common; case FIO_MSTR: fmt_print("Expecting one of the following: "); fmt_print("(abbreviations ok):\n"); common: for (sp = (char **)param->io_charlist; *sp != NULL; sp++) { fmt_print("\t%s\n", *sp); } break; case FIO_SLIST: fmt_print("Expecting one of the following: "); fmt_print("(abbreviations ok):\n"); /* * Figure out the width of the widest string */ width = slist_widest_str((slist_t *)param->io_slist); width += 4; /* * If the help messages are empty, print the * possible choices in left-justified columns */ lp = (slist_t *)param->io_slist; if (*lp->help == 0) { col = 0; ncols = 60 / width; for (; lp->str != NULL; lp++) { if (col == 0) fmt_print("\t"); ljust_print(lp->str, (++col == ncols) ? 0 : width); if (col == ncols) { col = 0; fmt_print("\n"); } } if (col != 0) fmt_print("\n"); } else { /* * With help messages, print each choice, * and help message, on its own line. */ for (; lp->str != NULL; lp++) { fmt_print("\t"); ljust_print(lp->str, width); fmt_print("- %s\n", lp->help); } } break; default: err_print("Error: unknown input type.\n"); fullabort(); } fmt_print("\n"); } /* * Search a string list for a particular string. * Use minimum recognition, to accept unique abbreviations * Return the number of possible matches found. * If only one match was found, return the arbitrary value * associated with the matched string in match_value. */ int find_value(slist_t *slist, char *match_str, int *match_value) { int i; int nmatches; int length; int match_length; nmatches = 0; length = 0; match_length = strlen(match_str); for (; slist->str != NULL; slist++) { /* * See how many characters of the token match */ i = strcnt(match_str, slist->str); /* * If it's not the whole token, then it's not a match. */ if (i < match_length) continue; /* * If it ties with another input, remember that. */ if (i == length) nmatches++; /* * If it matches the most so far, record that. */ if (i > length) { *match_value = slist->value; nmatches = 1; length = i; } } return (nmatches); } /* * Search a string list for a particular value. * Return the string associated with that value. */ char * find_string(slist_t *slist, int match_value) { for (; slist->str != NULL; slist++) { if (slist->value == match_value) { return (slist->str); } } return (NULL); } /* * Return the width of the widest string in an slist */ static int slist_widest_str(slist_t *slist) { int i; int width; width = 0; for (; slist->str != NULL; slist++) { if ((i = strlen(slist->str)) > width) width = i; } return (width); } /* * Print a string left-justified to a fixed width. */ static void ljust_print(char *str, int width) { int i; fmt_print("%s", str); for (i = width - strlen(str); i > 0; i--) { fmt_print(" "); } } /* * This routine is a modified version of printf. It handles the cases * of silent mode and logging; other than that it is identical to the * library version. */ /*PRINTFLIKE1*/ void fmt_print(char *format, ...) { va_list ap; va_start(ap, format); /* * If we are running silent, skip it. */ if (option_s == 0) { /* * Do the print to standard out. */ if (need_newline) { (void) printf("\n"); } (void) vprintf(format, ap); /* * If we are logging, also print to the log file. */ if (log_file) { if (need_newline) { (void) fprintf(log_file, "\n"); } (void) vfprintf(log_file, format, ap); (void) fflush(log_file); } } need_newline = 0; va_end(ap); } /* * This routine is a modified version of printf. It handles the cases * of silent mode; other than that it is identical to the * library version. It differs from the above printf in that it does * not print the message to a log file. */ /*PRINTFLIKE1*/ void nolog_print(char *format, ...) { va_list ap; va_start(ap, format); /* * If we are running silent, skip it. */ if (option_s == 0) { /* * Do the print to standard out. */ if (need_newline) { (void) printf("\n"); } (void) vprintf(format, ap); } va_end(ap); need_newline = 0; } /* * This routine is a modified version of printf. It handles the cases * of silent mode, and only prints the message to the log file, not * stdout. Other than that is identical to the library version. */ /*PRINTFLIKE1*/ void log_print(char *format, ...) { va_list ap; va_start(ap, format); /* * If we are running silent, skip it. */ if (option_s == 0) { /* * Do the print to the log file. */ if (need_newline) { (void) fprintf(log_file, "\n"); } (void) vfprintf(log_file, format, ap); (void) fflush(log_file); } va_end(ap); need_newline = 0; } /* * This routine is a modified version of printf. It prints the message * to stderr, and to the log file is appropriate. * Other than that is identical to the library version. */ /*PRINTFLIKE1*/ void err_print(char *format, ...) { va_list ap; va_start(ap, format); /* * Flush anything pending to stdout */ if (need_newline) { (void) printf("\n"); } (void) fflush(stdout); /* * Do the print to stderr. */ (void) vfprintf(stderr, format, ap); /* * If we are logging, also print to the log file. */ if (log_file) { if (need_newline) { (void) fprintf(log_file, "\n"); } (void) vfprintf(log_file, format, ap); (void) fflush(log_file); } va_end(ap); need_newline = 0; } /* * Print a number of characters from a buffer. The buffer * does not need to be null-terminated. Since the data * may be coming from a device, we cannot be sure the * data is not crud, so be rather defensive. */ void print_buf(char *buf, int nbytes) { int c; while (nbytes-- > 0) { c = *buf++; if (isascii(c) && isprint(c)) { fmt_print("%c", c); } else break; } } #ifdef not /* * This routine prints out a message describing the given ctlr. * The message is identical to the one printed by the kernel during * booting. */ void pr_ctlrline(struct ctlr_info *ctlr) { fmt_print(" %s%d at %s 0x%x ", ctlr->ctlr_cname, ctlr->ctlr_num, space2str(ctlr->ctlr_space), ctlr->ctlr_addr); if (ctlr->ctlr_vec != 0) fmt_print("vec 0x%x ", ctlr->ctlr_vec); else fmt_print("pri %d ", ctlr->ctlr_prio); fmt_print("\n"); } #endif /* not */ /* * This routine prints out a message describing the given disk. * The message is identical to the one printed by the kernel during * booting. */ void pr_diskline(struct disk_info *disk, int num) { struct ctlr_info *ctlr = disk->disk_ctlr; struct disk_type *type = disk->disk_type; fmt_print(" %4d. %s ", num, disk->disk_name); if ((type != NULL) && (disk->label_type == L_TYPE_SOLARIS)) { fmt_print("<%s cyl %u alt %u hd %u sec %u>", type->dtype_asciilabel, type->dtype_ncyl, type->dtype_acyl, type->dtype_nhead, type->dtype_nsect); } else if ((type != NULL) && (disk->label_type == L_TYPE_EFI)) { cur_blksz = disk->disk_lbasize; print_efi_string(type->vendor, type->product, type->revision, type->capacity); } else if (disk->disk_flags & DSK_RESERVED) { fmt_print(""); } else if (disk->disk_flags & DSK_UNAVAILABLE) { fmt_print(""); } else { fmt_print(""); } if (chk_volname(disk)) { fmt_print(" "); print_volname(disk); } fmt_print("\n"); if (disk->devfs_name != NULL) { fmt_print(" %s\n", disk->devfs_name); } else { fmt_print(" %s%d at %s%d slave %d\n", ctlr->ctlr_dname, disk->disk_dkinfo.dki_unit, ctlr->ctlr_cname, ctlr->ctlr_num, disk->disk_dkinfo.dki_slave); } #ifdef OLD fmt_print(" %4d. %s at %s%d slave %d", num, disk->disk_name, ctlr->ctlr_cname, ctlr->ctlr_num, disk->disk_dkinfo.dki_slave); if (chk_volname(disk)) { fmt_print(": "); print_volname(disk); } fmt_print("\n"); if (type != NULL) { fmt_print(" %s%d: <%s cyl %u alt %u hd %u sec %u>\n", ctlr->ctlr_dname, disk->disk_dkinfo.dki_unit, type->dtype_asciilabel, type->dtype_ncyl, type->dtype_acyl, type->dtype_nhead, type->dtype_nsect); } else { fmt_print(" %s%d: \n", ctlr->ctlr_dname, disk->disk_dkinfo.dki_unit); } #endif /* OLD */ } /* * This routine prints out a given disk block number in cylinder/head/sector * format. It uses the printing routine passed in to do the actual output. */ void pr_dblock(void (*func)(char *, ...), diskaddr_t bn) { if (cur_label == L_TYPE_SOLARIS) { (*func)("%u/%u/%u", bn2c(bn), bn2h(bn), bn2s(bn)); } else { (*func)("%llu", bn); } } /* * This routine inputs a character from the data file. It understands * the use of '\' to prevent interpretation of a newline. It also keeps * track of the current line in the data file via a global variable. */ static int sup_inputchar(void) { int c; /* * Input the character. */ c = getc(data_file); /* * If it's not a backslash, return it. */ if (c != '\\') return (c); /* * It was a backslash. Get the next character. */ c = getc(data_file); /* * If it was a newline, update the line counter and get the next * character. */ if (c == '\n') { data_lineno++; c = getc(data_file); } /* * Return the character. */ return (c); } /* * This routine pushes a character back onto the input pipe for the data file. */ static void sup_pushchar(int c) { (void) ungetc(c, data_file); } /* * Variables to support pushing back tokens */ static int have_pushed_token = 0; static TOKEN pushed_buf; static int pushed_token; /* * This routine inputs a token from the data file. A token is a series * of contiguous non-white characters or a recognized special delimiter * character. Use of the wrapper lets us always have the value of the * last token around, which is useful for error recovery. */ int sup_gettoken(char *buf) { last_token_type = sup_get_token(buf); return (last_token_type); } static int sup_get_token(char *buf) { char *ptr = buf; int c, quoted = 0; /* * First check for presence of push-backed token. * If so, return it. */ if (have_pushed_token) { have_pushed_token = 0; bcopy(pushed_buf, buf, TOKEN_SIZE+1); return (pushed_token); } /* * Zero out the returned token buffer */ bzero(buf, TOKEN_SIZE + 1); /* * Strip off leading white-space. */ while ((isspace(c = sup_inputchar())) && (c != '\n')) ; /* * Read in characters until we hit unquoted white-space. */ for (; !isspace(c) || quoted; c = sup_inputchar()) { /* * If we hit eof, that's a token. */ if (feof(data_file)) return (SUP_EOF); /* * If we hit a double quote, change the state of quoting. */ if (c == '"') { quoted = !quoted; continue; } /* * If we hit a newline, that delimits a token. */ if (c == '\n') break; /* * If we hit any nonquoted special delimiters, that delimits * a token. */ if (!quoted && (c == '=' || c == ',' || c == ':' || c == '#' || c == '|' || c == '&' || c == '~')) break; /* * Store the character if there's room left. */ if (ptr - buf < TOKEN_SIZE) *ptr++ = (char)c; } /* * If we stored characters in the buffer, then we inputted a string. * Push the delimiter back into the pipe and return the string. */ if (ptr - buf > 0) { sup_pushchar(c); return (SUP_STRING); } /* * We didn't input a string, so we must have inputted a known delimiter. * store the delimiter in the buffer, so it will get returned. */ buf[0] = c; /* * Switch on the delimiter. Return the appropriate value for each one. */ switch (c) { case '=': return (SUP_EQL); case ':': return (SUP_COLON); case ',': return (SUP_COMMA); case '\n': return (SUP_EOL); case '|': return (SUP_OR); case '&': return (SUP_AND); case '~': return (SUP_TILDE); case '#': /* * For comments, we flush out the rest of the line and return * an EOL. */ while ((c = sup_inputchar()) != '\n' && !feof(data_file)) ; if (feof(data_file)) return (SUP_EOF); else return (SUP_EOL); /* * Shouldn't ever get here. */ default: return (SUP_STRING); } } /* * Push back a token */ void sup_pushtoken(char *token_buf, int token_type) { /* * We can only push one token back at a time */ assert(have_pushed_token == 0); have_pushed_token = 1; bcopy(token_buf, pushed_buf, TOKEN_SIZE+1); pushed_token = token_type; } /* * Get an entire line of input. Handles logging, comments, * and EOF. */ void get_inputline(char *line, int nbytes) { char *p = line; int c; /* * Remove any leading white-space and comments */ do { while ((isspace(c = getchar())) && (c != '\n')) ; } while (c == COMMENT_CHAR); /* * Loop on each character until end of line */ while (c != '\n') { /* * If we hit eof, get out. */ if (checkeof()) { fullabort(); } /* * Add the character to the buffer. */ if (nbytes > 1) { *p++ = (char)c; nbytes --; } /* * Get the next character. */ c = getchar(); } /* * Null terminate the token. */ *p = 0; /* * Indicate that we've emptied the pipe */ token_present = 0; /* * If we're running out of a file, echo the line to * the user, otherwise if we're logging, copy the * input to the log file. */ if (option_f) { fmt_print("%s\n", line); } else if (log_file) { log_print("%s\n", line); } } /* * execute the shell escape command */ int execute_shell(char *s, size_t buff_size) { struct termio termio; struct termios tty; int tty_flag, i, j; char *shell_name; static char *default_shell = "/bin/sh"; tty_flag = -1; if (*s == '\0') { shell_name = getenv("SHELL"); if (shell_name == NULL) { shell_name = default_shell; } if (strlcpy(s, shell_name, buff_size) >= buff_size) { err_print("Error: Shell command ($SHELL) too long.\n"); fullabort(); } } /* save tty information */ if (isatty(0)) { if (ioctl(0, TCGETS, &tty) == 0) tty_flag = 1; else { if (ioctl(0, TCGETA, &termio) == 0) { tty_flag = 0; tty.c_iflag = termio.c_iflag; tty.c_oflag = termio.c_oflag; tty.c_cflag = termio.c_cflag; tty.c_lflag = termio.c_lflag; for (i = 0; i < NCC; i++) tty.c_cc[i] = termio.c_cc[i]; } } } /* close the current file descriptor */ if (cur_disk != NULL) { (void) close(cur_file); } /* execute the shell escape */ (void) system(s); /* reopen file descriptor if one was open before */ if (cur_disk != NULL) { if ((cur_file = open_disk(cur_disk->disk_path, O_RDWR | O_NDELAY)) < 0) { err_print("Error: can't reopen selected disk '%s'. \n", cur_disk->disk_name); fullabort(); } } /* Restore tty information */ if (isatty(0)) { if (tty_flag > 0) (void) ioctl(0, TCSETSW, &tty); else if (tty_flag == 0) { termio.c_iflag = tty.c_iflag; termio.c_oflag = tty.c_oflag; termio.c_cflag = tty.c_cflag; termio.c_lflag = tty.c_lflag; for (j = 0; j < NCC; j++) termio.c_cc[j] = tty.c_cc[j]; (void) ioctl(0, TCSETAW, &termio); } if (isatty(1)) { fmt_print("\n[Hit Return to continue] \n"); (void) fflush(stdin); if (getchar() == EOF) fullabort(); } } return (0); } void print_efi_string(char *vendor, char *product, char *revision, uint64_t capacity) { char *new_vendor; char *new_product; char *new_revision; char capacity_string[10]; float scaled; int i; /* Strip whitespace from the end of inquiry strings */ new_vendor = strdup(vendor); if (new_vendor == NULL) return; for (i = (strlen(new_vendor) - 1); i >= 0; i--) { if (new_vendor[i] != 0x20) { new_vendor[i+1] = '\0'; break; } } new_product = strdup(product); if (new_product == NULL) { free(new_vendor); return; } for (i = (strlen(new_product) - 1); i >= 0; i--) { if (new_product[i] != 0x20) { new_product[i+1] = '\0'; break; } } new_revision = strdup(revision); if (new_product == NULL) { free(new_vendor); free(new_product); return; } for (i = (strlen(new_revision) - 1); i >= 0; i--) { if (new_revision[i] != 0x20) { new_revision[i+1] = '\0'; break; } } /* Now build size string */ scaled = bn2mb(capacity); if (scaled >= (float)1024.0 * 1024) { (void) snprintf(capacity_string, sizeof (capacity_string), "%.2fTB", scaled/((float)1024.0 * 1024)); } else if (scaled >= (float)1024.0) { (void) snprintf(capacity_string, sizeof (capacity_string), "%.2fGB", scaled/(float)1024.0); } else { (void) snprintf(capacity_string, sizeof (capacity_string), "%.2fMB", scaled); } fmt_print("<%s-%s-%s-%s>", new_vendor, new_product, new_revision, capacity_string); free(new_revision); free(new_product); free(new_vendor); } /* * 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 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _IO_H #define _IO_H #ifdef __cplusplus extern "C" { #endif /* * Bounds structure for integer and disk input. */ struct bounds { diskaddr_t lower; diskaddr_t upper; }; /* * List of strings with arbitrary matching values */ typedef struct slist { char *str; char *help; int value; } slist_t; /* * Input structure for current partition information */ typedef struct partition_defaults { uint_t start_cyl; uint_t deflt_size; } part_deflt_t; typedef struct efi_defaults { uint64_t start_sector; uint64_t end_sector; } efi_deflt_t; /* * Input parameter can be any one of these types. */ typedef union input_param { struct slist *io_slist; char **io_charlist; struct bounds io_bounds; } u_ioparam_t; /* * These declarations define the legal input types. */ #define FIO_BN 0 /* block number */ #define FIO_INT 1 /* integer input */ #define FIO_CSTR 2 /* closed string - exact match */ #define FIO_MSTR 3 /* matched string, with abbreviations */ #define FIO_OSTR 4 /* open string - anything's legal */ #define FIO_BLNK 5 /* blank line */ #define FIO_SLIST 6 /* one string out of a list, abbr. */ #define FIO_CYL 7 /* nblocks, on cylinder boundary */ #define FIO_OPINT 8 /* optional integer input */ #define FIO_ECYL 9 /* allows end cylinder input */ #define FIO_INT64 10 /* Input for EFI partitions */ #define FIO_EFI 11 /* Input EFI part size */ /* * Miscellaneous definitions. */ #define TOKEN_SIZE 36 /* max length of a token */ typedef char TOKEN[TOKEN_SIZE+1]; /* token type */ #define DATA_INPUT 0 /* 2 modes of input */ #define CMD_INPUT 1 #define WILD_STRING "$" /* wildcard character */ #define COMMENT_CHAR '#' /* comment character */ /* * Prototypes for ANSI C */ char *gettoken(char *inbuf); void clean_token(char *cleantoken, char *token); int geti(char *str, int *iptr, int *wild); uint64_t input(int, char *, int, u_ioparam_t *, int *, int); int find_value(slist_t *slist, char *match_str, int *match_value); char *find_string(slist_t *slist, int match_value); void fmt_print(char *format, ...) __PRINTFLIKE(1); void nolog_print(char *format, ...) __PRINTFLIKE(1); void log_print(char *format, ...) __PRINTFLIKE(1); void err_print(char *format, ...) __PRINTFLIKE(1); void print_buf(char *buf, int nbytes); void pr_diskline(struct disk_info *disk, int num); void pr_dblock(void (*func)(char *, ...), diskaddr_t bn); int sup_gettoken(char *buf); void sup_pushtoken(char *token_buf, int token_type); void get_inputline(char *, int); int istokenpresent(void); int execute_shell(char *, size_t); void print_efi_string(char *vendor, char *product, char *revision, uint64_t capacity); /* * Most recent token type */ extern int last_token_type; #ifdef __cplusplus } #endif #endif /* _IO_H */ /* * 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 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * copyright (c) 1990, 1991 UNIX System Laboratories, Inc. * copyright (c) 1984, 1986, 1987, 1988, 1989, 1990 AT&T * All rights reserved. */ /* * Copyrighted as an unpublished work. * (c) Copyright INTERACTIVE Systems Corporation 1986, 1988, 1990 * All rights reserved. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "badsec.h" #include "global.h" #include "ctlr_ata.h" #include "misc.h" #define FAILURE 1 #define SUCCESS 0 #define CMD_READ 0 #define CMD_WRITE 1 struct badsec_lst *badsl_chain = NULL; int badsl_chain_cnt = 0; struct badsec_lst *gbadsl_chain = NULL; int gbadsl_chain_cnt = 0; static struct alts_mempart alts_part = { 0, NULL, 0 }; struct alts_mempart *ap = &alts_part; /* pointer to incore */ /* alts tables */ /* prototypes */ int updatebadsec(struct dkl_partition *, int); int read_altsctr(struct dkl_partition *); static int chk_badsec(void); static int init_altsctr(void); static int get_altsctr(void); int wr_altsctr(void); static void get_badsec(void); static int count_badsec(void); static int gen_alts_ent(void); static int assign_altsctr(void); static void expand_map(void); static void compress_map(void); static int altsmap_getbit(blkaddr_t); static blkaddr_t altsmap_alloc(blkaddr_t, blkaddr_t, int, int); static void ent_sort(struct alts_ent *, int); static void ent_compress(struct alts_ent *, int); static int ent_merge(struct alts_ent *, struct alts_ent *, int, struct alts_ent *, int); static int ent_bsearch(struct alts_ent *, int, struct alts_ent *); static int chk_bad_altsctr(blkaddr_t); /* * updatebadsec () -- update bad sector/track mapping tables */ int updatebadsec(struct dkl_partition *part, int init_flag) { if (init_flag) ap->ap_flag |= ALTS_ADDPART; get_badsec(); (void) read_altsctr(part); ent_sort(ap->ap_gbadp, ap->ap_gbadcnt); ent_compress(ap->ap_gbadp, ap->ap_gbadcnt); (void) gen_alts_ent(); compress_map(); return (SUCCESS); } /* * read_altsctr( ptr to alternate sector partition ) * -- read the alternate sector partition tables */ int read_altsctr(struct dkl_partition *part) { if (ap->ap_tblp == NULL) { /* allocate buffer for the alts partition table (sector size) */ ap->ap_tbl_secsiz = byte_to_secsiz(ALTS_PARTTBL_SIZE, NBPSCTR); ap->ap_tblp = malloc(ap->ap_tbl_secsiz); if (ap->ap_tblp == NULL) { (void) fprintf(stderr, "Unable to malloc alternate partition table.\n"); return (50); } /* * allocate buffer for the alts partition map (sector size) * buffers include the disk image bit map * and the incore transformed char map. */ if ((ap->ap_memmapp = malloc(part->p_size)) == NULL) { (void) fprintf(stderr, "Unable to malloc incore " "alternate partition map.\n"); return (51); } ap->ap_tblp->alts_map_len = (part->p_size + 8 - 1) / 8; ap->ap_map_secsiz = byte_to_secsiz(ap->ap_tblp->alts_map_len, NBPSCTR); ap->ap_map_sectot = ap->ap_map_secsiz / NBPSCTR; if ((ap->ap_mapp = malloc(ap->ap_map_secsiz)) == NULL) { (void) fprintf(stderr, "Unable to malloc alternate " "partition map.\n"); return (52); } /* clear the buffers to zero */ (void) memset(ap->ap_memmapp, 0, part->p_size); (void) memset(ap->ap_mapp, 0, ap->ap_map_secsiz); ap->part = *part; /* struct copy */ /* * if add alternate partition flag is set, then install * the partition. Otherwise read the alts partition info * from disk. If failed, then assume the first installation */ if (ap->ap_flag & ALTS_ADDPART) { (void) fprintf(stderr, "WARNING: Manually " "initializing alternate table.\n"); (void) init_altsctr(); } else { if (get_altsctr() == SUCCESS) (void) chk_badsec(); else (void) init_altsctr(); } } return (SUCCESS); } /* * checking duplicate bad sectors or bad sectors in ALTSCTR partition */ static int chk_badsec(void) { blkaddr_t badsec; blkaddr_t altsp_srtsec = ap->part.p_start; blkaddr_t altsp_endsec = ap->part.p_start + ap->part.p_size - 1; int cnt; int status; for (cnt = 0; cnt < ap->ap_gbadcnt; cnt++) { badsec = (ap->ap_gbadp)[cnt].bad_start; /* if bad sector is within the ATLSCTR partition */ if ((badsec >= altsp_srtsec) && (badsec <= altsp_endsec)) { if ((ap->ap_memmapp)[badsec - altsp_srtsec] != ALTS_BAD) { if ((badsec >= altsp_srtsec) && (badsec <= (altsp_srtsec + ap->ap_tbl_secsiz / NBPSCTR - 1))) { (void) fprintf(stderr, "Alternate partition information " "table is bad.\n"); return (53); } if ((badsec >= altsp_srtsec+ap->ap_tblp->alts_map_base) && (badsec <= (altsp_srtsec + ap->ap_tblp->alts_map_base + ap->ap_map_sectot - 1))) { (void) fprintf(stderr, "Alternate " "partition map is bad.\n"); return (54); } if ((badsec >= altsp_srtsec+ap->ap_tblp->alts_ent_base) && (badsec <= (altsp_srtsec + ap->ap_tblp->alts_ent_base + ap->ap_ent_secsiz / NBPSCTR - 1))) { (void) fprintf(stderr, "Alternate " "partition entry table is bad.\n"); return (55); } (ap->ap_memmapp)[badsec - altsp_srtsec] = ALTS_BAD; (ap->ap_gbadp)[cnt].bad_start = (uint32_t)ALTS_ENT_EMPTY; } else { status = chk_bad_altsctr(badsec); (ap->ap_gbadp)[cnt].bad_start = (uint32_t)ALTS_ENT_EMPTY; } } else { /* * binary search for bad sector in the alts entry table */ status = ent_bsearch(ap->ap_entp, ap->ap_tblp->alts_ent_used, &((ap->ap_gbadp)[cnt])); /* * if the bad sector had already been remapped * (found in alts_entry), then ignore the bad sector. */ if (status != -1) { (ap->ap_gbadp)[cnt].bad_start = (uint32_t)ALTS_ENT_EMPTY; } } } return (SUCCESS); } /* * initialize the alternate partition tables */ static int init_altsctr(void) { blkaddr_t badsec; blkaddr_t altsp_srtsec = ap->part.p_start; blkaddr_t altsp_endsec = ap->part.p_start + ap->part.p_size - 1; int cnt; ap->ap_entp = NULL; ap->ap_ent_secsiz = 0; ap->ap_tblp->alts_sanity = ALTS_SANITY; ap->ap_tblp->alts_version = ALTS_VERSION1; ap->ap_tblp->alts_map_len = (ap->part.p_size + 8 - 1) / 8; ap->ap_tblp->alts_ent_used = 0; ap->ap_tblp->alts_ent_base = 0; ap->ap_tblp->alts_ent_end = 0; ap->ap_tblp->alts_resv_base = ap->part.p_size - 1; for (cnt = 0; cnt < 5; cnt++) ap->ap_tblp->alts_pad[cnt] = 0; for (cnt = 0; cnt < ap->ap_gbadcnt; cnt++) { badsec = (ap->ap_gbadp)[cnt].bad_start; if ((badsec >= altsp_srtsec) && (badsec <= altsp_endsec)) { if (badsec == altsp_srtsec) { (void) fprintf(stderr, "First sector of " "alternate partition is bad.\n"); return (56); } (ap->ap_memmapp)[badsec - altsp_srtsec] = ALTS_BAD; (ap->ap_gbadp)[cnt].bad_start = (uint32_t)ALTS_ENT_EMPTY; } } /* allocate the alts_map on disk skipping possible bad sectors */ ap->ap_tblp->alts_map_base = altsmap_alloc(ap->ap_tbl_secsiz / NBPSCTR, ap->part.p_size, ap->ap_map_sectot, ALTS_MAP_UP); if (ap->ap_tblp->alts_map_base == 0) { perror("Unable to allocate alternate map on disk: "); return (57); } (void) wr_altsctr(); return (SUCCESS); } /* * read the alternate partition tables from disk */ static int get_altsctr(void) { int mystatus = FAILURE; int status = 0; /* get alts partition table info */ status = ata_rdwr(DIR_READ, cur_file, altsec_offset, ap->ap_tbl_secsiz / UBSIZE, (char *)ap->ap_tblp, 0, NULL); if (status == FAILURE) { perror("Unable to read alternate sector partition: "); return (58); } if (ap->ap_tblp->alts_sanity != ALTS_SANITY) return (mystatus); /* get the alts map */ status = ata_rdwr(DIR_READ, cur_file, (ap->ap_tblp->alts_map_base) + altsec_offset, ap->ap_map_secsiz / UBSIZE, (char *)ap->ap_mapp, 0, NULL); if (status == FAILURE) { perror("Unable to read alternate sector partition map: "); return (59); } /* transform the disk image bit-map to incore char map */ expand_map(); if (ap->ap_tblp->alts_ent_used == 0) { ap->ap_entp = NULL; ap->ap_ent_secsiz = 0; } else { ap->ap_ent_secsiz = byte_to_secsiz( (ap->ap_tblp->alts_ent_used*ALTS_ENT_SIZE), NBPSCTR); ap->ap_entp = malloc(ap->ap_ent_secsiz); if (ap->ap_entp == NULL) { (void) fprintf(stderr, "Unable to malloc alternate sector entry table.\n"); return (60); } status = ata_rdwr(DIR_READ, cur_file, (ap->ap_tblp->alts_ent_base) + altsec_offset, ap->ap_ent_secsiz / UBSIZE, (char *)ap->ap_entp, 0, NULL); if (status == FAILURE) { perror("Unable to read alternate sector entry table: "); return (61); } } return (SUCCESS); } /* * update the new alternate partition tables on disk */ int wr_altsctr(void) { int status; if (ap->ap_tblp == NULL) return (0); status = ata_rdwr(DIR_WRITE, cur_file, altsec_offset, ap->ap_tbl_secsiz / UBSIZE, (char *)ap->ap_tblp, 0, NULL); if (status) { (void) printf("ata_rdwr status = %d need = %d\n", status, ap->ap_tbl_secsiz / 512); perror("Unable to write with ata_rdwr the alt sector part: "); return (62); } if (ata_rdwr(DIR_WRITE, cur_file, (ap->ap_tblp->alts_map_base) + altsec_offset, ap->ap_map_secsiz / UBSIZE, (char *)ap->ap_mapp, 0, NULL) == FAILURE) { perror("Unable to write alternate sector partition map: "); return (63); } if (ap->ap_tblp->alts_ent_used != 0) { if (ata_rdwr(DIR_WRITE, cur_file, (ap->ap_tblp->alts_ent_base)+ altsec_offset, ap->ap_ent_secsiz / UBSIZE, (char *)ap->ap_entp, 0, NULL) == FAILURE) { perror("Unable to write alternate sector " "entry table: "); return (64); } } return (0); } /* * get a list of bad sector */ static void get_badsec(void) { int cnt; struct badsec_lst *blc_p; blkaddr_t curbad; blkaddr_t maxsec = cur_dtype->dtype_nhead * cur_dtype->dtype_ncyl * cur_dtype->dtype_nsect; struct alts_ent *growbadp; int i; cnt = count_badsec(); if (cnt == 0) { ap->ap_gbadp = NULL; ap->ap_gbadcnt = 0; } else { ap->ap_gbadp = malloc(cnt * ALTS_ENT_SIZE); if (ap->ap_gbadp == NULL) { err_print("get_badsec: unable to malloc %d bytes\n", cnt * ALTS_ENT_SIZE); fullabort(); } (void) memset(ap->ap_gbadp, 0, cnt * ALTS_ENT_SIZE); for (growbadp = ap->ap_gbadp, cnt = 0, blc_p = badsl_chain; blc_p; blc_p = blc_p->bl_nxt) { for (i = 0; i < blc_p->bl_cnt; i++) { curbad = blc_p->bl_sec[i]; if (curbad < (blkaddr_t)cur_dtype->dtype_nsect) { (void) fprintf(stderr, "Ignoring bad " "sector %ld which is in first " "track of the drive.\n", curbad); continue; } if (curbad >= maxsec) { (void) fprintf(stderr, "Ignoring bad " "sector %ld which is past the end " "of the drive.\n", curbad); continue; } growbadp[cnt].bad_start = curbad; growbadp[cnt].bad_end = curbad; cnt++; } } } ap->ap_gbadcnt = cnt; } /* * count number of bad sector on list * merging the bad sector list from surface analysis and the * one given through the command line */ static int count_badsec(void) { struct badsec_lst *blc_p; if (!badsl_chain) { badsl_chain = gbadsl_chain; } else { for (blc_p = badsl_chain; blc_p->bl_nxt; blc_p = blc_p->bl_nxt) ; blc_p->bl_nxt = gbadsl_chain; } badsl_chain_cnt += gbadsl_chain_cnt; return (badsl_chain_cnt); } /* * generate alternate entry table by merging the existing and * the new entry list. */ static int gen_alts_ent(void) { uint_t ent_used; struct alts_ent *entp; if (ap->ap_gbadcnt == 0) return (0); ent_used = ap->ap_tblp->alts_ent_used + ap->ap_gbadcnt; ap->ap_ent_secsiz = byte_to_secsiz(ent_used*ALTS_ENT_SIZE, NBPSCTR); entp = malloc(ap->ap_ent_secsiz); if (entp == NULL) { err_print("get_alts_ent: unable to malloc %d bytes\n", ap->ap_ent_secsiz); fullabort(); } ent_used = ent_merge(entp, ap->ap_entp, ap->ap_tblp->alts_ent_used, ap->ap_gbadp, ap->ap_gbadcnt); free(ap->ap_entp); free(ap->ap_gbadp); ap->ap_entp = entp; ap->ap_ent_secsiz = byte_to_secsiz(ent_used*ALTS_ENT_SIZE, NBPSCTR); ap->ap_tblp->alts_ent_used = ent_used; ap->ap_gbadp = NULL; ap->ap_gbadcnt = 0; /* assign alternate sectors to the bad sectors */ (void) assign_altsctr(); /* allocate the alts_entry on disk skipping possible bad sectors */ ap->ap_tblp->alts_ent_base = altsmap_alloc((blkaddr_t)ap->ap_tblp->alts_map_base + ap->ap_map_sectot, (blkaddr_t)ap->part.p_size, ap->ap_ent_secsiz / NBPSCTR, ALTS_MAP_UP); if (ap->ap_tblp->alts_ent_base == 0) { perror("Unable to allocate alternate entry table on disk: "); return (65); } ap->ap_tblp->alts_ent_end = ap->ap_tblp->alts_ent_base + (ap->ap_ent_secsiz / NBPSCTR) - 1; return (0); } /* * assign alternate sectors for bad sector mapping */ static int assign_altsctr(void) { uint_t i; uint_t j; blkaddr_t alts_ind; uint_t cluster; for (i = 0; i < ap->ap_tblp->alts_ent_used; i++) { if ((ap->ap_entp)[i].bad_start == (uint32_t)ALTS_ENT_EMPTY) continue; if ((ap->ap_entp)[i].good_start != 0) continue; cluster = (ap->ap_entp)[i].bad_end - (ap->ap_entp)[i].bad_start + 1; alts_ind = altsmap_alloc(ap->part.p_size - 1, ap->ap_tblp->alts_map_base + ap->ap_map_sectot - 1, cluster, ALTS_MAP_DOWN); if (alts_ind == 0) { (void) fprintf(stderr, "Unable to allocate alternates " "for bad starting sector %u.\n", (ap->ap_entp)[i].bad_start); return (65); } alts_ind = alts_ind - cluster + 1; (ap->ap_entp)[i].good_start = alts_ind +ap->part.p_start; for (j = 0; j < cluster; j++) { (ap->ap_memmapp)[alts_ind+j] = ALTS_BAD; } } return (SUCCESS); } /* * transform the disk image alts bit map to incore char map */ static void expand_map(void) { int i; for (i = 0; i < ap->part.p_size; i++) { (ap->ap_memmapp)[i] = altsmap_getbit(i); } } /* * transform the incore alts char map to the disk image bit map */ static void compress_map(void) { int i; int bytesz; char mask = 0; int maplen = 0; for (i = 0, bytesz = 7; i < ap->part.p_size; i++) { mask |= ((ap->ap_memmapp)[i] << bytesz--); if (bytesz < 0) { (ap->ap_mapp)[maplen++] = mask; bytesz = 7; mask = 0; } } /* * if partition size != multiple number of bytes * then record the last partial byte */ if (bytesz != 7) (ap->ap_mapp)[maplen] = mask; } /* * given a bad sector number, search in the alts bit map * and identify the sector as good or bad */ static int altsmap_getbit(blkaddr_t badsec) { uint_t slot = badsec / 8; uint_t field = badsec % 8; uchar_t mask; mask = ALTS_BAD<<7; mask >>= field; if ((ap->ap_mapp)[slot] & mask) return (ALTS_BAD); return (ALTS_GOOD); } /* * allocate a range of sectors from the alternate partition */ static blkaddr_t altsmap_alloc(blkaddr_t srt_ind, blkaddr_t end_ind, int cnt, int dir) { blkaddr_t i; blkaddr_t total; blkaddr_t first_ind; for (i = srt_ind, first_ind = srt_ind, total = 0; i != end_ind; i += dir) { if ((ap->ap_memmapp)[i] == ALTS_BAD) { total = 0; first_ind = i + dir; continue; } total++; if (total == cnt) return (first_ind); } return (0); } /* * bubble sort the entry table into ascending order */ static void ent_sort(struct alts_ent buf[], int cnt) { struct alts_ent temp; int flag; int i, j; for (i = 0; i < cnt-1; i++) { temp = buf[cnt-1]; flag = 1; for (j = cnt-1; j > i; j--) { if (buf[j-1].bad_start < temp.bad_start) { buf[j] = temp; temp = buf[j - 1]; } else { buf[j] = buf[j - 1]; flag = 0; } } buf[i] = temp; if (flag) break; } } /* * compress all the contiguous bad sectors into a single entry * in the entry table. The entry table must be sorted into ascending * before the compression. */ static void ent_compress(struct alts_ent buf[], int cnt) { int keyp; int movp; int i; for (i = 0; i < cnt; i++) { if (buf[i].bad_start == (uint32_t)ALTS_ENT_EMPTY) continue; for (keyp = i, movp = i+1; movp < cnt; movp++) { if (buf[movp].bad_start == (uint32_t)ALTS_ENT_EMPTY) continue; if (buf[keyp].bad_end+1 != buf[movp].bad_start) break; buf[keyp].bad_end++; buf[movp].bad_start = (uint32_t)ALTS_ENT_EMPTY; } if (movp == cnt) break; } } /* * merging two entry tables into a single table. In addition, * all empty slots in the entry table will be removed. */ static int ent_merge(struct alts_ent buf[], struct alts_ent list1[], int lcnt1, struct alts_ent list2[], int lcnt2) { int i; int j1, j2; for (i = 0, j1 = 0, j2 = 0; j1 < lcnt1 && j2 < lcnt2; ) { if (list1[j1].bad_start == (uint32_t)ALTS_ENT_EMPTY) { j1++; continue; } if (list2[j2].bad_start == (uint32_t)ALTS_ENT_EMPTY) { j2++; continue; } if (list1[j1].bad_start < list2[j2].bad_start) buf[i++] = list1[j1++]; else buf[i++] = list2[j2++]; } for (; j1 < lcnt1; j1++) { if (list1[j1].bad_start == (uint32_t)ALTS_ENT_EMPTY) continue; buf[i++] = list1[j1]; } for (; j2 < lcnt2; j2++) { if (list2[j2].bad_start == (uint32_t)ALTS_ENT_EMPTY) continue; buf[i++] = list2[j2]; } return (i); } /* * binary search for bad sector in the alternate entry table */ static int ent_bsearch(struct alts_ent buf[], int cnt, struct alts_ent *key) { int i; int ind; int interval; int mystatus = -1; if (!cnt) return (mystatus); for (i = 1; i <= cnt; i <<= 1) ind = i; for (interval = ind; interval; ) { if ((key->bad_start >= buf[ind-1].bad_start) && (key->bad_start <= buf[ind-1].bad_end)) { return (mystatus = ind-1); } else { interval >>= 1; if (!interval) break; if (key->bad_start < buf[ind-1].bad_start) { ind = ind - interval; } else { /* * if key is larger than the last element, * then break. */ if (ind == cnt) break; if ((ind + interval) <= cnt) ind += interval; } } } return (mystatus); } /* * check for bad sector in assigned alternate sectors */ static int chk_bad_altsctr(blkaddr_t badsec) { int i; blkaddr_t numsec; int cnt = ap->ap_tblp->alts_ent_used; /* * daddr_t intv[3]; */ for (i = 0; i < cnt; i++) { numsec = (ap->ap_entp)[i].bad_end - (ap->ap_entp)[i].bad_start; if ((badsec >= (ap->ap_entp)[i].good_start) && (badsec <= ((ap->ap_entp)[i].good_start + numsec))) { (void) fprintf(stderr, "Bad sector %ld is an assigned alternate sector.\n", badsec); return (66); /* * if (!numsec) { * (ap->ap_entp)[i].good_start = 0; * return (FAILURE); * } * intv[0] = badsec - (ap->ap_entp)[i].good_start; * intv[1] = 1; * intv[2] = (ap->ap_entp)[i].good_start + numsec - badsec; */ } } /* the bad sector has already been identified as bad */ return (SUCCESS); } /* * 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 (c) 1991, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright 2015 Nexenta Systems, Inc. All rights reserved. */ /* * This file contains the code relating to label manipulation. */ #include #include #include #include #include #include #include #include #include #include #include "global.h" #include "label.h" #include "misc.h" #include "main.h" #include "partition.h" #include "ctlr_scsi.h" #include "checkdev.h" #if defined(_FIRMWARE_NEEDS_FDISK) #include #include "menu_fdisk.h" #endif /* defined(_FIRMWARE_NEEDS_FDISK) */ #ifndef WD_NODE #define WD_NODE 7 #endif static int do_geometry_sanity_check(void); static int vtoc_to_label(struct dk_label *, struct extvtoc *, struct dk_geom *, struct dk_cinfo *); extern int read_extvtoc(int, struct extvtoc *); extern int write_extvtoc(int, struct extvtoc *); static int vtoc64_to_label(struct efi_info *, struct dk_gpt *); #ifdef DEBUG static void dump_label(struct dk_label *); #endif /* * This routine checks the given label to see if it is valid. */ int checklabel(struct dk_label *label) { /* * Check the magic number. */ if (label->dkl_magic != DKL_MAGIC) return (0); /* * Check the checksum. */ if (checksum(label, CK_CHECKSUM) != 0) return (0); return (1); } /* * This routine checks or calculates the label checksum, depending on * the mode it is called in. */ int checksum(struct dk_label *label, int mode) { short *sp, sum = 0; short count = (sizeof (struct dk_label)) / (sizeof (short)); /* * If we are generating a checksum, don't include the checksum * in the rolling xor. */ if (mode == CK_MAKESUM) count -= 1; sp = (short *)label; /* * Take the xor of all the half-words in the label. */ while (count--) { sum ^= *sp++; } /* * If we are checking the checksum, the total will be zero for * a correct checksum, so we can just return the sum. */ if (mode == CK_CHECKSUM) return (sum); /* * If we are generating the checksum, fill it in. */ else { label->dkl_cksum = sum; return (0); } } /* * This routine is used to extract the id string from the string stored * in a disk label. The problem is that the string in the label has * the physical characteristics of the drive appended to it. The approach * is to find the beginning of the physical attributes portion of the string * and truncate it there. */ int trim_id(char *id) { char *c; /* * Start at the end of the string. When we match the word ' cyl', * we are at the beginning of the attributes. */ for (c = id + strlen(id); c >= id; c--) { if (strncmp(c, " cyl", strlen(" cyl")) == 0) { /* * Remove any white space. */ for (; (((*(c - 1) == ' ') || (*(c - 1) == '\t')) && (c >= id)); c--) ; break; } } /* * If we ran off the beginning of the string, something is wrong. */ if (c < id) return (-1); /* * Truncate the string. */ *c = '\0'; return (0); } /* * This routine is used by write_label() to do a quick sanity check on the * supplied geometry. This is not a thorough check. * * The SCSI READ_CAPACITY command is used here to get the capacity of the * disk. But, the available area to store data on a disk is usually less * than this. So, if the specified geometry evaluates to a value which falls * in this margin, then such illegal geometries can slip through the cracks. */ static int do_geometry_sanity_check(void) { struct scsi_capacity_16 capacity; if (uscsi_read_capacity(cur_file, &capacity)) { err_print("Warning: Unable to get capacity." " Cannot check geometry\n"); return (0); /* Just ignore this problem */ } if (capacity.sc_capacity < ncyl * nhead * nsect) { err_print("\nWarning: Current geometry overshoots " "actual geometry of disk\n\n"); if (check("Continue labelling disk") != 0) return (-1); return (0); /* Just ignore this problem */ } return (0); } /* * create a clear EFI partition table when format is used * to convert an SMI label to an EFI label */ int SMI_vtoc_to_EFI(int fd, struct dk_gpt **new_vtoc) { int i; struct dk_gpt *efi; uint64_t reserved; if (efi_alloc_and_init(fd, EFI_NUMPAR, new_vtoc) != 0) { err_print("SMI vtoc to EFI failed\n"); return (-1); } efi = *new_vtoc; reserved = efi_reserved_sectors(efi); /* * create a clear EFI partition table: * s0 takes the whole disk except the primary EFI lable, * backup EFI labels, and the reserved partition. * s1-s6 are unassigned slices. */ efi->efi_parts[0].p_tag = V_USR; efi->efi_parts[0].p_start = efi->efi_first_u_lba; efi->efi_parts[0].p_size = efi->efi_last_u_lba - efi->efi_first_u_lba - reserved + 1; /* * s1-s6 are unassigned slices */ for (i = 1; i < efi->efi_nparts - 2; i++) { efi->efi_parts[i].p_tag = V_UNASSIGNED; efi->efi_parts[i].p_start = 0; efi->efi_parts[i].p_size = 0; } /* * the reserved slice */ efi->efi_parts[efi->efi_nparts - 1].p_tag = V_RESERVED; efi->efi_parts[efi->efi_nparts - 1].p_start = efi->efi_last_u_lba - reserved + 1; efi->efi_parts[efi->efi_nparts - 1].p_size = reserved; return (0); } /* * This routine constructs and writes a label on the disk. It writes both * the primary and backup labels. It assumes that there is a current * partition map already defined. It also notifies the SunOS kernel of * the label and partition information it has written on the disk. */ int write_label(void) { int error = 0, head, sec; struct dk_label label; struct extvtoc vtoc; struct dk_geom geom; struct dk_gpt *vtoc64; int nbackups; char *new_label; #if defined(_SUNOS_VTOC_8) int i; #endif /* defined(_SUNOS_VTOC_8) */ /* * Check to see if any partitions used for svm, vxvm or live upgrade * are on the disk. If so, refuse to label the disk, but only * if we are trying to shrink a partition in use. */ if (checkdevinuse(cur_disk->disk_name, (diskaddr_t)-1, (diskaddr_t)-1, 0, 1)) { err_print("Cannot label disk when " "partitions are in use as described.\n"); return (-1); } /* * If EFI label, then write it out to disk */ if (cur_label == L_TYPE_EFI) { enter_critical(); vtoc64 = cur_parts->etoc; efi_err_check(vtoc64); if (efi_write(cur_file, vtoc64) != 0) { err_print("Warning: error writing EFI.\n"); error = -1; } cur_disk->disk_flags |= DSK_LABEL; exit_critical(); return (error); } /* * Fill in a label structure with the geometry information. */ (void) memset((char *)&label, 0, sizeof (struct dk_label)); new_label = zalloc(cur_blksz); label.dkl_pcyl = pcyl; label.dkl_ncyl = ncyl; label.dkl_acyl = acyl; #if defined(_SUNOS_VTOC_16) label.dkl_bcyl = bcyl; #endif /* defined(_SUNOC_VTOC_16) */ label.dkl_nhead = nhead; label.dkl_nsect = nsect; label.dkl_apc = apc; label.dkl_intrlv = 1; label.dkl_rpm = cur_dtype->dtype_rpm; #if defined(_SUNOS_VTOC_8) /* * Also fill in the current partition information. */ for (i = 0; i < NDKMAP; i++) { label.dkl_map[i] = cur_parts->pinfo_map[i]; } #endif /* defined(_SUNOS_VTOC_8) */ label.dkl_magic = DKL_MAGIC; /* * Fill in the vtoc information */ label.dkl_vtoc = cur_parts->vtoc; /* * Use the current label */ bcopy(cur_disk->v_volume, label.dkl_vtoc.v_volume, LEN_DKL_VVOL); /* * Put asciilabel in; on x86 it's in the vtoc, not the label. */ (void) snprintf(label.dkl_asciilabel, sizeof (label.dkl_asciilabel), "%s cyl %d alt %d hd %d sec %d", cur_dtype->dtype_asciilabel, ncyl, acyl, nhead, nsect); #if defined(_SUNOS_VTOC_16) /* * Also add in v_sectorsz, as the driver will. */ label.dkl_vtoc.v_sectorsz = cur_blksz; #endif /* defined(_SUNOS_VTOC_16) */ /* * Generate the correct checksum. */ (void) checksum(&label, CK_MAKESUM); /* * Convert the label into a vtoc */ if (label_to_vtoc(&vtoc, &label) == -1) { free(new_label); return (-1); } /* * Fill in the geometry info. This is critical that * we do this before writing the vtoc. */ bzero((caddr_t)&geom, sizeof (struct dk_geom)); geom.dkg_ncyl = ncyl; geom.dkg_acyl = acyl; #if defined(_SUNOS_VTOC_16) geom.dkg_bcyl = bcyl; #endif /* defined(_SUNOS_VTOC_16) */ geom.dkg_nhead = nhead; geom.dkg_nsect = nsect; geom.dkg_intrlv = 1; geom.dkg_apc = apc; geom.dkg_rpm = cur_dtype->dtype_rpm; geom.dkg_pcyl = pcyl; /* * Make a quick check to see that the geometry is being * written now is not way off from the actual capacity * of the disk. This is only an appoximate check and * is only for SCSI disks. */ if (SCSI && do_geometry_sanity_check() != 0) { free(new_label); return (-1); } /* * Lock out interrupts so we do things in sync. */ enter_critical(); /* * Do the ioctl to tell the kernel the geometry. */ if (ioctl(cur_file, DKIOCSGEOM, &geom) == -1) { err_print("Warning: error setting drive geometry.\n"); error = -1; } /* * Write the vtoc. At the time of this writing, our * drivers convert the vtoc back to a label, and * then write both the primary and backup labels. * This is not a requirement, however, as we * always use an ioctl to read the vtoc from the * driver, so it can do as it likes. */ if (write_extvtoc(cur_file, &vtoc) != 0) { err_print("Warning: error writing VTOC.\n"); error = -1; } /* * Calculate where the backup labels went. They are always on * the last alternate cylinder, but some older drives put them * on head 2 instead of the last head. They are always on the * first 5 odd sectors of the appropriate track. */ if (cur_ctype->ctype_flags & CF_BLABEL) head = 2; else head = nhead - 1; /* * Read and verify the backup labels. */ nbackups = 0; for (sec = 1; ((sec < BAD_LISTCNT * 2 + 1) && (sec < nsect)); sec += 2) { if ((*cur_ops->op_rdwr)(DIR_READ, cur_file, (diskaddr_t) ((chs2bn(ncyl + acyl - 1, head, sec)) + solaris_offset), 1, new_label, F_NORMAL, NULL)) { err_print("Warning: error reading" "backup label.\n"); error = -1; } else { if (bcmp((char *)&label, new_label, sizeof (struct dk_label)) == 0) { nbackups++; } } } if (nbackups != BAD_LISTCNT) { err_print("Warning: %s\n", nbackups == 0 ? "no backup labels" : "some backup labels incorrect"); } /* * Mark the current disk as labelled and notify the kernel of what * has happened. */ cur_disk->disk_flags |= DSK_LABEL; exit_critical(); free(new_label); return (error); } /* * Read the label from the disk. * Do this via the read_extvtoc() library routine, then convert it to a label. * We also need a DKIOCGGEOM ioctl to get the disk's geometry. */ int read_label(int fd, struct dk_label *label) { struct extvtoc vtoc; struct dk_geom geom; struct dk_cinfo dkinfo; if (read_extvtoc(fd, &vtoc) < 0 || ioctl(fd, DKIOCGGEOM, &geom) == -1 || ioctl(fd, DKIOCINFO, &dkinfo) == -1) { return (-1); } return (vtoc_to_label(label, &vtoc, &geom, &dkinfo)); } int get_disk_inquiry_prop(char *devpath, char **vid, char **pid, char **rid) { char *v, *p, *r; di_node_t node; int ret = -1; node = di_init(devpath, DINFOCPYALL); if (node == DI_NODE_NIL) goto out; if (di_prop_lookup_strings(DDI_DEV_T_ANY, node, "inquiry-vendor-id", &v) != 1) goto out; if (di_prop_lookup_strings(DDI_DEV_T_ANY, node, "inquiry-product-id", &p) != 1) goto out; if (di_prop_lookup_strings(DDI_DEV_T_ANY, node, "inquiry-revision-id", &r) != 1) goto out; *vid = strdup(v); *pid = strdup(p); *rid = strdup(r); if (*vid == NULL || *pid == NULL || *rid == NULL) { free(*vid); free(*pid); free(*rid); goto out; } ret = 0; out: di_fini(node); return (ret); } int get_disk_inquiry_uscsi(int fd, char **vid, char **pid, char **rid) { struct scsi_inquiry inquiry; if (uscsi_inquiry(fd, (char *)&inquiry, sizeof (inquiry))) return (-1); *vid = strndup(inquiry.inq_vid, 8); *pid = strndup(inquiry.inq_pid, 16); *rid = strndup(inquiry.inq_revision, 4); if (*vid == NULL || *pid == NULL || *rid == NULL) { free(*vid); free(*pid); free(*rid); return (-1); } return (0); } int get_disk_capacity(int fd, uint64_t *capacity) { struct dk_minfo minf; struct scsi_capacity_16 cap16; if (ioctl(fd, DKIOCGMEDIAINFO, &minf) == 0) { *capacity = minf.dki_capacity * minf.dki_lbsize / cur_blksz; return (0); } if (uscsi_read_capacity(fd, &cap16) == 0) { *capacity = cap16.sc_capacity; /* Since we are counting from zero, add 1 to capacity */ (*capacity)++; return (0); } err_print("Fetch Capacity failed\n"); return (-1); } int get_disk_inquiry_devid(int fd, char **vid, char **pid, char **rid) { ddi_devid_t devid; char *s; char *v, *p; struct dk_cinfo dkinfo; if (devid_get(fd, &devid)) { if (option_msg && diag_msg) err_print("devid_get failed\n"); return (-1); } s = (char *)devid; if (ioctl(fd, DKIOCINFO, &dkinfo) == -1) { if (option_msg && diag_msg) err_print("DKIOCINFO failed\n"); return (-1); } if (dkinfo.dki_ctype != DKC_DIRECT) return (-1); v = s+12; if (!(p = strchr(v, '='))) return (-1); p += 1; *vid = strdup(v); *pid = strdup(p); *rid = strdup("0001"); devid_free(devid); if (*vid == NULL || *pid == NULL || *rid == NULL) { free(*vid); free(*pid); free(*rid); return (-1); } return (0); } /* * Issue uscsi_inquiry and read_capacity commands to * retrieve the disk's Vendor, Product, Revision and * Capacity information. */ int get_disk_info(int fd, struct efi_info *label, struct disk_info *disk_info) { (void) get_disk_capacity(fd, &label->capacity); if (get_disk_inquiry_prop(disk_info->devfs_name, &label->vendor, &label->product, &label->revision) != 0) { if (get_disk_inquiry_devid(fd, &label->vendor, &label->product, &label->revision) != 0) { if (get_disk_inquiry_uscsi(fd, &label->vendor, &label->product, &label->revision) != 0) { label->vendor = strdup("Unknown"); label->product = strdup("Unknown"); label->revision = strdup("0001"); if (label->vendor == NULL || label->product == NULL || label->revision == NULL) { free(label->vendor); free(label->product); free(label->revision); return (-1); } } } } return (0); } int read_efi_label(int fd, struct efi_info *label, struct disk_info *disk_info) { struct dk_gpt *vtoc64; /* This could fail if there is no label already */ if (efi_alloc_and_read(fd, &vtoc64) < 0) { return (-1); } if (vtoc64_to_label(label, vtoc64) != 0) { err_print("vtoc64_to_label failed\n"); return (-1); } efi_free(vtoc64); if (get_disk_info(fd, label, disk_info) != 0) { return (-1); } return (0); } /* * We've read a 64-bit label which has no geometry information. Use * some heuristics to fake up a geometry that would match the disk in * order to make the rest of format(8) happy. */ static int vtoc64_to_label(struct efi_info *label, struct dk_gpt *vtoc) { int i, nparts = 0; struct dk_gpt *lmap; (void) memset((char *)label, 0, sizeof (struct efi_info)); /* XXX do a sanity check here for nparts */ nparts = vtoc->efi_nparts; lmap = (struct dk_gpt *) calloc(1, (sizeof (struct dk_part) * nparts) + sizeof (struct dk_gpt)); if (lmap == NULL) { err_print("vtoc64_to_label: unable to allocate lmap\n"); fullabort(); } label->e_parts = lmap; /* * Copy necessary portions * XXX Maybe we can use memcpy() ?? */ lmap->efi_version = vtoc->efi_version; lmap->efi_nparts = vtoc->efi_nparts; lmap->efi_part_size = vtoc->efi_part_size; lmap->efi_lbasize = vtoc->efi_lbasize; lmap->efi_last_lba = vtoc->efi_last_lba; lmap->efi_first_u_lba = vtoc->efi_first_u_lba; lmap->efi_last_u_lba = vtoc->efi_last_u_lba; lmap->efi_altern_lba = vtoc->efi_altern_lba; lmap->efi_flags = vtoc->efi_flags; (void) memcpy((uchar_t *)&lmap->efi_disk_uguid, (uchar_t *)&vtoc->efi_disk_uguid, sizeof (struct uuid)); for (i = 0; i < nparts; i++) { lmap->efi_parts[i].p_tag = vtoc->efi_parts[i].p_tag; lmap->efi_parts[i].p_flag = vtoc->efi_parts[i].p_flag; lmap->efi_parts[i].p_start = vtoc->efi_parts[i].p_start; lmap->efi_parts[i].p_size = vtoc->efi_parts[i].p_size; (void) memcpy((uchar_t *)&lmap->efi_parts[i].p_uguid, (uchar_t *)&vtoc->efi_parts[i].p_uguid, sizeof (struct uuid)); if (vtoc->efi_parts[i].p_tag == V_RESERVED) { bcopy(vtoc->efi_parts[i].p_name, lmap->efi_parts[i].p_name, LEN_DKL_VVOL); } } return (0); } /* * Convert vtoc/geom to label. */ static int vtoc_to_label(struct dk_label *label, struct extvtoc *vtoc, struct dk_geom *geom, struct dk_cinfo *cinfo) { #if defined(_SUNOS_VTOC_8) struct dk_map32 *lmap; #elif defined(_SUNOS_VTOC_16) struct dkl_partition *lmap; #else #error No VTOC format defined. #endif /* defined(_SUNOS_VTOC_8) */ struct extpartition *vpart; ulong_t nblks; int i; (void) memset((char *)label, 0, sizeof (struct dk_label)); /* * Sanity-check the vtoc */ if (vtoc->v_sanity != VTOC_SANE || vtoc->v_nparts != V_NUMPAR) { return (-1); } /* * Sanity check of geometry */ if (geom->dkg_ncyl == 0 || geom->dkg_nhead == 0 || geom->dkg_nsect == 0) { return (-1); } label->dkl_magic = DKL_MAGIC; /* * Copy necessary portions of the geometry information */ label->dkl_rpm = geom->dkg_rpm; label->dkl_pcyl = geom->dkg_pcyl; label->dkl_apc = geom->dkg_apc; label->dkl_intrlv = geom->dkg_intrlv; label->dkl_ncyl = geom->dkg_ncyl; label->dkl_acyl = geom->dkg_acyl; #if defined(_SUNOS_VTOC_16) label->dkl_bcyl = geom->dkg_bcyl; #endif /* defined(_SUNOS_VTOC_16) */ label->dkl_nhead = geom->dkg_nhead; label->dkl_nsect = geom->dkg_nsect; #if defined(_SUNOS_VTOC_8) label->dkl_obs1 = geom->dkg_obs1; label->dkl_obs2 = geom->dkg_obs2; label->dkl_obs3 = geom->dkg_obs3; #endif /* defined(_SUNOS_VTOC_8) */ label->dkl_write_reinstruct = geom->dkg_write_reinstruct; label->dkl_read_reinstruct = geom->dkg_read_reinstruct; /* * Copy vtoc structure fields into the disk label dk_vtoc */ label->dkl_vtoc.v_sanity = vtoc->v_sanity; label->dkl_vtoc.v_nparts = vtoc->v_nparts; label->dkl_vtoc.v_version = vtoc->v_version; (void) memcpy(label->dkl_vtoc.v_volume, vtoc->v_volume, LEN_DKL_VVOL); for (i = 0; i < V_NUMPAR; i++) { label->dkl_vtoc.v_part[i].p_tag = vtoc->v_part[i].p_tag; label->dkl_vtoc.v_part[i].p_flag = vtoc->v_part[i].p_flag; label->dkl_vtoc.v_timestamp[i] = vtoc->timestamp[i]; } for (i = 0; i < 10; i++) label->dkl_vtoc.v_reserved[i] = vtoc->v_reserved[i]; label->dkl_vtoc.v_bootinfo[0] = vtoc->v_bootinfo[0]; label->dkl_vtoc.v_bootinfo[1] = vtoc->v_bootinfo[1]; label->dkl_vtoc.v_bootinfo[2] = vtoc->v_bootinfo[2]; (void) memcpy(label->dkl_asciilabel, vtoc->v_asciilabel, LEN_DKL_ASCII); /* * Note the conversion from starting sector number * to starting cylinder number. * Return error if division results in a remainder. * * Note: don't check, if probing virtual disk in Xen * for that virtual disk will use fabricated # of headers * and sectors per track which may cause the capacity * not multiple of # of blocks per cylinder */ #if defined(_SUNOS_VTOC_8) lmap = label->dkl_map; #elif defined(_SUNOS_VTOC_16) lmap = label->dkl_vtoc.v_part; #else #error No VTOC format defined. #endif /* defined(_SUNOS_VTOC_8) */ vpart = vtoc->v_part; nblks = label->dkl_nsect * label->dkl_nhead; for (i = 0; i < NDKMAP; i++, lmap++, vpart++) { if (cinfo->dki_ctype != DKC_VBD) { if ((vpart->p_start % nblks) != 0 || (vpart->p_size % nblks) != 0) { return (-1); } } #if defined(_SUNOS_VTOC_8) lmap->dkl_cylno = (blkaddr32_t)(vpart->p_start / nblks); lmap->dkl_nblk = (blkaddr32_t)vpart->p_size; #elif defined(_SUNOS_VTOC_16) lmap->p_start = (blkaddr32_t)vpart->p_start; lmap->p_size = (blkaddr32_t)vpart->p_size; #else #error No VTOC format defined. #endif /* defined(_SUNOS_VTOC_8) */ } /* * Finally, make a checksum */ (void) checksum(label, CK_MAKESUM); #ifdef DEBUG if (option_msg && diag_msg) dump_label(label); #endif return (0); } /* * Extract a vtoc structure out of a valid label */ int label_to_vtoc(struct extvtoc *vtoc, struct dk_label *label) { #if defined(_SUNOS_VTOC_8) struct dk_map2 *lpart; struct dk_map32 *lmap; ulong_t nblks; #elif defined(_SUNOS_VTOC_16) struct dkl_partition *lpart; #else #error No VTOC format defined. #endif /* defined(_SUNOS_VTOC_8) */ struct extpartition *vpart; int i; (void) memset((char *)vtoc, 0, sizeof (struct extvtoc)); switch (label->dkl_vtoc.v_version) { case 0: /* * No valid vtoc information in the label. * Construct default p_flags and p_tags. */ vpart = vtoc->v_part; for (i = 0; i < V_NUMPAR; i++, vpart++) { vpart->p_tag = default_vtoc_map[i].p_tag; vpart->p_flag = default_vtoc_map[i].p_flag; } break; case V_VERSION: vpart = vtoc->v_part; lpart = label->dkl_vtoc.v_part; for (i = 0; i < V_NUMPAR; i++, vpart++, lpart++) { vpart->p_tag = lpart->p_tag; vpart->p_flag = lpart->p_flag; #if defined(_SUNOS_VTOC_16) vpart->p_start = (diskaddr_t)lpart->p_start; vpart->p_size = (diskaddr_t)lpart->p_size; #endif /* defined(_SUNOS_VTOC_16) */ vtoc->timestamp[i] = label->dkl_vtoc.v_timestamp[i]; } (void) memcpy(vtoc->v_volume, label->dkl_vtoc.v_volume, LEN_DKL_VVOL); for (i = 0; i < 10; i++) vtoc->v_reserved[i] = label->dkl_vtoc.v_reserved[i]; vtoc->v_bootinfo[0] = label->dkl_vtoc.v_bootinfo[0]; vtoc->v_bootinfo[1] = label->dkl_vtoc.v_bootinfo[1]; vtoc->v_bootinfo[2] = label->dkl_vtoc.v_bootinfo[2]; break; default: return (-1); } /* * XXX - this looks wrong to me.... * why are these values hardwired, rather than returned from * the real disk label? */ vtoc->v_sanity = VTOC_SANE; vtoc->v_version = V_VERSION; vtoc->v_sectorsz = cur_blksz; vtoc->v_nparts = V_NUMPAR; (void) memcpy(vtoc->v_asciilabel, label->dkl_asciilabel, LEN_DKL_ASCII); #if defined(_SUNOS_VTOC_8) /* * Convert partitioning information. * Note the conversion from starting cylinder number * to starting sector number. */ lmap = label->dkl_map; vpart = vtoc->v_part; nblks = label->dkl_nsect * label->dkl_nhead; for (i = 0; i < V_NUMPAR; i++, vpart++, lmap++) { vpart->p_start = (diskaddr_t)(lmap->dkl_cylno * nblks); vpart->p_size = (diskaddr_t)lmap->dkl_nblk; } #endif /* defined(_SUNOS_VTOC_8) */ return (0); } /* * Input: File descriptor * Output: 1 if disk has an EFI label, 0 otherwise. */ int is_efi_type(int fd) { struct extvtoc vtoc; if (read_extvtoc(fd, &vtoc) == VT_ENOTSUP) { /* assume the disk has EFI label */ return (1); } return (0); } #ifdef DEBUG static void dump_label(struct dk_label *label) { int i; fmt_print("%s\n", label->dkl_asciilabel); fmt_print("version: %d\n", label->dkl_vtoc.v_version); fmt_print("volume: "); for (i = 0; i < LEN_DKL_VVOL; i++) { if (label->dkl_vtoc.v_volume[i] == 0) break; fmt_print("%c", label->dkl_vtoc.v_volume[i]); } fmt_print("\n"); fmt_print("v_nparts: %d\n", label->dkl_vtoc.v_nparts); fmt_print("v_sanity: %lx\n", label->dkl_vtoc.v_sanity); #if defined(_SUNOS_VTOC_8) fmt_print("rpm: %d\n", label->dkl_rpm); fmt_print("pcyl: %d\n", label->dkl_pcyl); fmt_print("apc: %d\n", label->dkl_apc); fmt_print("obs1: %d\n", label->dkl_obs1); fmt_print("obs2: %d\n", label->dkl_obs2); fmt_print("intrlv: %d\n", label->dkl_intrlv); fmt_print("ncyl: %d\n", label->dkl_ncyl); fmt_print("acyl: %d\n", label->dkl_acyl); fmt_print("nhead: %d\n", label->dkl_nhead); fmt_print("nsect: %d\n", label->dkl_nsect); fmt_print("obs3: %d\n", label->dkl_obs3); fmt_print("obs4: %d\n", label->dkl_obs4); #elif defined(_SUNOS_VTOC_16) fmt_print("rpm: %d\n", label->dkl_rpm); fmt_print("pcyl: %d\n", label->dkl_pcyl); fmt_print("apc: %d\n", label->dkl_apc); fmt_print("intrlv: %d\n", label->dkl_intrlv); fmt_print("ncyl: %d\n", label->dkl_ncyl); fmt_print("acyl: %d\n", label->dkl_acyl); fmt_print("nhead: %d\n", label->dkl_nhead); fmt_print("nsect: %d\n", label->dkl_nsect); fmt_print("bcyl: %d\n", label->dkl_bcyl); fmt_print("skew: %d\n", label->dkl_skew); #else #error No VTOC format defined. #endif /* defined(_SUNOS_VTOC_8) */ fmt_print("magic: %0x\n", label->dkl_magic); fmt_print("cksum: %0x\n", label->dkl_cksum); for (i = 0; i < NDKMAP; i++) { #if defined(_SUNOS_VTOC_8) fmt_print("%c: cyl=%d, blocks=%d", i+'a', label->dkl_map[i].dkl_cylno, label->dkl_map[i].dkl_nblk); #elif defined(_SUNOS_VTOC_16) fmt_print("%c: start=%u, blocks=%u", i+'a', label->dkl_vtoc.v_part[i].p_start, label->dkl_vtoc.v_part[i].p_size); #else #error No VTOC format defined. #endif /* defined(_SUNOS_VTOC_8) */ fmt_print(", tag=%d, flag=%d", label->dkl_vtoc.v_part[i].p_tag, label->dkl_vtoc.v_part[i].p_flag); fmt_print("\n"); } fmt_print("read_reinstruct: %d\n", label->dkl_read_reinstruct); fmt_print("write_reinstruct: %d\n", label->dkl_write_reinstruct); fmt_print("bootinfo: "); for (i = 0; i < 3; i++) { fmt_print("0x%x ", label->dkl_vtoc.v_bootinfo[i]); } fmt_print("\n"); fmt_print("reserved: "); for (i = 0; i < 10; i++) { if ((i % 4) == 3) fmt_print("\n"); fmt_print("0x%x ", label->dkl_vtoc.v_reserved[i]); } fmt_print("\n"); fmt_print("timestamp:\n"); for (i = 0; i < NDKMAP; i++) { if ((i % 4) == 3) fmt_print("\n"); fmt_print("0x%x ", label->dkl_vtoc.v_timestamp[i]); } fmt_print("\n"); fmt_print("pad:\n"); dump("", label->dkl_pad, LEN_DKL_PAD, HEX_ONLY); fmt_print("\n\n"); } #endif /* DEBUG */ /* * 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 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * Copyright 2015 Nexenta Systems, Inc. All rights reserved. */ #ifndef _LABEL_H #define _LABEL_H #ifdef __cplusplus extern "C" { #endif /* * Prototypes for ANSI C compilers */ int checklabel(struct dk_label *); int checksum(struct dk_label *, int); int trim_id(char *); int write_label(void); int read_label(int, struct dk_label *); int read_efi_label(int, struct efi_info *, struct disk_info *); int get_disk_inquiry_prop(char *, char **, char **, char **); int get_disk_info(int, struct efi_info *, struct disk_info *); int label_to_vtoc(struct extvtoc *, struct dk_label *); int SMI_vtoc_to_EFI(int, struct dk_gpt **); void efi_err_check(struct dk_gpt *); extern int is_efi_type(int); #ifdef __cplusplus } #endif #endif /* _LABEL_H */ /* * 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. * Copyright (c) 2016 by Delphix. All rights reserved. */ /* * This file contains the main entry point of the program and other * routines relating to the general flow. */ #include "global.h" #include #include #include #include #include #include #include #ifdef sparc #include #include #endif #include #include "main.h" #include "analyze.h" #include "menu.h" #include "param.h" #include "misc.h" #include "startup.h" #include "menu_command.h" #include "menu_partition.h" #include "prompts.h" #include "checkdev.h" #include "label.h" extern struct menu_item menu_command[]; uint_t apc; uint_t solaris_offset; char cur_menu; char last_menu; void *pattern_buf; FILE *log_file; void *cur_buf; struct disk_info *cur_disk; struct ctlr_ops *cur_ops; struct ctlr_info *cur_ctlr; struct ctlr_type *cur_ctype; struct disk_type *cur_dtype; struct partition_info *cur_parts; struct defect_list cur_list; int cur_file; int cur_flags; int cur_label; uint_t pcyl; uint_t ncyl; uint_t acyl; uint_t bcyl; uint_t nhead; uint_t phead; uint_t nsect; uint_t psect; #ifdef __STDC__ /* * Local prototypes for ANSI C compilers */ static void get_disk_characteristics(void); #else /* __STDC__ */ /* * Local prototypes for non-ANSI C compilers */ static void get_disk_characteristics(); #endif /* __STDC__ */ /* * This is the main entry point. */ int main(int argc, char *argv[]) { int i; int ret_code = 1; char **arglist; struct disk_info *disk = NULL; struct disk_type *type, *oldtype; struct partition_info *parts; struct sigaction act; solaris_offset = 0; /* * Initialize cur_ctype to avoid null pointer dereference * in auto_efi_sense(). */ cur_ctype = NULL; /* * Decode the command line options. */ i = do_options(argc, argv); /* * If we are to run from a command file, open it up. */ if (option_f) { if (freopen(option_f, "r", stdin) == NULL) { err_print("Unable to open command file '%s'.\n", option_f); fullabort(); } } /* * If we are logging, open the log file. */ if (option_l) { if ((log_file = fopen(option_l, "w")) == NULL) { err_print("Unable to open log file '%s'.\n", option_l); fullabort(); } } /* * Read in the data file and initialize the hardware structs. */ sup_init(); /* * If there are no disks on the command line, search the * appropriate device directory for character devices that * look like disks. */ if (i < 0) { arglist = NULL; /* * There were disks on the command line. They comprise the * search list. */ } else { arglist = &argv[i]; } /* * Perform the search for disks. */ do_search(arglist); /* * Catch ctrl-C and ctrl-Z so critical sections can be * implemented. We use sigaction, as this sets up the * signal handler permanently, and also automatically * restarts any interrupted system call. */ act.sa_handler = cmdabort; (void) memset(&act.sa_mask, 0, sizeof (sigset_t)); act.sa_flags = SA_RESTART | SA_NODEFER; if (sigaction(SIGINT, &act, NULL) == -1) { err_print("sigaction(SIGINT) failed - %s\n", strerror(errno)); fullabort(); } act.sa_handler = onsusp; (void) memset(&act.sa_mask, 0, sizeof (sigset_t)); act.sa_flags = SA_RESTART | SA_NODEFER; if (sigaction(SIGTSTP, &act, NULL) == -1) { err_print("sigaction(SIGTSTP) failed - %s\n", strerror(errno)); fullabort(); } act.sa_handler = onalarm; (void) memset(&act.sa_mask, 0, sizeof (sigset_t)); act.sa_flags = SA_RESTART; if (sigaction(SIGALRM, &act, NULL) == -1) { err_print("sigaction(SIGALRM) failed - %s\n", strerror(errno)); fullabort(); } /* * If there was only 1 disk on the command line, mark it * to be the current disk. If it wasn't found, it's an error. */ if (i == argc - 1) { disk = disk_list; if (disk == NULL) { err_print("Unable to find specified disk '%s'.\n", argv[i]); fullabort(); } } /* * A disk was forced on the command line. */ if (option_d) { /* * Find it in the list of found disks and mark it to * be the current disk. */ for (disk = disk_list; disk != NULL; disk = disk->disk_next) if (diskname_match(option_d, disk)) break; /* * If it wasn't found, it's an error. */ if (disk == NULL) { err_print("Unable to find specified disk '%s'.\n", option_d); fullabort(); } } /* * A disk type was forced on the command line. */ if (option_t != NULL) { /* * Only legal if a disk was also forced. */ if (disk == NULL) { err_print("Must specify disk as well as type.\n"); fullabort(); } oldtype = disk->disk_type; /* * Find the specified type in the list of legal types * for the disk. */ for (type = disk->disk_ctlr->ctlr_ctype->ctype_dlist; type != NULL; type = type->dtype_next) if (strcmp(option_t, type->dtype_asciilabel) == 0) break; /* * If it wasn't found, it's an error. */ if (type == NULL) { err_print( "Specified type '%s' is not a known type.\n", option_t); fullabort(); } /* * If the specified type is not the same as the type * in the disk label, update the type and nullify the * partition map. */ if (type != oldtype) { disk->disk_type = type; disk->disk_parts = NULL; } } /* * A partition map was forced on the command line. */ if (option_p) { /* * Only legal if both disk and type were also forced. */ if (disk == NULL || disk->disk_type == NULL) { err_print("Must specify disk and type as well "); err_print("as partitiion.\n"); fullabort(); } /* * Find the specified map in the list of legal maps * for the type. */ for (parts = disk->disk_type->dtype_plist; parts != NULL; parts = parts->pinfo_next) if (strcmp(option_p, parts->pinfo_name) == 0) break; /* * If it wasn't found, it's an error. */ if (parts == NULL) { err_print( "Specified table '%s' is not a known table.\n", option_p); fullabort(); } /* * Update the map. */ disk->disk_parts = parts; } /* * If a disk was marked to become current, initialize the state * to make it current. If not, ask user to pick one. */ if (disk != NULL) { init_globals(disk); } else if (option_f == 0 && option_d == 0) { while (ret_code) { ret_code = c_disk(); } } #ifdef BUG1134748 /* * if -f command-file is specified, check for disk and disktype * input also. For SCSI disks, the type input may not be needed * since format would have figured that using inquiry information. */ if (option_f) { if (cur_disk == NULL) { err_print("Must specify a disk using -d option.\n"); fullabort(); } if (cur_dtype == NULL) { err_print("Must specify disk as well as type.\n"); fullabort(); } } #endif /* BUG1134748 */ /* * Run the command menu. */ cur_menu = last_menu = 0; run_menu(menu_command, "FORMAT", "format", 1); /* * normal ending. Explicitly return(0); */ return (0); } /* * This routine initializes the internal state to ready it for a new * current disk. There are a zillion state variables that store * information on the current disk, and they must all be updated. * We also tell SunOS about the disk, since it may not know if the * disk wasn't labeled at boot time. */ void init_globals(struct disk_info *disk) { int status; int found_mount; int found_inuse; #ifdef sparc int i; caddr_t bad_ptr = (caddr_t)&badmap; #endif /* * If there was an old current disk, close the file for it. */ if (cur_disk != NULL) (void) close(cur_file); /* * Kill off any defect lists still lying around. */ kill_deflist(&cur_list); kill_deflist(&work_list); /* * If there were any buffers, free them up. */ if ((char *)cur_buf != NULL) { destroy_data((char *)cur_buf); cur_buf = NULL; } if ((char *)pattern_buf != NULL) { destroy_data((char *)pattern_buf); pattern_buf = NULL; } /* * Fill in the hardware struct pointers for the new disk. */ cur_disk = disk; cur_dtype = cur_disk->disk_type; cur_label = cur_disk->label_type; cur_ctlr = cur_disk->disk_ctlr; cur_parts = cur_disk->disk_parts; cur_blksz = cur_disk->disk_lbasize; cur_ctype = cur_ctlr->ctlr_ctype; cur_ops = cur_ctype->ctype_ops; cur_flags = 0; /* * Open a file for the new disk. */ if ((cur_file = open_disk(cur_disk->disk_path, O_RDWR | O_NDELAY)) < 0) { err_print( "Error: can't open selected disk '%s'.\n", cur_disk->disk_name); fullabort(); } #ifdef sparc /* * If the new disk uses bad-144, initialize the bad block table. */ if (cur_ctlr->ctlr_flags & DKI_BAD144) { badmap.bt_mbz = badmap.bt_csn = badmap.bt_flag = 0; for (i = 0; i < NDKBAD; i++) { badmap.bt_bad[i].bt_cyl = -1; badmap.bt_bad[i].bt_trksec = -1; } } #endif /* * If the type of the new disk is known... */ if (cur_dtype != NULL) { /* * Initialize the physical characteristics. * If need disk specs, prompt for undefined disk * characteristics. If running from a file, * use defaults. */ if (cur_dtype->dtype_flags & DT_NEED_SPEFS) { get_disk_characteristics(); cur_dtype->dtype_flags &= ~DT_NEED_SPEFS; } ncyl = cur_dtype->dtype_ncyl; acyl = cur_dtype->dtype_acyl; pcyl = cur_dtype->dtype_pcyl; nhead = cur_dtype->dtype_nhead; nsect = cur_dtype->dtype_nsect; phead = cur_dtype->dtype_phead; psect = cur_dtype->dtype_psect; /* * Alternates per cylinder are forced to 0 or 1, * independent of what the label says. This works * because we know which ctlr we are dealing with. */ if (cur_ctype->ctype_flags & CF_APC) apc = 1; else apc = 0; /* * Initialize the surface analysis info. We always start * out with scan set for the whole disk. Note, * for SCSI disks, we can only scan the data area. */ scan_lower = 0; scan_size = BUF_SECTS; if ((cur_ctype->ctype_flags & CF_SCSI) && (cur_disk->label_type == L_TYPE_SOLARIS)) { scan_upper = datasects() - 1; } else if (cur_disk->label_type == L_TYPE_SOLARIS) { scan_upper = physsects() - 1; } else if (cur_disk->label_type == L_TYPE_EFI) { scan_upper = cur_parts->etoc->efi_last_lba; } /* * Allocate the buffers. */ cur_buf = (void *) zalloc(BUF_SECTS * cur_blksz); pattern_buf = (void *) zalloc(BUF_SECTS * cur_blksz); /* * Tell the user which disk they selected. */ if (chk_volname(cur_disk)) { fmt_print("selecting %s: ", cur_disk->disk_name); print_volname(cur_disk); fmt_print("\n"); } else { fmt_print("selecting %s\n", cur_disk->disk_name); } /* * If the drive is formatted... */ if ((*cur_ops->op_ck_format)()) { /* * Mark it formatted. */ cur_flags |= DISK_FORMATTED; /* * Read the defect list, if we have one. */ if (!EMBEDDED_SCSI) { read_list(&cur_list); } #ifdef sparc /* * If the disk does BAD-144, we do an ioctl to * tell SunOS about the bad block table. */ if (cur_ctlr->ctlr_flags & DKI_BAD144) { if (ioctl(cur_file, HDKIOCSBAD, &bad_ptr)) { err_print( "Warning: error telling SunOS bad block map table.\n"); } } #endif fmt_print("[disk formatted"); if (!EMBEDDED_SCSI) { if (cur_list.list != NULL) { fmt_print(", defect list found"); } else { fmt_print(", no defect list found"); } } fmt_print("]"); /* * Drive wasn't formatted. Tell the user in case they * disagree. */ } else if (EMBEDDED_SCSI) { fmt_print("[disk unformatted]"); } else { /* * Make sure the user is serious. Note, for * SCSI disks since this is instantaneous, we * will just do it and not ask for confirmation. */ status = 0; if (!(cur_ctype->ctype_flags & CF_CONFIRM)) { if (check("\n\ Ready to get manufacturer's defect list from unformatted drive.\n\ This cannot be interrupted and takes a long while.\n\ Continue")) status = 1; else fmt_print( "Extracting manufacturer's defect list..."); } /* * Extract manufacturer's defect list. */ if ((status == 0) && (cur_ops->op_ex_man != NULL)) { status = (*cur_ops->op_ex_man)(&cur_list); } else { status = 1; } fmt_print("[disk unformatted"); if (status != 0) { fmt_print(", no defect list found]"); } else { fmt_print(", defect list found]"); } } } else { /* * Disk type is not known. * Initialize physical characteristics to 0 and tell the * user we don't know what type the disk is. */ ncyl = acyl = nhead = nsect = psect = 0; } fmt_print("\n"); /* * Check to see if there are any mounted file systems on the * disk. If there are, print a warning. */ if ((found_mount = checkmount((diskaddr_t)-1, (diskaddr_t)-1)) != 0) err_print("Warning: Current Disk has mounted partitions.\n"); /* * If any part of this device is also part of an SVM, VxVM or * Live Upgrade device, print a warning. */ found_inuse = checkdevinuse(cur_disk->disk_name, (diskaddr_t)-1, (diskaddr_t)-1, 1, 0); /* * Get the Solaris Fdisk Partition information */ (void) copy_solaris_part(&cur_disk->fdisk_part); if (!found_mount && !found_inuse && cur_disk->label_type == L_TYPE_EFI) { /* * If alter_lba is 1, we are using the backup label. * Since we can locate the backup label by disk capacity, * there must be no space expanded after backup label. */ if ((cur_parts->etoc->efi_altern_lba != 1) && (cur_parts->etoc->efi_altern_lba < cur_parts->etoc->efi_last_lba)) { /* * Lun expansion detected. Prompt user now and actually * adjust the label in command. */ fmt_print( "Note: capacity in disk label is smaller than the real disk capacity.\n\ Select to adjust the label capacity. \n"); } } } /* * Prompt for some undefined disk characteristics. * Used when there is no disk definition, but the * disk has a valid label, so basically we're * prompting for everything that isn't in the label. */ static void get_disk_characteristics(void) { /* * The need_spefs flag is used to tell us that this disk * is not a known type and the ctlr specific info must * be prompted for. We only prompt for the info that applies * to this ctlr. */ assert(cur_dtype->dtype_flags & DT_NEED_SPEFS); /* * If we're running with input from a file, use * reasonable defaults, since prompting for the * information will probably mess things up. */ if (option_f) { cur_dtype->dtype_pcyl = ncyl + acyl; cur_dtype->dtype_rpm = AVG_RPM; cur_dtype->dtype_bpt = INFINITY; cur_dtype->dtype_phead = 0; cur_dtype->dtype_psect = 0; cur_dtype->dtype_cyl_skew = 0; cur_dtype->dtype_trk_skew = 0; cur_dtype->dtype_trks_zone = 0; cur_dtype->dtype_atrks = 0; cur_dtype->dtype_asect = 0; cur_dtype->dtype_cache = 0; cur_dtype->dtype_threshold = 0; cur_dtype->dtype_prefetch_min = 0; cur_dtype->dtype_prefetch_max = 0; if (cur_ctype->ctype_flags & CF_SMD_DEFS) { cur_dtype->dtype_bps = AVG_BPS; } } else { cur_dtype->dtype_pcyl = get_pcyl(ncyl, cur_dtype->dtype_acyl); cur_dtype->dtype_bpt = get_bpt(cur_dtype->dtype_nsect, &cur_dtype->dtype_options); cur_dtype->dtype_rpm = get_rpm(); cur_dtype->dtype_fmt_time = get_fmt_time(&cur_dtype->dtype_options); cur_dtype->dtype_cyl_skew = get_cyl_skew(&cur_dtype->dtype_options); cur_dtype->dtype_trk_skew = get_trk_skew(&cur_dtype->dtype_options); cur_dtype->dtype_trks_zone = get_trks_zone(&cur_dtype->dtype_options); cur_dtype->dtype_atrks = get_atrks(&cur_dtype->dtype_options); cur_dtype->dtype_asect = get_asect(&cur_dtype->dtype_options); cur_dtype->dtype_cache = get_cache(&cur_dtype->dtype_options); cur_dtype->dtype_threshold = get_threshold(&cur_dtype->dtype_options); cur_dtype->dtype_prefetch_min = get_min_prefetch(&cur_dtype->dtype_options); cur_dtype->dtype_prefetch_max = get_max_prefetch(cur_dtype->dtype_prefetch_min, &cur_dtype->dtype_options); cur_dtype->dtype_phead = get_phead(nhead, &cur_dtype->dtype_options); cur_dtype->dtype_psect = get_psect(&cur_dtype->dtype_options); cur_dtype->dtype_bps = get_bps(); #ifdef sparc cur_dtype->dtype_dr_type = 0; #endif } } /* * 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. */ #ifndef _MAIN_H #define _MAIN_H #ifdef __cplusplus extern "C" { #endif /* * Prototypes for ANSI C compilers */ int main(int argc, char *argv[]); int notify_unix(void); void init_globals(struct disk_info *disk); #ifdef __cplusplus } #endif #endif /* _MAIN_H */ /* * 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 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * This file contains routines relating to running the menus. */ #include #include "global.h" #include "menu.h" #include "misc.h" static int (*find_enabled_menu_item(struct menu_item *, int))(void); static char cur_title[MAXPATHLEN]; /* * This routine takes a menu struct and concatenates the * command names into an array of strings describing the menu. * All menus have a 'quit' command at the bottom to exit the menu. */ char ** create_menu_list(struct menu_item *menu) { struct menu_item *mptr; char **cpptr; char **list; int nitems; /* * A minimum list consists of the quit command, followed * by a terminating null. */ nitems = 2; /* * Count the number of active commands in the menu and allocate * space for the array of pointers. */ for (mptr = menu; mptr->menu_cmd != NULL; mptr++) { if ((*mptr->menu_state)()) nitems++; } list = (char **)zalloc(nitems * sizeof (char *)); cpptr = list; /* * Fill in the array with the names of the menu commands. */ for (mptr = menu; mptr->menu_cmd != NULL; mptr++) { if ((*mptr->menu_state)()) { *cpptr++ = mptr->menu_cmd; } } /* * Add the 'quit' command to the end. */ *cpptr = "quit"; return (list); } /* * This routine takes a menu list created by the above routine and * prints it nicely on the screen. */ void display_menu_list(char **list) { char **str; for (str = list; *str != NULL; str++) fmt_print(" %s\n", *str); } /* * Find the "i"th enabled menu in a menu list. This depends * on menu_state() returning the same status as when the * original list of enabled commands was constructed. */ static int (* find_enabled_menu_item(struct menu_item *menu, int item))(void) { struct menu_item *mp; for (mp = menu; mp->menu_cmd != NULL; mp++) { if ((*mp->menu_state)()) { if (item-- == 0) { return (mp->menu_func); } } } return (NULL); } /* * This routine 'runs' a menu. It repeatedly requests a command and * executes the command chosen. It exits when the 'quit' command is * executed. */ /*ARGSUSED*/ void run_menu(struct menu_item *menu, char *title, char *prompt, int display_flag) { char **list; int i; struct env env; u_ioparam_t ioparam; int (*f)(); /* * Create the menu list and display it. */ list = create_menu_list(menu); (void) strcpy(cur_title, title); fmt_print("\n\n%s MENU:\n", title); display_menu_list(list); /* * Save the environment so a ctrl-C out of a command lands here. */ saveenv(env); for (;;) { /* * Ask the user which command they want to run. */ ioparam.io_charlist = list; i = input(FIO_MSTR, prompt, '>', &ioparam, NULL, CMD_INPUT); /* * If they choose 'quit', the party's over. */ if ((f = find_enabled_menu_item(menu, i)) == NULL) break; /* * Mark the saved environment active so the user can now * do a ctrl-C to get out of the command. */ useenv(); /* * Run the command. If it returns an error and we are * running out of a command file, the party's really over. */ if ((*f)() && option_f) fullabort(); /* * Mark the saved environment inactive so ctrl-C doesn't * work at the menu itself. */ unuseenv(); /* * Since menu items are dynamic, some commands * cause changes to occur. Destroy the old menu, * and rebuild it, so we're always up-to-date. */ destroy_data((char *)list); list = create_menu_list(menu); /* * Redisplay menu, if we're returning to this one. */ if (cur_menu != last_menu) { last_menu = cur_menu; (void) strcpy(cur_title, title); fmt_print("\n\n%s MENU:\n", title); display_menu_list(list); } } /* * Clean up the environment stack and throw away the menu list. */ clearenv(); destroy_data((char *)list); } /* * re-display the screen after exiting from shell escape * */ void redisplay_menu_list(char **list) { fmt_print("\n\n%s MENU:\n", cur_title); display_menu_list(list); } /* * Glue to always return true. Used for menu items which * are always enabled. */ int true(void) { return (1); } /* * Note: The following functions are used to enable the inclusion * of device specific options (see init_menus.c). But when we are * running non interactively with commands taken from a script file, * current disk (cur_disk, cur_type, cur_ctype) may not be defined. * They get defined when the script selects a disk using "disk" option * in the main menu. However, in the normal interactive mode, the disk * selection happens before entering the main menu. */ /* * Return true for menu items enabled only for embedded SCSI controllers */ int embedded_scsi(void) { if (cur_ctype == NULL && option_f) return (0); return (EMBEDDED_SCSI); } /* * Return false for menu items disabled only for embedded SCSI controllers */ int not_embedded_scsi(void) { if (cur_ctype == NULL && option_f) return (0); return (!EMBEDDED_SCSI); } /* * Return false for menu items disabled for scsi controllers */ int not_scsi(void) { if (cur_ctype == NULL && option_f) return (0); return (!SCSI); } /* * Return false for menu items disabled for efi labels */ int not_efi(void) { if ((cur_disk == NULL) && option_f) return (0); if (cur_disk->label_type == L_TYPE_EFI) return (0); return (1); } int disp_expert_change_expert_efi(void) { if ((cur_disk == NULL) && option_f) return (0); if ((cur_disk->label_type == L_TYPE_EFI) && expert_mode) return (1); if (cur_disk->label_type != L_TYPE_EFI) return (1); return (0); } int disp_expand_efi(void) { if ((cur_disk == NULL) && option_f) return (0); if (cur_disk->label_type != L_TYPE_EFI) return (0); if (cur_parts == NULL) return (0); return (1); } int disp_all_change_expert_efi(void) { if ((cur_disk == NULL) && option_f) return (0); if ((cur_disk->label_type != L_TYPE_EFI) || (!expert_mode)) return (0); return (1); } /* * Return true for menu items enabled scsi controllers */ int scsi(void) { if (cur_ctype == NULL && option_f) return (0); return (SCSI); } /* * Return true for menu items enabled if expert mode is enabled */ int scsi_expert(void) { if (cur_ctype == NULL && option_f) return (0); return (SCSI && expert_mode); } #if defined(i386) /* * Return true for menu items enabled if expert mode is enabled */ int expert(void) { return (expert_mode); } #endif /* defined(i386) */ /* * Return true for menu items enabled if developer mode is enabled */ int developer(void) { return (dev_expert); } /* * For x86, always return true for menu items enabled * since fdisk is already supported on these two platforms. * For Sparc, only return true for menu items enabled * if a PCATA disk is selected. */ int support_fdisk_on_sparc(void) { #if defined(sparc) /* * If it's a SCSI disk then we don't support fdisk and we * don't need to know the type cause we can ask the disk, * therefore we return true only if we *KNOW* it's an ATA * disk. */ if (cur_ctype && cur_ctype->ctype_ctype == DKC_PCMCIA_ATA) { return (1); } else { return (0); } #elif defined(i386) || defined(__amd64) return (1); #else #error No Platform defined #endif /* defined(sparc) */ } /* * 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 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _MENU_H #define _MENU_H #ifdef __cplusplus extern "C" { #endif /* * This file contains declarations pertaining to the menus. */ /* * This structure defines a menu entry. It consists of a command * name, the function to run the command, and a function to determine * if the menu entry is enabled at this particular state in the program. * The descriptive text that appears after the command name in the menus * is actually part of the command name to the program. Since * abbreviation is allowed for commands, the user never notices the * extra characters. */ struct menu_item { char *menu_cmd; int (*menu_func)(); int (*menu_state)(); }; /* * Prototypes for ANSI C compilers */ char **create_menu_list(struct menu_item *menu); void display_menu_list(char **list); void redisplay_menu_list(char **list); void run_menu(struct menu_item *, char *, char *, int); int true(void); int embedded_scsi(void); int not_embedded_scsi(void); int not_scsi(void); int not_efi(void); int disp_expert_change_expert_efi(void); int disp_expand_efi(void); int disp_all_change_expert_efi(void); int scsi(void); int scsi_expert(void); int expert(void); int developer(void); int support_fdisk_on_sparc(void); #ifdef __cplusplus } #endif #endif /* _MENU_H */ /* * 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. */ /* * This file contains functions implementing the analyze menu commands. */ #include #include "global.h" #include "analyze.h" #include "misc.h" #include "menu_analyze.h" #include "param.h" /* * This routine implements the 'read' command. It performs surface * analysis by reading the disk. It is ok to run this command on * mounted file systems. */ int a_read(void) { /* * The current disk must be formatted before disk analysis. */ if (!(cur_flags & DISK_FORMATTED)) { err_print("Current Disk is unformatted.\n"); return (-1); } if (check( "Ready to analyze (won't harm SunOS). This takes a long time, \n" "but is interruptible with CTRL-C. Continue")) return (-1); return (do_scan(SCAN_VALID, F_NORMAL)); } /* * This routine implements the 'refresh' command. It performs surface * analysis by reading the disk then writing the same data back to the * disk. It is ok to run this command on file systems, but not while * they are mounted. */ int a_refresh(void) { /* * The current disk must be formatted before disk analysis. */ if (!(cur_flags & DISK_FORMATTED)) { err_print("Current Disk is unformatted.\n"); return (-1); } if (check( "Ready to analyze (won't harm data). This takes a long time, \n" "but is interruptible with CTRL-C. Continue")) return (-1); return (do_scan(SCAN_VALID | SCAN_WRITE, F_NORMAL)); } /* * This routine implements the 'test' command. It performs surface * analysis by reading the disk, writing then reading a pattern on the disk, * then writing the original data back to the disk. * It is ok to run this command on file systems, but not while they are * mounted. */ int a_test(void) { /* * The current disk must be formatted before disk analysis. */ if (!(cur_flags & DISK_FORMATTED)) { err_print("Current Disk is unformatted.\n"); return (-1); } if (check( "Ready to analyze (won't harm data). This takes a long time, \n" "but is interruptible with CTRL-C. Continue")) return (-1); return (do_scan(SCAN_VALID | SCAN_PATTERN | SCAN_WRITE, F_NORMAL)); } /* * This routine implements the 'write' command. It performs surface * analysis by writing a pattern to the disk then reading it back. * It is not ok to run this command on any data you want to keep. */ int a_write(void) { /* * The current disk must be formatted before disk analysis. */ if (!(cur_flags & DISK_FORMATTED)) { err_print("Current Disk is unformatted.\n"); return (-1); } if (check( "Ready to analyze (will corrupt data). This takes a long time, \n" "but is interruptible with CTRL-C. Continue")) return (-1); return (do_scan(SCAN_PATTERN, F_NORMAL)); } /* * This routine implements the 'compare' command. It performs surface * analysis by writing a pattern to the disk, reading it back, then * checking the data to be sure it's the same. * It is not ok to run this command on any data you want to keep. */ int a_compare(void) { /* * The current disk must be formatted before disk analysis. */ if (!(cur_flags & DISK_FORMATTED)) { err_print("Current Disk is unformatted.\n"); return (-1); } if (check( "Ready to analyze (will corrupt data). This takes a long time, \n" "but is interruptible with CTRL-C. Continue")) return (-1); return (do_scan(SCAN_PATTERN | SCAN_COMPARE, F_NORMAL)); } /* * This routine implements the 'print' command. It displays the data * buffer in hexadecimal. It is only useful for checking the disk for * a specific set of data (by reading it then printing it). */ int a_print(void) { int i, j, lines, nomore = 0; int c, one_line = 0; int tty_lines = get_tty_lines(); /* * If we are running out of command file, don't page the output. * Otherwise we are running with a user. Turn off echoing of * input characters so we can page the output. */ if (option_f || (!isatty(0)) || (!isatty(1))) nomore++; else { enter_critical(); echo_off(); charmode_on(); exit_critical(); } /* * Loop through the data buffer. */ lines = 0; for (i = 0; i < scan_size * cur_blksz / sizeof (int); i += 6) { /* * Print the data. */ for (j = 0; j < 6; j++) if (i + j < scan_size * cur_blksz / sizeof (int)) fmt_print("0x%08x ", *((int *)((int *)cur_buf + i + j))); fmt_print("\n"); lines++; /* * If we are paging and hit the end of a page, wait for * the user to hit either space-bar, "q", return, * or ctrl-C before going on. */ if (one_line || (!nomore && (lines % (tty_lines - 1) == 0))) { /* * Print until first screenfull */ if (lines < (tty_lines -1)) continue; /* * Get the next character. */ (void) printf("- hit space for more - "); c = getchar(); (void) printf("\015"); one_line = 0; /* * Handle display one line command (return key) */ if (c == '\012') { one_line++; } /* Handle Quit command */ if (c == 'q') { (void) printf( " \015"); goto PRINT_EXIT; } /* handle ^D */ if (c == '\004') fullabort(); } } /* * If we were doing paging, turn echoing back on. */ PRINT_EXIT: if (!nomore) { enter_critical(); charmode_off(); echo_on(); exit_critical(); } return (0); } /* * This routine implements the 'setup' command. It allows the user * to program the variables that drive surface analysis. The approach * is to prompt the user for the value of each variable, with the current * value as the default. */ int a_setup(void) { int deflt; uint64_t size; u_ioparam_t ioparam; /* * Because of the polarity of the yes/no structure (yes is 0), * we have to invert the values for all yes/no questions. */ deflt = !scan_entire; ioparam.io_charlist = confirm_list; scan_entire = !input(FIO_MSTR, "Analyze entire disk", '?', &ioparam, &deflt, DATA_INPUT); /* * If we are not scanning the whole disk, input the bounds of the scan. */ if (!scan_entire) { ioparam.io_bounds.lower = 0; if ((cur_ctype->ctype_flags & CF_SCSI) && (cur_disk->label_type == L_TYPE_SOLARIS)) { ioparam.io_bounds.upper = datasects() - 1; } else if (cur_disk->label_type == L_TYPE_SOLARIS) { ioparam.io_bounds.upper = physsects() - 1; } else if (cur_disk->label_type == L_TYPE_EFI) { ioparam.io_bounds.upper = cur_parts->etoc->efi_last_lba; } scan_lower = (diskaddr_t)input(FIO_BN, "Enter starting block number", ':', &ioparam, (int *)&scan_lower, DATA_INPUT); ioparam.io_bounds.lower = scan_lower; if (scan_upper < scan_lower) scan_upper = scan_lower; scan_upper = (diskaddr_t)input(FIO_BN, "Enter ending block number", ':', &ioparam, (int *)&scan_upper, DATA_INPUT); } deflt = !scan_loop; ioparam.io_charlist = confirm_list; scan_loop = !input(FIO_MSTR, "Loop continuously", '?', &ioparam, &deflt, DATA_INPUT); /* * If we are not looping continuously, input the number of passes. */ if (!scan_loop) { ioparam.io_bounds.lower = 1; ioparam.io_bounds.upper = 100; scan_passes = input(FIO_INT, "Enter number of passes", ':', &ioparam, &scan_passes, DATA_INPUT); } deflt = !scan_correct; ioparam.io_charlist = confirm_list; scan_correct = !input(FIO_MSTR, "Repair defective blocks", '?', &ioparam, &deflt, DATA_INPUT); deflt = !scan_stop; ioparam.io_charlist = confirm_list; scan_stop = !input(FIO_MSTR, "Stop after first error", '?', &ioparam, &deflt, DATA_INPUT); deflt = !scan_random; ioparam.io_charlist = confirm_list; scan_random = !input(FIO_MSTR, "Use random bit patterns", '?', &ioparam, &deflt, DATA_INPUT); ioparam.io_bounds.lower = 1; /* * The number of blocks per transfer is limited by the buffer * size, or the scan boundaries, whichever is smaller. */ if ((scan_entire) && (cur_disk->label_type == L_TYPE_SOLARIS)) { size = physsects() - 1; } else if ((scan_entire) && (cur_disk->label_type == L_TYPE_EFI)) { size = cur_parts->etoc->efi_last_lba; } else { size = scan_upper - scan_lower + 1; } ioparam.io_bounds.upper = min(size, BUF_SECTS); if (scan_size > ioparam.io_bounds.upper) scan_size = ioparam.io_bounds.upper; scan_size = input(FIO_INT, "Enter number of blocks per transfer", ':', &ioparam, (int *)&scan_size, DATA_INPUT); deflt = !scan_auto; ioparam.io_charlist = confirm_list; scan_auto = !input(FIO_MSTR, "Verify media after formatting", '?', &ioparam, &deflt, DATA_INPUT); deflt = !option_msg; ioparam.io_charlist = confirm_list; option_msg = !input(FIO_MSTR, "Enable extended messages", '?', &ioparam, &deflt, DATA_INPUT); deflt = !scan_restore_defects; ioparam.io_charlist = confirm_list; scan_restore_defects = !input(FIO_MSTR, "Restore defect list", '?', &ioparam, &deflt, DATA_INPUT); deflt = !scan_restore_label; ioparam.io_charlist = confirm_list; scan_restore_label = !input(FIO_MSTR, "Restore disk label", '?', &ioparam, &deflt, DATA_INPUT); fmt_print("\n"); return (0); } /* * This routine implements the 'config' command. It simply prints out * the values of all the variables controlling surface analysis. It * is meant to complement the 'setup' command by allowing the user to * check the current setup. */ int a_config(void) { fmt_print(" Analyze entire disk? "); fmt_print(scan_entire ? "yes\n" : "no\n"); if (!scan_entire) { fmt_print(" Starting block number: %llu (", scan_lower); pr_dblock(fmt_print, scan_lower); fmt_print(")\n Ending block number: %llu (", scan_upper); pr_dblock(fmt_print, scan_upper); fmt_print(")\n"); } fmt_print(" Loop continuously? "); fmt_print(scan_loop ? "yes\n" : "no\n"); if (!scan_loop) { fmt_print(" Number of passes: %d\n", scan_passes); } fmt_print(" Repair defective blocks? "); fmt_print(scan_correct ? "yes\n" : "no\n"); fmt_print(" Stop after first error? "); fmt_print(scan_stop ? "yes\n" : "no\n"); fmt_print(" Use random bit patterns? "); fmt_print(scan_random ? "yes\n" : "no\n"); fmt_print(" Number of blocks per transfer: %d (", scan_size); pr_dblock(fmt_print, (diskaddr_t)scan_size); fmt_print(")\n"); fmt_print(" Verify media after formatting? "); fmt_print(scan_auto ? "yes\n" : "no\n"); fmt_print(" Enable extended messages? "); fmt_print(option_msg ? "yes\n" : "no\n"); fmt_print(" Restore defect list? "); fmt_print(scan_restore_defects ? "yes\n" : "no\n"); fmt_print(" Restore disk label? "); fmt_print(scan_restore_label ? "yes\n" : "no\n"); fmt_print("\n"); return (0); } /* * This routine implements the 'purge' command. It purges the disk * by writing three patterns to the disk then reading the last one back. * It is not ok to run this command on any data you want to keep. */ int a_purge(void) { int status = 0; /* * The current disk must be formatted before disk analysis. */ if (!(cur_flags & DISK_FORMATTED)) { err_print("Current Disk is unformatted.\n"); return (-1); } if (scan_random) { fmt_print("The purge command does not write random data\n"); scan_random = 0; } if (!scan_loop && (scan_passes <= NPPATTERNS)) { if (scan_passes < NPPATTERNS) { fmt_print("The purge command runs for a minimum of "); fmt_print("%d passes plus a last pass if the\n", NPPATTERNS); fmt_print("first %d passes were successful.\n", NPPATTERNS); } scan_passes = NPPATTERNS + 1; } if (check( "Ready to purge (will corrupt data). This takes a long time, \n" "but is interruptible with CTRL-C. Continue")) return (-1); status = do_scan(SCAN_PATTERN | SCAN_PURGE, F_NORMAL); return (status); } /* * This routine implements the 'verify' command. It writes the disk * by writing unique data for each block; after the write pass, it * reads the data and verifies for correctness. Note that the entire * disk (or the range of disk) is fully written first and then read. * This should eliminate any caching effect on the drives. * It is not ok to run this command on any data you want to keep. */ int a_verify(void) { /* * The current disk must be formatted before disk analysis. */ if (!(cur_flags & DISK_FORMATTED)) { err_print("Current Disk is unformatted.\n"); return (-1); } if (scan_random) { fmt_print("The verify command does not write random data\n"); scan_random = 0; } if (scan_passes < 2 && !scan_loop) { scan_passes = 2; fmt_print("The verify command runs minimum of 2 passes, one" " for writing and \nanother for reading and verfying." " Resetting the number of passes to 2.\n"); } if (check("Ready to verify (will corrupt data). This takes a long time," "\nbut is interruptible with CTRL-C. Continue")) { return (-1); } return (do_scan(SCAN_WRITE | SCAN_VERIFY, F_NORMAL)); } /* * 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 (c) 1991-2001 by Sun Microsystems, Inc. */ #ifndef _MENU_ANALYZE_H #define _MENU_ANALYZE_H #ifdef __cplusplus extern "C" { #endif /* * Prototypes for ANSI */ int a_read(void); int a_refresh(void); int a_test(void); int a_write(void); int a_compare(void); int a_verify(void); int a_print(void); int a_setup(void); int a_config(void); int a_purge(void); #ifdef __cplusplus } #endif #endif /* _MENU_ANALYZE_H */ /* * 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. */ /* * This file contains functions that implement the cache menu commands. */ #include "global.h" #include #include #include #include #include #include #include #include #include "main.h" #include "analyze.h" #include "menu.h" #include "menu_cache.h" #include "param.h" #include "misc.h" #include "label.h" #include "startup.h" #include "partition.h" #include "prompts.h" #include "checkdev.h" #include "io.h" #include "ctlr_scsi.h" #include "auto_sense.h" #include "hardware_structs.h" extern struct menu_item menu_cache[]; extern struct menu_item menu_write_cache[]; extern struct menu_item menu_read_cache[]; int c_cache(void) { cur_menu++; last_menu = cur_menu; run_menu(menu_cache, "CACHE", "cache", 0); cur_menu--; return (0); } int ca_write_cache(void) { cur_menu++; last_menu = cur_menu; run_menu(menu_write_cache, "WRITE_CACHE", "write_cache", 0); cur_menu--; return (0); } int ca_read_cache(void) { cur_menu++; last_menu = cur_menu; run_menu(menu_read_cache, "READ_CACHE", "read_cache", 0); cur_menu--; return (0); } int ca_write_display(void) { struct mode_cache *page8; struct scsi_ms_header header; int status; union { struct mode_cache page8; char rawbuf[MAX_MODE_SENSE_SIZE]; } u_page8; page8 = &u_page8.page8; status = uscsi_mode_sense(cur_file, DAD_MODE_CACHE, MODE_SENSE_PC_CURRENT, (caddr_t)page8, MAX_MODE_SENSE_SIZE, &header); if (status == 0) { if (page8->wce) { fmt_print("Write Cache is enabled\n"); } else { fmt_print("Write Cache is disabled\n"); } } else { err_print("Mode sense failed.\n"); } return (0); } int ca_write_enable(void) { struct mode_cache *page8; struct scsi_ms_header header; int status; int length; int sp_flags; union { struct mode_cache page8; char rawbuf[MAX_MODE_SENSE_SIZE]; } u_page8; page8 = &u_page8.page8; status = uscsi_mode_sense(cur_file, DAD_MODE_CACHE, MODE_SENSE_PC_CHANGEABLE, (caddr_t)page8, MAX_MODE_SENSE_SIZE, &header); if (status == 0) { if (page8->wce) { status = uscsi_mode_sense(cur_file, DAD_MODE_CACHE, MODE_SENSE_PC_SAVED, (caddr_t)page8, MAX_MODE_SENSE_SIZE, &header); if (status != 0) { status = uscsi_mode_sense(cur_file, DAD_MODE_CACHE, MODE_SENSE_PC_CURRENT, (caddr_t)page8, MAX_MODE_SENSE_SIZE, &header); } if (status == 0) { length = MODESENSE_PAGE_LEN(page8); sp_flags = MODE_SELECT_PF; if (page8->mode_page.ps) { sp_flags |= MODE_SELECT_SP; } else { err_print("\ This setting is valid until next reset only. It is not saved permanently.\n"); } page8->mode_page.ps = 0; page8->wce = 1; header.mode_header.length = 0; header.mode_header.device_specific = 0; status = uscsi_mode_select(cur_file, DAD_MODE_CACHE, sp_flags, (caddr_t)page8, length, &header); if (status != 0) { err_print("Mode select failed\n"); return (0); } } } else { err_print("Write cache setting is not changeable\n"); } } if (status != 0) { err_print("Mode sense failed.\n"); } return (0); } int ca_write_disable(void) { struct mode_cache *page8; struct scsi_ms_header header; int status; int length; int sp_flags; union { struct mode_cache page8; char rawbuf[MAX_MODE_SENSE_SIZE]; } u_page8; page8 = &u_page8.page8; status = uscsi_mode_sense(cur_file, DAD_MODE_CACHE, MODE_SENSE_PC_CHANGEABLE, (caddr_t)page8, MAX_MODE_SENSE_SIZE, &header); if (status == 0) { if (page8->wce) { status = uscsi_mode_sense(cur_file, DAD_MODE_CACHE, MODE_SENSE_PC_SAVED, (caddr_t)page8, MAX_MODE_SENSE_SIZE, &header); if (status != 0) { status = uscsi_mode_sense(cur_file, DAD_MODE_CACHE, MODE_SENSE_PC_CURRENT, (caddr_t)page8, MAX_MODE_SENSE_SIZE, &header); } if (status == 0) { length = MODESENSE_PAGE_LEN(page8); sp_flags = MODE_SELECT_PF; if (page8->mode_page.ps) { sp_flags |= MODE_SELECT_SP; } else { err_print("\ This setting is valid until next reset only. It is not saved permanently.\n"); } page8->mode_page.ps = 0; page8->wce = 0; header.mode_header.length = 0; header.mode_header.device_specific = 0; status = uscsi_mode_select(cur_file, DAD_MODE_CACHE, sp_flags, (caddr_t)page8, length, &header); if (status != 0) { err_print("Mode select failed\n"); return (0); } } } else { err_print("Write cache setting is not changeable\n"); } } if (status != 0) { err_print("Mode sense failed.\n"); } return (0); } int ca_read_display(void) { struct mode_cache *page8; struct scsi_ms_header header; int status; union { struct mode_cache page8; char rawbuf[MAX_MODE_SENSE_SIZE]; } u_page8; page8 = &u_page8.page8; status = uscsi_mode_sense(cur_file, DAD_MODE_CACHE, MODE_SENSE_PC_CURRENT, (caddr_t)page8, MAX_MODE_SENSE_SIZE, &header); if (status == 0) { if (page8->rcd) { fmt_print("Read Cache is disabled\n"); } else { fmt_print("Read Cache is enabled\n"); } } else { err_print("Mode sense failed.\n"); } return (0); } int ca_read_enable(void) { struct mode_cache *page8; struct scsi_ms_header header; int status; int length; int sp_flags; union { struct mode_cache page8; char rawbuf[MAX_MODE_SENSE_SIZE]; } u_page8; page8 = &u_page8.page8; status = uscsi_mode_sense(cur_file, DAD_MODE_CACHE, MODE_SENSE_PC_CHANGEABLE, (caddr_t)page8, MAX_MODE_SENSE_SIZE, &header); if (status == 0) { if (page8->rcd) { status = uscsi_mode_sense(cur_file, DAD_MODE_CACHE, MODE_SENSE_PC_SAVED, (caddr_t)page8, MAX_MODE_SENSE_SIZE, &header); if (status != 0) { status = uscsi_mode_sense(cur_file, DAD_MODE_CACHE, MODE_SENSE_PC_CURRENT, (caddr_t)page8, MAX_MODE_SENSE_SIZE, &header); } if (status == 0) { length = MODESENSE_PAGE_LEN(page8); sp_flags = MODE_SELECT_PF; if (page8->mode_page.ps) { sp_flags |= MODE_SELECT_SP; } else { err_print("\ This setting is valid until next reset only. It is not saved permanently.\n"); } page8->mode_page.ps = 0; page8->rcd = 0; header.mode_header.length = 0; header.mode_header.device_specific = 0; status = uscsi_mode_select(cur_file, DAD_MODE_CACHE, sp_flags, (caddr_t)page8, length, &header); if (status != 0) { err_print("Mode select failed\n"); return (0); } } } else { err_print("Read cache setting is not changeable\n"); } } if (status != 0) { err_print("Mode sense failed.\n"); } return (0); } int ca_read_disable(void) { struct mode_cache *page8; struct scsi_ms_header header; int status; int length; int sp_flags; union { struct mode_cache page8; char rawbuf[MAX_MODE_SENSE_SIZE]; } u_page8; page8 = &u_page8.page8; status = uscsi_mode_sense(cur_file, DAD_MODE_CACHE, MODE_SENSE_PC_CHANGEABLE, (caddr_t)page8, MAX_MODE_SENSE_SIZE, &header); if (status == 0) { if (page8->rcd) { status = uscsi_mode_sense(cur_file, DAD_MODE_CACHE, MODE_SENSE_PC_SAVED, (caddr_t)page8, MAX_MODE_SENSE_SIZE, &header); if (status != 0) { status = uscsi_mode_sense(cur_file, DAD_MODE_CACHE, MODE_SENSE_PC_CURRENT, (caddr_t)page8, MAX_MODE_SENSE_SIZE, &header); } if (status == 0) { length = MODESENSE_PAGE_LEN(page8); sp_flags = MODE_SELECT_PF; if (page8->mode_page.ps) { sp_flags |= MODE_SELECT_SP; } else { err_print("\ This setting is valid until next reset only. It is not saved permanently.\n"); } page8->mode_page.ps = 0; page8->rcd = 1; header.mode_header.length = 0; header.mode_header.device_specific = 0; status = uscsi_mode_select(cur_file, DAD_MODE_CACHE, sp_flags, (caddr_t)page8, length, &header); if (status != 0) { err_print("Mode select failed\n"); return (0); } } } else { err_print("Read cache setting is not changeable\n"); } } if (status != 0) { err_print("Mode sense failed.\n"); } return (0); } /* * 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 (c) 1999 by Sun Microsystems, Inc. */ #ifndef _MENU_CACHE_H #define _MENU_CACHE_H #ifdef __cplusplus extern "C" { #endif /* * Prototypes for ANSI */ int c_cache(void); int ca_write_cache(void); int ca_read_cache(void); int ca_write_display(void); int ca_write_enable(void); int ca_write_disable(void); int ca_read_display(void); int ca_read_enable(void); int ca_read_disable(void); #ifdef __cplusplus } #endif #endif /* _MENU_CACHE_H */ /* * 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 (c) 1993, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright 2012 Milan Jurik. All rights reserved. * Copyright 2014 Toomas Soome * Copyright 2015 Nexenta Systems, Inc. All rights reserved. * Copyright 2016 Igor Kozhukhov * Copyright (c) 2016 by Delphix. All rights reserved. */ /* * This file contains functions that implement the command menu commands. */ #include "global.h" #include #include #include #include #include #include #include #include #if defined(sparc) #include #endif /* defined(sparc) */ #include "main.h" #include "analyze.h" #include "menu.h" #include "menu_command.h" #include "menu_defect.h" #include "menu_partition.h" #include "param.h" #include "misc.h" #include "label.h" #include "startup.h" #include "partition.h" #include "prompts.h" #include "checkdev.h" #include "io.h" #include "ctlr_scsi.h" #include "auto_sense.h" #include "modify_partition.h" extern struct menu_item menu_partition[]; extern struct menu_item menu_analyze[]; extern struct menu_item menu_defect[]; int prot_type; /* * Choices for the p_tag vtoc field */ slist_t ptag_choices[] = { { "unassigned", "", V_UNASSIGNED }, { "boot", "", V_BOOT }, { "root", "", V_ROOT }, { "swap", "", V_SWAP }, { "usr", "", V_USR }, { "backup", "", V_BACKUP }, { "stand", "", V_STAND }, { "var", "", V_VAR }, { "home", "", V_HOME }, { "alternates", "", V_ALTSCTR }, { "reserved", "", V_RESERVED }, { "system", "", V_SYSTEM }, { "BIOS_boot", "", V_BIOS_BOOT }, { "FreeBSD boot", "", V_FREEBSD_BOOT }, { "FreeBSD swap", "", V_FREEBSD_SWAP }, { "FreeBSD UFS", "", V_FREEBSD_UFS }, { "FreeBSD ZFS", "", V_FREEBSD_ZFS }, { NULL } }; /* * Choices for the p_flag vtoc field */ slist_t pflag_choices[] = { { "wm", "read-write, mountable", 0 }, { "wu", "read-write, unmountable", V_UNMNT }, { "rm", "read-only, mountable", V_RONLY }, { "ru", "read-only, unmountable", V_RONLY|V_UNMNT }, { NULL } }; /* * This routine implements the 'disk' command. It allows the user to * select a disk to be current. The list of choices is the list of * disks that were found at startup time. */ int c_disk(void) { struct disk_info *disk; u_ioparam_t ioparam; int i; int ndisks = 0; int blind_select = 0; int deflt; int index; int *defltptr = NULL; int more = 0; int more_quit = 0; int one_line = 0; int tty_lines; /* * This buffer holds the check() prompt that verifies we've got the right * disk when performing a blind selection. The size should be sufficient * to hold the prompt string, plus 256 characters for the disk name - * way more than should ever be necessary. See the #define in misc.h. */ char chk_buf[BLIND_SELECT_VER_PROMPT]; if (istokenpresent()) { /* * disk number to be selected is already in the * input stream . */ TOKEN token, cleantoken; /* * Get the disk number the user has given. */ i = 0; for (disk = disk_list; disk != NULL; disk = disk->disk_next) { i++; } ioparam.io_bounds.lower = 0; ioparam.io_bounds.upper = i - 1; (void) gettoken(token); clean_token(cleantoken, token); /* * Convert the token into an integer. */ if (geti(cleantoken, &index, NULL)) return (0); /* * Check to be sure it is within the legal bounds. */ if ((index < 0) || (index >= i)) { err_print("`%d' is out of range.\n", index); return (0); } goto checkdisk; } fmt_print("\n\nAVAILABLE DISK SELECTIONS:\n"); i = 0; if ((option_f == NULL) && isatty(0) == 1 && isatty(1) == 1) { /* * We have a real terminal for std input and output, enable * more style of output for disk selection list. */ more = 1; tty_lines = get_tty_lines(); enter_critical(); echo_off(); charmode_on(); exit_critical(); } /* * Loop through the list of found disks. */ for (disk = disk_list; disk != NULL; disk = disk->disk_next) { /* * If using more output, account 2 lines for each disk. */ if (more && !more_quit && i && (one_line || ((2 * i + 1) % (tty_lines - 2) <= 1))) { int c; /* * Get the next character. */ fmt_print("- hit space for more or s to select - "); c = getchar(); fmt_print("\015"); one_line = 0; /* * Handle display one line command * (return key) */ if (c == '\012') { one_line++; } /* Handle Quit command */ if (c == 'q') { fmt_print( " \015"); more_quit++; } /* Handle ^D command */ if (c == '\004') fullabort(); /* or get on with the show */ if (c == 's' || c == 'S') { fmt_print("%80s\n", " "); break; } } /* * If this is the current disk, mark it as * the default. */ if (cur_disk == disk) { deflt = i; defltptr = &deflt; } if (!more || !more_quit) pr_diskline(disk, i); i++; } if (more) { enter_critical(); charmode_off(); echo_on(); exit_critical(); } /* * Determine total number of disks, and ask the user which disk they * would like to make current. */ for (disk = disk_list; disk != NULL; disk = disk->disk_next) { ndisks++; } ioparam.io_bounds.lower = 0; ioparam.io_bounds.upper = ndisks - 1; index = input(FIO_INT, "Specify disk (enter its number)", ':', &ioparam, defltptr, DATA_INPUT); if (index >= i) { blind_select = 1; } /* * Find the disk chosen. Search through controllers/disks * in the same original order, so we match what the user * chose. */ checkdisk: i = 0; for (disk = disk_list; disk != NULL; disk = disk->disk_next) { if (i == index) goto found; i++; } /* * Should never happen. */ impossible("no disk found"); found: if (blind_select) { (void) snprintf(chk_buf, sizeof (chk_buf), "Disk %s selected - is this the desired disk? ", disk->disk_name); if (check(chk_buf)) { return (-1); } } /* * Update the state. We lock out interrupts so the state can't * get half-updated. */ enter_critical(); init_globals(disk); exit_critical(); /* * If type unknown and interactive, ask user to specify type. * Also, set partition table (best guess) too. */ if (!option_f && ncyl == 0 && nhead == 0 && nsect == 0 && (disk->label_type != L_TYPE_EFI)) { (void) c_type(); } /* * Get the Solaris Fdisk Partition information */ if (nhead != 0 && nsect != 0) (void) copy_solaris_part(&cur_disk->fdisk_part); if ((cur_disk->label_type == L_TYPE_EFI) && (cur_disk->disk_parts->etoc->efi_flags & EFI_GPT_PRIMARY_CORRUPT)) { err_print("Reading the primary EFI GPT label "); err_print("failed. Using backup label.\n"); err_print("Use the 'backup' command to restore "); err_print("the primary label.\n"); } #if defined(_SUNOS_VTOC_16) /* * If there is no fdisk solaris partition. */ if (cur_disk->fdisk_part.numsect == 0) { err_print("No Solaris fdisk partition found.\n"); goto exit; } #endif /* defined(_SUNOS_VTOC_16) */ /* * If the label of the disk is marked dirty, * see if they'd like to label the disk now. */ if (cur_disk->disk_flags & DSK_LABEL_DIRTY) { if (check("Disk not labeled. Label it now") == 0) { if (write_label()) { err_print("Write label failed\n"); } else { cur_disk->disk_flags &= ~DSK_LABEL_DIRTY; } } } exit: return (0); } /* * This routine implements the 'type' command. It allows the user to * specify the type of the current disk. It should be necessary only * if the disk was not labelled or was somehow labelled incorrectly. * The list of legal types for the disk comes from information that was * in the data file. */ int c_type(void) { struct disk_type *type, *tptr, *oldtype; u_ioparam_t ioparam; int i, index, deflt, *defltptr = NULL; struct disk_type disk_type; struct disk_type *d = &disk_type; int first_disk; int auto_conf_choice; int other_choice; struct dk_label label; struct efi_info efi_info; uint64_t maxLBA; char volname[LEN_DKL_VVOL]; int volinit = 0; /* * There must be a current disk. */ if (cur_disk == NULL) { err_print("Current Disk is not set.\n"); return (-1); } oldtype = cur_disk->disk_type; type = cur_ctype->ctype_dlist; /* * Print out the list of choices. */ fmt_print("\n\nAVAILABLE DRIVE TYPES:\n"); first_disk = 0; if (cur_ctype->ctype_ctype == DKC_SCSI_CCS) { auto_conf_choice = 0; fmt_print(" %d. Auto configure\n", first_disk++); } else { auto_conf_choice = -1; } i = first_disk; for (tptr = type; tptr != NULL; tptr = tptr->dtype_next) { /* * If we pass the current type, mark it to be the default. */ if (cur_dtype == tptr) { deflt = i; defltptr = &deflt; } if (cur_disk->label_type == L_TYPE_EFI) { continue; } if (tptr->dtype_asciilabel) fmt_print(" %d. %s\n", i++, tptr->dtype_asciilabel); } other_choice = i; fmt_print(" %d. other\n", i); ioparam.io_bounds.lower = 0; ioparam.io_bounds.upper = i; /* * Ask the user which type the disk is. */ index = input(FIO_INT, "Specify disk type (enter its number)", ':', &ioparam, defltptr, DATA_INPUT); /* * Find the type they chose. */ if (index == auto_conf_choice) { float scaled; diskaddr_t nblks; int nparts; /* * User chose "auto configure". */ (void) strcpy(x86_devname, cur_disk->disk_name); switch (cur_disk->label_type) { case L_TYPE_SOLARIS: if ((tptr = auto_sense(cur_file, 1, &label)) == NULL) { err_print("Auto configure failed\n"); return (-1); } fmt_print("%s: configured with capacity of ", cur_disk->disk_name); nblks = (diskaddr_t)tptr->dtype_ncyl * tptr->dtype_nhead * tptr->dtype_nsect; scaled = bn2mb(nblks); if (scaled > 1024.0) { fmt_print("%1.2fGB\n", scaled/1024.0); } else { fmt_print("%1.2fMB\n", scaled); } fmt_print("<%s cyl %d alt %d hd %d sec %d>\n", tptr->dtype_asciilabel, tptr->dtype_ncyl, tptr->dtype_acyl, tptr->dtype_nhead, tptr->dtype_nsect); break; case L_TYPE_EFI: if ((tptr = auto_efi_sense(cur_file, &efi_info)) == NULL) { err_print("Auto configure failed\n"); return (-1); } fmt_print("%s: configured with capacity of ", cur_disk->disk_name); scaled = bn2mb(efi_info.capacity); if (scaled > 1024.0) { fmt_print("%1.2fGB\n", scaled/1024.0); } else { fmt_print("%1.2fMB\n", scaled); } cur_blksz = efi_info.e_parts->efi_lbasize; print_efi_string(efi_info.vendor, efi_info.product, efi_info.revision, efi_info.capacity); fmt_print("\n"); for (nparts = 0; nparts < cur_parts->etoc->efi_nparts; nparts++) { if (cur_parts->etoc->efi_parts[nparts].p_tag == V_RESERVED) { (void) strcpy(volname, cur_parts->etoc->efi_parts [nparts].p_name); volinit = 1; break; } } enter_critical(); if (delete_disk_type(cur_disk->disk_type) != 0) { fmt_print("Autoconfiguration failed.\n"); return (-1); } cur_disk->disk_type = tptr; cur_disk->disk_parts = tptr->dtype_plist; init_globals(cur_disk); exit_critical(); if (volinit) { for (nparts = 0; nparts < cur_parts->etoc->efi_nparts; nparts++) { if (cur_parts->etoc->efi_parts[nparts].p_tag == V_RESERVED) { (void) strcpy( cur_parts->etoc->efi_parts[nparts].p_name, volname); (void) strlcpy(cur_disk->v_volume, volname, LEN_DKL_VVOL); break; } } } return (0); default: /* Should never happen */ return (-1); } } else if ((index == other_choice) && (cur_label == L_TYPE_SOLARIS)) { /* * User chose "other". * Get the standard information on the new type. * Put all information in a tmp structure, in * case user aborts. */ bzero((char *)d, sizeof (struct disk_type)); d->dtype_ncyl = get_ncyl(); d->dtype_acyl = get_acyl(d->dtype_ncyl); d->dtype_pcyl = get_pcyl(d->dtype_ncyl, d->dtype_acyl); d->dtype_nhead = get_nhead(); d->dtype_phead = get_phead(d->dtype_nhead, &d->dtype_options); d->dtype_nsect = get_nsect(); d->dtype_psect = get_psect(&d->dtype_options); d->dtype_bpt = get_bpt(d->dtype_nsect, &d->dtype_options); d->dtype_rpm = get_rpm(); d->dtype_fmt_time = get_fmt_time(&d->dtype_options); d->dtype_cyl_skew = get_cyl_skew(&d->dtype_options); d->dtype_trk_skew = get_trk_skew(&d->dtype_options); d->dtype_trks_zone = get_trks_zone(&d->dtype_options); d->dtype_atrks = get_atrks(&d->dtype_options); d->dtype_asect = get_asect(&d->dtype_options); d->dtype_cache = get_cache(&d->dtype_options); d->dtype_threshold = get_threshold(&d->dtype_options); d->dtype_prefetch_min = get_min_prefetch(&d->dtype_options); d->dtype_prefetch_max = get_max_prefetch(d->dtype_prefetch_min, &d->dtype_options); d->dtype_bps = get_bps(); #if defined(sparc) d->dtype_dr_type = 0; #endif /* defined(sparc) */ d->dtype_asciilabel = get_asciilabel(); /* * Add the new type to the list of possible types for * this controller. We lock out interrupts so the lists * can't get munged. We put off actually allocating the * structure till here in case the user wanted to * interrupt while still inputting information. */ enter_critical(); tptr = (struct disk_type *)zalloc(sizeof (struct disk_type)); if (type == NULL) cur_ctype->ctype_dlist = tptr; else { while (type->dtype_next != NULL) type = type->dtype_next; type->dtype_next = tptr; } bcopy((char *)d, (char *)tptr, sizeof (disk_type)); tptr->dtype_next = NULL; /* * the new disk type does not have any defined * partition table . Hence copy the current partition * table if possible else create a default * paritition table. */ new_partitiontable(tptr, oldtype); } else if ((index == other_choice) && (cur_label == L_TYPE_EFI)) { uint64_t start_lba = cur_parts->etoc->efi_first_u_lba; uint64_t reserved; reserved = efi_reserved_sectors(cur_parts->etoc); maxLBA = get_mlba(); cur_parts->etoc->efi_last_lba = maxLBA; cur_parts->etoc->efi_last_u_lba = maxLBA - start_lba; for (i = 0; i < cur_parts->etoc->efi_nparts; i++) { cur_parts->etoc->efi_parts[i].p_start = 0; cur_parts->etoc->efi_parts[i].p_size = 0; cur_parts->etoc->efi_parts[i].p_tag = V_UNASSIGNED; } cur_parts->etoc->efi_parts[8].p_start = maxLBA - start_lba - reserved; cur_parts->etoc->efi_parts[8].p_size = reserved; cur_parts->etoc->efi_parts[8].p_tag = V_RESERVED; if (write_label()) { err_print("Write label failed\n"); } else { cur_disk->disk_flags &= ~DSK_LABEL_DIRTY; } return (0); } else { /* * User picked an existing disk type. */ i = first_disk; tptr = type; while (i < index) { if (tptr->dtype_asciilabel) { i++; } tptr = tptr->dtype_next; } if ((tptr->dtype_asciilabel == NULL) && (tptr->dtype_next != NULL)) { while (tptr->dtype_asciilabel == NULL) { tptr = tptr->dtype_next; } } } /* * Check for mounted file systems in the format zone. * One potential problem with this would be that check() * always returns 'yes' when running out of a file. However, * it is actually ok because we don't let the program get * started if there are mounted file systems and we are * running from a file. */ if ((tptr != oldtype) && checkmount((diskaddr_t)-1, (diskaddr_t)-1)) { err_print( "Cannot set disk type while it has mounted " "partitions.\n\n"); return (-1); } /* * check for partitions being used for swapping in format zone */ if ((tptr != oldtype) && checkswap((diskaddr_t)-1, (diskaddr_t)-1)) { err_print("Cannot set disk type while its partition are " "currently being used for swapping.\n"); return (-1); } /* * Check for partitions being used in SVM, VxVM or LU devices */ if ((tptr != oldtype) && checkdevinuse(cur_disk->disk_name, (diskaddr_t)-1, (diskaddr_t)-1, 0, 0)) { err_print("Cannot set disk type while its " "partitions are currently in use.\n"); return (-1); } /* * If the type selected is different from the previous type, * mark the disk as not labelled and reload the current * partition info. This is not essential but probably the * right thing to do, since the size of the disk has probably * changed. */ enter_critical(); if (tptr != oldtype) { cur_disk->disk_type = tptr; cur_disk->disk_parts = NULL; cur_disk->disk_flags &= ~DSK_LABEL; } /* * Initialize the state of the current disk. */ init_globals(cur_disk); (void) get_partition(); exit_critical(); /* * If the label of the disk is marked dirty, * see if they'd like to label the disk now. */ if (cur_disk->disk_flags & DSK_LABEL_DIRTY) { if (check("Disk not labeled. Label it now") == 0) { if (write_label()) { err_print("Write label failed\n"); } else { cur_disk->disk_flags &= ~DSK_LABEL_DIRTY; } } } return (0); } /* * This routine implements the 'partition' command. It simply runs * the partition menu. */ int c_partition(void) { /* * There must be a current disk type and a current disk */ if (cur_dtype == NULL) { err_print("Current Disk Type is not set.\n"); return (-1); } /* * Check for a valid fdisk table entry for Solaris */ if (!good_fdisk()) { return (-1); } cur_menu++; last_menu = cur_menu; #ifdef not /* * If there is no current partition table, make one. This is * so the commands within the menu never have to check for * a non-existent table. */ if (cur_parts == NULL) err_print("making partition.\n"); make_partition(); #endif /* not */ /* * Run the menu. */ run_menu(menu_partition, "PARTITION", "partition", 0); cur_menu--; return (0); } /* * This routine implements the 'current' command. It describes the * current disk. */ int c_current(void) { /* * If there is no current disk, say so. Note that this is * not an error since it is a legitimate response to the inquiry. */ if (cur_disk == NULL) { fmt_print("No Current Disk.\n"); return (0); } /* * Print out the info we have on the current disk. */ fmt_print("Current Disk = %s", cur_disk->disk_name); if (chk_volname(cur_disk)) { fmt_print(": "); print_volname(cur_disk); } fmt_print("\n"); if (cur_disk->devfs_name != NULL) { if (cur_dtype == NULL) { fmt_print("\n"); } else if (cur_label == L_TYPE_SOLARIS) { fmt_print("<%s cyl %d alt %d hd %d sec %d>\n", cur_dtype->dtype_asciilabel, ncyl, acyl, nhead, nsect); } else if (cur_label == L_TYPE_EFI) { print_efi_string(cur_dtype->vendor, cur_dtype->product, cur_dtype->revision, cur_dtype->capacity); fmt_print("\n"); } fmt_print("%s\n", cur_disk->devfs_name); } else { fmt_print("%s%d: <", cur_ctlr->ctlr_dname, cur_disk->disk_dkinfo.dki_unit); if (cur_dtype == NULL) { fmt_print("type unknown"); } else if (cur_label == L_TYPE_SOLARIS) { fmt_print("%s cyl %d alt %d hd %d sec %d", cur_dtype->dtype_asciilabel, ncyl, acyl, nhead, nsect); } else if (cur_label == L_TYPE_EFI) { print_efi_string(cur_dtype->vendor, cur_dtype->product, cur_dtype->revision, cur_dtype->capacity); fmt_print("\n"); } fmt_print(">\n"); } fmt_print("\n"); return (0); } /* * This routine implements the 'format' command. It allows the user * to format and verify any portion of the disk. */ int c_format(void) { diskaddr_t start, end; time_t clock; int format_time, format_tracks, format_cyls; int format_interval; diskaddr_t deflt; int status; u_ioparam_t ioparam; struct scsi_inquiry *inq; char rawbuf[MAX_MODE_SENSE_SIZE]; struct scsi_capacity_16 capacity; struct vpd_hdr *vpdhdr; uint8_t protect; uint8_t pagecode; uint8_t spt; uint8_t p_type; uint8_t prot_flag[NUM_PROT_TYPE] = {1, 0, 0, 0}; int i; char *prot_descriptor[NUM_PROT_TYPE] = { "Protection Information is disabled.", "Protection Information is enabled.", "Protection Information is enabled.", "Protection Information is enabled.", }; /* * There must be a current disk type and a current disk */ if (cur_dtype == NULL) { err_print("Current Disk Type is not set.\n"); return (-1); } /* * There must be a format routine in cur_ops structure to have * this routine work. */ if (cur_ops->op_format == NULL) { err_print( "Cannot format this drive. Please use your Manufacturer supplied formatting " "utility.\n"); return (-1); } /* * There must be a current defect list. Except for * unformatted SCSI disks. For them the defect list * can only be retrieved after formatting the disk. */ if ((cur_ctype->ctype_flags & CF_SCSI) && !EMBEDDED_SCSI && (cur_ctype->ctype_flags & CF_DEFECTS) && ! (cur_flags & DISK_FORMATTED)) { cur_list.flags |= LIST_RELOAD; } else if (cur_list.list == NULL && !EMBEDDED_SCSI) { err_print("Current Defect List must be initialized.\n"); return (-1); } /* * Ask for the bounds of the format. We always use the whole * disk as the default, since that is the most likely case. * Note, for disks which must be formatted accross the whole disk, * don't bother the user. */ ioparam.io_bounds.lower = start = 0; if (cur_label == L_TYPE_SOLARIS) { if (cur_ctype->ctype_flags & CF_SCSI) { ioparam.io_bounds.upper = end = datasects() - 1; } else { ioparam.io_bounds.upper = end = physsects() - 1; } } else { ioparam.io_bounds.upper = end = cur_parts->etoc->efi_last_lba; } if (! (cur_ctlr->ctlr_flags & DKI_FMTVOL)) { deflt = ioparam.io_bounds.lower; start = input(FIO_BN, "Enter starting block number", ':', &ioparam, (int *)&deflt, DATA_INPUT); ioparam.io_bounds.lower = start; deflt = ioparam.io_bounds.upper; end = input(FIO_BN, "Enter ending block number", ':', &ioparam, (int *)&deflt, DATA_INPUT); } /* * Some disks can format tracks. Make sure the whole track is * specified for them. */ if (cur_ctlr->ctlr_flags & DKI_FMTTRK) { if (bn2s(start) != 0 || bn2s(end) != sectors(bn2h(end)) - 1) { err_print("Controller requires formatting of "); err_print("entire tracks.\n"); return (-1); } } /* * Check for mounted file systems in the format zone, and if we * find any, make sure they are really serious. One potential * problem with this would be that check() always returns 'yes' * when running out of a file. However, it is actually ok * because we don't let the program get started if there are * mounted file systems and we are running from a file. */ if (checkmount(start, end)) { err_print( "Cannot format disk while it has mounted partitions.\n\n"); return (-1); } /* * check for partitions being used for swapping in format zone */ if (checkswap(start, end)) { err_print("Cannot format disk while its partition are \ currently being used for swapping.\n"); return (-1); } /* * Check for partitions being used in SVM, VxVM or LU devices * in this format zone */ if (checkdevinuse(cur_disk->disk_name, start, end, 0, 0)) { err_print("Cannot format disk while its partitions " "are currently in use.\n"); return (-1); } if (cur_disk->disk_lbasize != DEV_BSIZE) { fmt_print("Current disk sector size is %d Byte, format\n" "will change the sector size to 512 Byte. ", cur_disk->disk_lbasize); if (check("Continue")) { return (-1); } } /* * set the default protection type */ prot_type = PROT_TYPE_0; /* * Check if the protect information of this disk is enabled */ if (uscsi_inquiry(cur_file, rawbuf, sizeof (rawbuf))) { err_print("Inquiry failed\n"); return (-1); } inq = (struct scsi_inquiry *)rawbuf; protect = inq->inq_protect; if (protect == 0) { fmt_print("The protection information is not enabled\n"); fmt_print( "The disk will be formatted with protection type 0\n"); } else { (void) memset(rawbuf, 0, MAX_MODE_SENSE_SIZE); if (uscsi_inquiry_page_86h(cur_file, rawbuf, sizeof (rawbuf))) { err_print("Inquiry with page 86h failed\n"); return (-1); } vpdhdr = (struct vpd_hdr *)rawbuf; pagecode = vpdhdr->page_code; if (pagecode != 0x86) { err_print("Inquiry with page 86h failed\n"); return (-1); } spt = (rawbuf[4] << 2) >> 5; fmt_print("This disk can support protection types:\n"); switch (spt) { case 0: prot_flag[1] = 1; break; case 1: prot_flag[1] = 1; prot_flag[2] = 1; break; case 2: prot_flag[2] = 1; break; case 3: prot_flag[1] = 1; prot_flag[3] = 1; break; case 4: prot_flag[3] = 1; break; case 5: prot_flag[2] = 1; prot_flag[3] = 1; break; case 7: prot_flag[1] = 1; prot_flag[2] = 1; prot_flag[3] = 1; break; default: err_print( "Invalid supported protection types\n"); return (-1); } for (i = 0; i < NUM_PROT_TYPE; i++) { if (prot_flag[i] == 1) { fmt_print("[%d] TYPE_%d : ", i, i); fmt_print("%s\n", prot_descriptor[i]); } } /* * Get the current protection type */ if (uscsi_read_capacity_16(cur_file, &capacity)) { err_print("Read capacity_16 failed\n"); return (-1); } p_type = get_cur_protection_type(&capacity); fmt_print("\nThe disk is currently formatted with TYPE_%d.\n", p_type); /* * Ask user what protection type to use */ ioparam.io_bounds.lower = PROT_TYPE_0; ioparam.io_bounds.upper = PROT_TYPE_3; prot_type = input(FIO_INT, "Specify the New Protection Type", ':', &ioparam, NULL, DATA_INPUT); /* * if get a unsupported protection type, then use the * current type: p_type. */ if (prot_flag[prot_type] == 0) { fmt_print("Unsupported protection type.\n"); prot_type = p_type; } fmt_print("The disk will be formatted to type %d\n", prot_type); } if (SCSI && (format_time = scsi_format_time()) > 0) { fmt_print( "\nReady to format. Formatting cannot be interrupted\n" "and takes %d minutes (estimated). ", format_time); } else if (cur_dtype->dtype_options & SUP_FMTTIME) { /* * Formatting time is (2 * time of 1 spin * number of * tracks) + (step rate * number of cylinders) rounded * up to the nearest minute. Note, a 10% fudge factor * is thrown in for insurance. */ if (cur_dtype->dtype_fmt_time == 0) cur_dtype->dtype_fmt_time = 2; format_tracks = ((end-start) / cur_dtype->dtype_nsect) + 1; format_cyls = format_tracks / cur_dtype->dtype_nhead; format_tracks = format_tracks * cur_dtype->dtype_fmt_time; /* * ms. */ format_time = ((60000 / cur_dtype->dtype_rpm) +1) * format_tracks + format_cyls * 7; /* * 20% done tick (sec) */ format_interval = format_time / 5000; /* * min. */ format_time = (format_time + 59999) / 60000; /* * Check format time values and make adjustments * to prevent sleeping too long (forever?) or * too short. */ if (format_time <= 1) { /* * Format time is less than 1 min.. */ format_time = 1; } if (format_interval < 11) { /* Format time is less than 1 minute. */ if (format_interval < 2) format_interval = 2; /* failsafe */ format_interval = 10; } else { /* Format time is greater than 1 minute. */ format_interval -= 10; } fmt_print( "Ready to format. Formatting cannot be interrupted\n" "and takes %d minutes (estimated). ", format_time); } else { fmt_print( "Ready to format. Formatting cannot be interrupted.\n"); } if (check("Continue")) { return (-1); } /* * Print the time so that the user will know when format started. * Lock out interrupts. This could be a problem, since it could * cause the user to sit for quite awhile with no control, but we * don't have any other good way of keeping their gun from going off. */ clock = time(NULL); fmt_print("Beginning format. The current time is %s\n", ctime(&clock)); enter_critical(); /* * Mark the defect list dirty so it will be rewritten when we are * done. It is possible to qualify this so it doesn't always * get rewritten, but it's not worth the trouble. * Note: no defect lists for embedded scsi drives. */ if (!EMBEDDED_SCSI) { cur_list.flags |= LIST_DIRTY; } /* * If we are formatting over any of the labels, mark the label * dirty so it will be rewritten. */ if (cur_disk->label_type == L_TYPE_SOLARIS) { if (start < totalsects() && end >= datasects()) { if (cur_disk->disk_flags & DSK_LABEL) cur_flags |= LABEL_DIRTY; } } else if (cur_disk->label_type == L_TYPE_EFI) { if (start < cur_parts->etoc->efi_first_u_lba) { if (cur_disk->disk_flags & DSK_LABEL) cur_flags |= LABEL_DIRTY; } } if (start == 0) { cur_flags |= LABEL_DIRTY; } /* * Do the format. bugid 1009138 removed the use of fork to * background the format and print a tick. */ status = (*cur_ops->op_format)(start, end, &cur_list); if (status) { exit_critical(); err_print("failed\n"); return (-1); } fmt_print("done\n"); if (option_msg && diag_msg) { clock = time((time_t *)0); fmt_print("The current time is %s\n", ctime(&clock)); } cur_flags |= DISK_FORMATTED; /* * If the defect list or label is dirty, write them out again. * Note, for SCSI we have to wait til now to load defect list * since we can't access it until after formatting a virgin disk. */ /* enter_critical(); */ if (cur_list.flags & LIST_RELOAD) { assert(!EMBEDDED_SCSI); if (*cur_ops->op_ex_man == NULL || (*cur_ops->op_ex_man)(&cur_list)) { err_print("Warning: unable to reload defect list\n"); cur_list.flags &= ~LIST_DIRTY; return (-1); } cur_list.flags |= LIST_DIRTY; } if (cur_list.flags & LIST_DIRTY) { assert(!EMBEDDED_SCSI); write_deflist(&cur_list); cur_list.flags = 0; } if (cur_flags & LABEL_DIRTY) { (void) write_label(); cur_flags &= ~LABEL_DIRTY; } /* * Come up for air, since the verify step does not need to * be atomic (it does it's own lockouts when necessary). */ exit_critical(); /* * If we are supposed to verify, we do the 'write' test over * the format zone. The rest of the analysis parameters are * left the way they were. */ if (scan_auto) { scan_entire = 0; scan_lower = start; scan_upper = end; fmt_print("\nVerifying media..."); status = do_scan(SCAN_PATTERN, F_SILENT); } /* * If the defect list or label is dirty, write them out again. */ if (cur_list.flags & LIST_DIRTY) { assert(!EMBEDDED_SCSI); cur_list.flags = 0; write_deflist(&cur_list); } if (cur_flags & LABEL_DIRTY) { cur_flags &= ~LABEL_DIRTY; (void) write_label(); } return (status); } /* * This routine implements the 'repair' command. It allows the user * to reallocate sectors on the disk that have gone bad. */ int c_repair(void) { diskaddr_t bn; int status; u_ioparam_t ioparam; char *buf; int buf_is_good; int block_has_error; int i; /* * There must be a current disk type (and therefore a current disk). */ if (cur_dtype == NULL) { err_print("Current Disk Type is not set.\n"); return (-1); } /* * The current disk must be formatted for repair to work. */ if (!(cur_flags & DISK_FORMATTED)) { err_print("Current Disk is unformatted.\n"); return (-1); } /* * Check for a valid fdisk table entry for Solaris */ if (!good_fdisk()) { return (-1); } /* * Repair is an optional command for controllers, so it may * not be supported. */ if (cur_ops->op_repair == NULL) { err_print("Controller does not support repairing.\n"); err_print("or disk supports automatic defect management.\n"); return (-1); } /* * There must be a defect list for non-embedded scsi devices, * since we will add to it. */ if (!EMBEDDED_SCSI && cur_list.list == NULL) { err_print("Current Defect List must be initialized.\n"); return (-1); } /* * Ask the user which sector has gone bad. */ ioparam.io_bounds.lower = 0; if (cur_disk->label_type == L_TYPE_SOLARIS) { ioparam.io_bounds.upper = physsects() - 1; } else { ioparam.io_bounds.upper = cur_parts->etoc->efi_last_lba; } bn = input(FIO_BN, "Enter absolute block number of defect", ':', &ioparam, NULL, DATA_INPUT); /* * Check to see if there is a mounted file system over the * specified sector. If there is, make sure the user is * really serious. */ if (checkmount(bn, bn)) { if (check("Repair is in a mounted partition, continue")) return (-1); } /* * check for partitions being used for swapping in format zone */ if (checkswap(bn, bn)) { if (check("Repair is in a partition which is currently \ being used for swapping.\ncontinue")) return (-1); } if (checkdevinuse(cur_disk->disk_name, bn, bn, 0, 0)) { if (check("Repair is in a partition which is currently " "in use.\ncontinue")) return (-1); } buf = zalloc((cur_disk->disk_lbasize == 0) ? SECSIZE : cur_disk->disk_lbasize); /* * Try to read the sector before repairing it. If we can * get good data out of it, we can write that data back * after the repair. If the sector looks ok, ask the * user to confirm the repair, since it doesn't appear * necessary. Try reading the block several times to * see if we can read it consistently. * * First, let's see if the block appears to have problems... */ block_has_error = 1; for (i = 0; i < 5; i++) { status = (*cur_ops->op_rdwr)(DIR_READ, cur_file, bn, 1, buf, (F_SILENT | F_ALLERRS), NULL); if (status) break; /* one of the tries failed */ } if (status == 0) { block_has_error = 0; if (check("\ This block doesn't appear to be bad. Repair it anyway")) { free(buf); return (0); } } /* * Last chance... */ if (check("Ready to repair defect, continue")) { free(buf); return (-1); } /* * We're committed to repairing it. Try to get any good * data out of the block if possible. Note that we do * not set the F_ALLERRS flag. */ buf_is_good = 0; for (i = 0; i < 5; i++) { status = (*cur_ops->op_rdwr)(DIR_READ, cur_file, bn, 1, buf, F_SILENT, NULL); if (status == 0) { buf_is_good = 1; break; } } /* * Lock out interrupts so the disk can't get out of sync with * the defect list. */ enter_critical(); fmt_print("Repairing "); if (block_has_error) { fmt_print("%s error on ", buf_is_good ? "soft" : "hard"); } fmt_print("block %llu (", bn); pr_dblock(fmt_print, bn); fmt_print(")..."); /* * Do the repair. */ status = (*cur_ops->op_repair)(bn, F_NORMAL); if (status) { fmt_print("failed.\n\n"); } else { /* * The repair worked. Write the old data to the new * block if we were able to read it, otherwise * zero out the new block. If it looks like the * new block is bad, let the user know that, too. * Should we attempt auto-repair in this case? */ fmt_print("ok.\n"); if (!buf_is_good) { bzero(buf, cur_disk->disk_lbasize); } status = (*cur_ops->op_rdwr)(DIR_WRITE, cur_file, bn, 1, buf, (F_SILENT | F_ALLERRS), NULL); if (status == 0) { status = (*cur_ops->op_rdwr)(DIR_READ, cur_file, bn, 1, buf, (F_SILENT | F_ALLERRS), NULL); } if (status) { fmt_print("The new block %llu (", bn); pr_dblock(fmt_print, bn); fmt_print(") also appears defective.\n"); } fmt_print("\n"); /* * Add the bad sector to the defect list, write out * the defect list, and kill off the working list so * it will get synced up with the current defect list * next time we need it. * * For embedded scsi, we don't require a defect list. * However, if we have one, add the defect if the * list includes the grown list. If not, kill it * to force a resync if we need the list later. */ if (EMBEDDED_SCSI) { if (cur_list.list != NULL) { if (cur_list.flags & LIST_PGLIST) { add_ldef(bn, &cur_list); } else { kill_deflist(&cur_list); } } } else if (cur_ctype->ctype_flags & CF_WLIST) { kill_deflist(&cur_list); if (*cur_ops->op_ex_cur != NULL) { (*cur_ops->op_ex_cur)(&cur_list); fmt_print("Current list updated\n"); } } else { add_ldef(bn, &cur_list); write_deflist(&cur_list); } kill_deflist(&work_list); } exit_critical(); free(buf); /* * Return status. */ return (status); } /* * This routine implements the 'show' command. It translates a disk * block given in any format into decimal, hexadecimal, and * cylinder/head/sector format. */ int c_show(void) { u_ioparam_t ioparam; diskaddr_t bn; /* * There must be a current disk type, so we will know the geometry. */ if (cur_dtype == NULL) { err_print("Current Disk Type is not set.\n"); return (-1); } /* * Ask the user for a disk block. */ ioparam.io_bounds.lower = 0; if (cur_disk->label_type == L_TYPE_SOLARIS) { ioparam.io_bounds.upper = physsects() - 1; } else { ioparam.io_bounds.upper = cur_parts->etoc->efi_last_lba; } bn = input(FIO_BN, "Enter a disk block", ':', &ioparam, NULL, DATA_INPUT); /* * Echo it back. */ fmt_print("Disk block = %lld = 0x%llx = (", bn, bn); pr_dblock(fmt_print, bn); fmt_print(")\n\n"); return (0); } /* * This routine implements the 'label' command. It writes the * primary and backup labels onto the current disk. */ int c_label(void) { int status; int deflt, *defltptr = NULL; /* * There must be a current disk type (and therefore a current disk). */ if (cur_dtype == NULL) { err_print("Current Disk Type is not set.\n"); return (-1); } /* * The current disk must be formatted to label it. */ if (!(cur_flags & DISK_FORMATTED)) { err_print("Current Disk is unformatted.\n"); return (-1); } /* * Check for a valid fdisk table entry for Solaris */ if (!good_fdisk()) { return (-1); } /* * Check to see if there are any mounted file systems anywhere * on the current disk. If so, refuse to label the disk, but * only if the partitions would change for the mounted partitions. * */ if (checkmount((diskaddr_t)-1, (diskaddr_t)-1)) { /* Bleagh, too descriptive */ if (check_label_with_mount()) { err_print("Cannot label disk while it has " "mounted partitions.\n\n"); return (-1); } } /* * check to see if there any partitions being used for swapping * on the current disk. If so, refuse to label the disk, but * only if the partitions would change for the mounted partitions. */ if (checkswap((diskaddr_t)-1, (diskaddr_t)-1)) { if (check_label_with_swap()) { err_print("Cannot label disk while its " "partitions are currently being used for " "swapping.\n"); return (-1); } } /* * Check to see if any partitions used for svm, vxvm or live upgrade * are on the disk. If so, refuse to label the disk, but only * if we are trying to shrink a partition in use. */ if (checkdevinuse(cur_disk->disk_name, (diskaddr_t)-1, (diskaddr_t)-1, 0, 1)) { err_print("Cannot label disk when " "partitions are in use as described.\n"); return (-1); } /* * If there is not a current partition map, warn the user we * are going to use the default. The default is the first * partition map we encountered in the data file. If there is * no default we give up. */ if (cur_parts == NULL) { fmt_print("Current Partition Table is not set, " "using default.\n"); cur_disk->disk_parts = cur_parts = cur_dtype->dtype_plist; if (cur_parts == NULL) { err_print("No default available, cannot label.\n"); return (-1); } } /* * If expert (-e) mode, then ask user if they wish * to change the current solaris label into an EFI one */ if (expert_mode) { #if defined(_SUNOS_VTOC_8) int i; #endif int choice; u_ioparam_t ioparam; struct extvtoc vtoc; struct dk_label label; struct dk_gpt *vtoc64; struct efi_info efinfo; struct disk_type *dptr; /* Ask user what label to use */ fmt_print("[0] SMI Label\n"); fmt_print("[1] EFI Label\n"); ioparam.io_bounds.lower = 0; ioparam.io_bounds.upper = 1; if ((cur_label == L_TYPE_SOLARIS) && (cur_disk->fdisk_part.systid != EFI_PMBR)) deflt = L_TYPE_SOLARIS; else deflt = L_TYPE_EFI; defltptr = &deflt; choice = input(FIO_INT, "Specify Label type", ':', &ioparam, defltptr, DATA_INPUT); if ((choice == L_TYPE_SOLARIS) && (cur_label == L_TYPE_SOLARIS) && (cur_disk->fdisk_part.systid != EFI_PMBR)) { goto expert_end; } else if ((choice == L_TYPE_EFI) && (cur_label == L_TYPE_EFI)) { goto expert_end; } switch (choice) { case L_TYPE_SOLARIS: /* * EFI label to SMI label */ if (cur_dtype->capacity > INFINITY) { fmt_print("Warning: SMI labels only support up to " "2 TB.\n"); } if (cur_disk->fdisk_part.systid == EFI_PMBR) { fmt_print("Warning: This disk has an EFI label. " "Changing to SMI label will erase all\n" "current partitions.\n"); if (check("Continue")) return (-1); #if defined(_FIRMWARE_NEEDS_FDISK) fmt_print("You must use fdisk to delete the current " "EFI partition and create a new\n" "Solaris partition before you can convert the " "label.\n"); return (-1); #endif } #if defined(_FIRMWARE_NEEDS_FDISK) if (!(((cur_disk->fdisk_part.systid != SUNIXOS) || (cur_disk->fdisk_part.systid != SUNIXOS2)) && (cur_disk->fdisk_part.numsect > 0))) { fmt_print("You must use fdisk to create a Solaris " "partition before you can convert the label.\n"); return (-1); } #endif (void) memset((char *)&label, 0, sizeof (struct dk_label)); (void) strcpy(x86_devname, cur_disk->disk_name); if (cur_ctype->ctype_ctype == DKC_DIRECT || cur_ctype->ctype_ctype == DKC_BLKDEV) dptr = auto_direct_get_geom_label(cur_file, &label); else dptr = auto_sense(cur_file, 1, &label); if (dptr == NULL) { fmt_print("Autoconfiguration failed.\n"); return (-1); } pcyl = label.dkl_pcyl; ncyl = label.dkl_ncyl; acyl = label.dkl_acyl; nhead = label.dkl_nhead; nsect = label.dkl_nsect; if (delete_disk_type(cur_disk->disk_type) == 0) { cur_label = L_TYPE_SOLARIS; cur_disk->label_type = L_TYPE_SOLARIS; cur_disk->disk_type = dptr; cur_disk->disk_parts = dptr->dtype_plist; cur_dtype = dptr; cur_parts = dptr->dtype_plist; if (status = write_label()) err_print("Label failed.\n"); else cur_disk->disk_flags &= ~DSK_LABEL_DIRTY; return (status); } else { err_print("Label failed.\n"); return (-1); } case L_TYPE_EFI: /* * SMI label to EFI label */ if ((cur_disk->fdisk_part.systid == SUNIXOS) || (cur_disk->fdisk_part.systid == SUNIXOS2)) { fmt_print("Warning: This disk has an SMI label. " "Changing to EFI label will erase all\ncurrent " "partitions.\n"); if (check("Continue")) { return (-1); } } if (get_disk_info(cur_file, &efinfo, cur_disk) != 0) { return (-1); } (void) memset((char *)&label, 0, sizeof (struct dk_label)); label.dkl_pcyl = pcyl; label.dkl_ncyl = ncyl; label.dkl_acyl = acyl; #if defined(_SUNOS_VTOC_16) label.dkl_bcyl = bcyl; #endif /* defined(_SUNOC_VTOC_16) */ label.dkl_nhead = nhead; label.dkl_nsect = nsect; #if defined(_SUNOS_VTOC_8) for (i = 0; i < NDKMAP; i++) { label.dkl_map[i] = cur_parts->pinfo_map[i]; } #endif /* defined(_SUNOS_VTOC_8) */ label.dkl_magic = DKL_MAGIC; label.dkl_vtoc = cur_parts->vtoc; if (label_to_vtoc(&vtoc, &label) == -1) { return (-1); } if (SMI_vtoc_to_EFI(cur_file, &vtoc64) == -1) { return (-1); } if (efi_write(cur_file, vtoc64) != 0) { efi_err_check(vtoc64); err_print("Warning: error writing EFI.\n"); return (-1); } else { cur_disk->disk_flags &= ~DSK_LABEL_DIRTY; } /* * copy over the EFI vtoc onto the SMI vtoc and return * okay. */ dptr = auto_efi_sense(cur_file, &efinfo); if (dptr == NULL) { fmt_print("Autoconfiguration failed.\n"); return (-1); } cur_label = L_TYPE_EFI; cur_disk->label_type = L_TYPE_EFI; cur_disk->disk_type = dptr; cur_disk->disk_parts = dptr->dtype_plist; cur_dtype = dptr; cur_parts = dptr->dtype_plist; cur_parts->etoc = vtoc64; ncyl = pcyl = nsect = psect = acyl = phead = 0; /* * Get the Solais Fdisk Partition information. */ (void) copy_solaris_part(&cur_disk->fdisk_part); return (0); } } expert_end: /* * Make sure the user is serious. */ if (check("Ready to label disk, continue")) { return (-1); } /* * Write the labels out (this will also notify unix) and * return status. */ fmt_print("\n"); if (status = write_label()) err_print("Label failed.\n"); return (status); } /* * This routine implements the 'analyze' command. It simply runs * the analyze menu. */ int c_analyze(void) { /* * There must be a current disk type (and therefor a current disk). */ if (cur_dtype == NULL) { err_print("Current Disk Type is not set.\n"); return (-1); } cur_menu++; last_menu = cur_menu; /* * Run the menu. */ run_menu(menu_analyze, "ANALYZE", "analyze", 0); cur_menu--; return (0); } /* * This routine implements the 'defect' command. It simply runs * the defect menu. */ int c_defect(void) { int i; /* * There must be a current disk type (and therefor a current disk). */ if (cur_dtype == NULL) { err_print("Current Disk Type is not set.\n"); return (-1); } /* * Check for the defect management and list management ops and * display appropriate message. */ if ((cur_ops->op_ex_man == NULL) && (cur_ops->op_ex_cur == NULL) && (cur_ops->op_create == NULL) && (cur_ops->op_wr_cur == NULL)) { err_print("Controller does not support defect management\n"); err_print("or disk supports automatic defect management.\n"); return (-1); } cur_menu++; last_menu = cur_menu; /* * Lock out interrupt while we manipulate the defect lists. */ enter_critical(); /* * If the working list is null but there is a current list, * update the working list to be a copy of the current list. */ if ((work_list.list == NULL) && (cur_list.list != NULL)) { work_list.header = cur_list.header; work_list.list = (struct defect_entry *)zalloc( deflist_size(cur_blksz, work_list.header.count) * cur_blksz); for (i = 0; i < work_list.header.count; i++) *(work_list.list + i) = *(cur_list.list + i); work_list.flags = cur_list.flags & LIST_PGLIST; } exit_critical(); /* * Run the menu. */ run_menu(menu_defect, "DEFECT", "defect", 0); cur_menu--; /* * If the user has modified the working list but not committed * it, warn them that they are probably making a mistake. */ if (work_list.flags & LIST_DIRTY) { if (!EMBEDDED_SCSI) { err_print( "Warning: working defect list modified; but not committed.\n"); if (!check( "Do you wish to commit changes to current defect list")) (void) do_commit(); } } return (0); } /* * This routine implements the 'backup' command. It allows the user * to search for backup labels on the current disk. This is useful * if the primary label was lost and the user wishes to recover the * partition information for the disk. The disk is relabeled and * the current defect list is written out if a backup label is found. */ int c_backup(void) { struct dk_label label; struct disk_type *dtype; struct partition_info *parts, *plist; diskaddr_t bn; int sec, head, i; char *buf; /* * There must be a current disk type (and therefore a current disk). */ if (cur_dtype == NULL) { err_print("Current Disk Type is not set.\n"); return (-1); } /* * The disk must be formatted to read backup labels. */ if (!(cur_flags & DISK_FORMATTED)) { err_print("Current Disk is unformatted.\n"); return (-1); } /* * Check for a valid fdisk table entry for Solaris */ if (!good_fdisk()) { return (-1); } /* * If we found a primary label on this disk, make sure * the user is serious. */ if (cur_disk->label_type == L_TYPE_EFI) { if (((cur_disk->disk_parts->etoc->efi_flags & EFI_GPT_PRIMARY_CORRUPT) == 0) && check("Disk has a primary label, still continue")) return (-1); fmt_print("Restoring primary label.\n"); if (write_label()) { err_print("Failed\n"); return (-1); } return (0); } else if (((cur_disk->disk_flags & (DSK_LABEL | DSK_LABEL_DIRTY)) == DSK_LABEL) && (check("Disk has a primary label, still continue"))) { return (-1); } buf = zalloc(cur_blksz); fmt_print("Searching for backup labels..."); (void) fflush(stdout); /* * Some disks have the backup labels in a strange place. */ if (cur_ctype->ctype_flags & CF_BLABEL) head = 2; else head = nhead - 1; /* * Loop through each copy of the backup label. */ for (sec = 1; ((sec < BAD_LISTCNT * 2 + 1) && (sec < nsect)); sec += 2) { bn = chs2bn(ncyl + acyl - 1, head, sec) + solaris_offset; /* * Attempt to read it. */ if ((*cur_ops->op_rdwr)(DIR_READ, cur_file, bn, 1, buf, F_NORMAL, NULL)) { continue; } (void) memcpy((char *)&label, buf, sizeof (struct dk_label)); /* * Verify that it is a reasonable label. */ if (!checklabel(&label)) continue; if (trim_id(label.dkl_asciilabel)) continue; /* * Lock out interrupts while we manipulate lists. */ enter_critical(); fmt_print("found.\n"); /* * Find out which disk type the backup label claims. */ for (dtype = cur_ctype->ctype_dlist; dtype != NULL; dtype = dtype->dtype_next) if (dtype_match(&label, dtype)) break; /* * If it disagrees with our current type, something * real bad is happening. */ if (dtype != cur_dtype) { if (dtype == NULL) { fmt_print("\ Unknown disk type in backup label\n"); exit_critical(); free(buf); return (-1); } fmt_print("Backup label claims different type:\n"); fmt_print(" <%s cyl %d alt %d hd %d sec %d>\n", label.dkl_asciilabel, label.dkl_ncyl, label.dkl_acyl, label.dkl_nhead, label.dkl_nsect); if (check("Continue")) { exit_critical(); free(buf); return (-1); } cur_dtype = dtype; } /* * Try to match the partition map with a known map. */ for (parts = dtype->dtype_plist; parts != NULL; parts = parts->pinfo_next) if (parts_match(&label, parts)) break; /* * If we couldn't match it, allocate space for a new one, * fill in the info, and add it to the list. The name * for the new map is derived from the disk name. */ if (parts == NULL) { parts = (struct partition_info *) zalloc(sizeof (struct partition_info)); plist = dtype->dtype_plist; if (plist == NULL) dtype->dtype_plist = parts; else { while (plist->pinfo_next != NULL) plist = plist->pinfo_next; plist->pinfo_next = parts; } parts->pinfo_name = alloc_string("original"); for (i = 0; i < NDKMAP; i++) { #if defined(_SUNOS_VTOC_8) parts->pinfo_map[i] = label.dkl_map[i]; #elif defined(_SUNOS_VTOC_16) parts->pinfo_map[i].dkl_cylno = label.dkl_vtoc.v_part[i].p_start / spc(); parts->pinfo_map[i].dkl_nblk = label.dkl_vtoc.v_part[i].p_size; #else #error No VTOC layout defined. #endif /* defined(_SUNOS_VTOC_8) */ } parts->vtoc = label.dkl_vtoc; } /* * We now have a partition map. Make it the current map. */ cur_disk->disk_parts = cur_parts = parts; exit_critical(); /* * Rewrite the labels and defect lists, as appropriate. */ if (EMBEDDED_SCSI) { fmt_print("Restoring primary label.\n"); if (write_label()) { free(buf); return (-1); } } else { fmt_print("Restoring primary label and defect list.\n"); if (write_label()) { free(buf); return (-1); } if (cur_list.list != NULL) write_deflist(&cur_list); } fmt_print("\n"); free(buf); return (0); } /* * If we didn't find any backup labels, say so. */ fmt_print("not found.\n\n"); free(buf); return (0); } /* * This routine is called by c_verify() for an EFI labeled disk */ static int c_verify_efi(void) { struct efi_info efi_info; struct partition_info tmp_pinfo; int status; status = read_efi_label(cur_file, &efi_info, cur_disk); if (status != 0) { err_print("Warning: Could not read label.\n"); return (-1); } if (cur_parts->etoc->efi_flags & EFI_GPT_PRIMARY_CORRUPT) { err_print("Reading the primary EFI GPT label "); err_print("failed. Using backup label.\n"); err_print("Use the 'backup' command to restore "); err_print("the primary label.\n"); } tmp_pinfo.etoc = efi_info.e_parts; fmt_print("\n"); fmt_print("Volume name = <%8s>\n", cur_parts->etoc->efi_parts[8].p_name); fmt_print("ascii name = "); print_efi_string(efi_info.vendor, efi_info.product, efi_info.revision, efi_info.capacity); fmt_print("\n"); fmt_print("bytes/sector = %d\n", cur_blksz); fmt_print("sectors = %llu\n", cur_parts->etoc->efi_last_lba + 1); fmt_print("accessible sectors = %llu\n", cur_parts->etoc->efi_last_u_lba - cur_parts->etoc->efi_first_u_lba - efi_reserved_sectors(cur_parts->etoc) + 1); fmt_print("first usable sector = %llu\n", cur_parts->etoc->efi_first_u_lba); fmt_print("last usable sector = %llu\n", cur_parts->etoc->efi_last_u_lba); print_map(&tmp_pinfo); free(efi_info.vendor); free(efi_info.product); free(efi_info.revision); return (0); } /* * This routine implements the 'verify' command. It allows the user * to read the labels on the current disk. */ int c_verify(void) { struct dk_label p_label, b_label, *label; struct partition_info tmp_pinfo; diskaddr_t bn; int sec, head, i, status; int p_label_bad = 0; int b_label_bad = 0; int p_label_found = 0; int b_label_found = 0; char id_str[128]; char *buf; /* * There must be a current disk type (and therefore a current disk). */ if (cur_dtype == NULL) { err_print("Current Disk Type is not set.\n"); return (-1); } /* * The disk must be formatted to read labels. */ if (!(cur_flags & DISK_FORMATTED)) { err_print("Current Disk is unformatted.\n"); return (-1); } /* * Check for a valid fdisk table entry for Solaris */ if (!good_fdisk()) { return (-1); } /* * Branch off here if the disk is EFI labelled. */ if (cur_label == L_TYPE_EFI) { return (c_verify_efi()); } /* * Attempt to read the primary label. */ status = read_label(cur_file, &p_label); if (status == -1) { err_print("Warning: Could not read primary label.\n"); p_label_bad = 1; } else { /* * Verify that it is a reasonable label. */ /* * Save complete ascii string for printing later. */ (void) strncpy(id_str, p_label.dkl_asciilabel, 128); if ((!checklabel((struct dk_label *)&p_label)) || (trim_id(p_label.dkl_asciilabel))) { err_print("\ Warning: Primary label appears to be corrupt.\n"); p_label_bad = 1; } else { p_label_found = 1; /* * Make sure it matches current label */ if ((!dtype_match(&p_label, cur_dtype)) || (!parts_match(&p_label, cur_parts))) { err_print("\ Warning: Primary label on disk appears to be different from\ncurrent label.\n"); p_label_bad = 1; } } } /* * Read backup labels. * Some disks have the backup labels in a strange place. */ if (cur_ctype->ctype_flags & CF_BLABEL) head = 2; else head = nhead - 1; buf = zalloc(cur_blksz); /* * Loop through each copy of the backup label. */ for (sec = 1; ((sec < BAD_LISTCNT * 2 + 1) && (sec < nsect)); sec += 2) { bn = chs2bn(ncyl + acyl - 1, head, sec) + solaris_offset; /* * Attempt to read it. */ if ((*cur_ops->op_rdwr)(DIR_READ, cur_file, bn, 1, buf, F_NORMAL, NULL)) continue; (void) memcpy((char *)&b_label, buf, sizeof (struct dk_label)); /* * Verify that it is a reasonable label. */ if (!checklabel(&b_label)) continue; /* * Save complete label only if no primary label exists */ if (!p_label_found) (void) strncpy(id_str, b_label.dkl_asciilabel, 128); if (trim_id(b_label.dkl_asciilabel)) continue; b_label_found = 1; /* * Compare against primary label */ if (p_label_found) { if ((strcmp(b_label.dkl_asciilabel, p_label.dkl_asciilabel) != 0) || (b_label.dkl_ncyl != p_label.dkl_ncyl) || (b_label.dkl_acyl != p_label.dkl_acyl) || (b_label.dkl_nhead != p_label.dkl_nhead) || (b_label.dkl_nsect != p_label.dkl_nsect)) { b_label_bad = 1; } else { for (i = 0; i < NDKMAP; i++) { #if defined(_SUNOS_VTOC_8) if ((b_label.dkl_map[i].dkl_cylno != p_label.dkl_map[i].dkl_cylno) || (b_label.dkl_map[i].dkl_nblk != p_label.dkl_map[i].dkl_nblk)) { b_label_bad = 1; break; } #elif defined(_SUNOS_VTOC_16) if ((b_label.dkl_vtoc.v_part[i].p_tag != p_label.dkl_vtoc.v_part[i].p_tag) || (b_label.dkl_vtoc.v_part[i].p_flag != p_label.dkl_vtoc.v_part[i]. p_flag) || (b_label.dkl_vtoc.v_part[i].p_start != p_label.dkl_vtoc.v_part[i]. p_start) || (b_label.dkl_vtoc.v_part[i].p_size != p_label.dkl_vtoc.v_part[i]. p_size)) { b_label_bad = 1; break; } #else #error No VTOC layout defined. #endif /* defined(_SUNOS_VTOC_8) */ } } } if (b_label_bad) err_print( "Warning: Primary and backup labels do not match.\n"); break; } /* * If we didn't find any backup labels, say so. */ if (!b_label_found) err_print("Warning: Could not read backup labels.\n"); if ((!b_label_found) || (p_label_bad) || (b_label_bad)) err_print("\n\ Warning: Check the current partitioning and 'label' the disk or use the\n\ \t 'backup' command.\n"); /* * Print label information. */ if (p_label_found) { fmt_print("\nPrimary label contents:\n"); label = &p_label; } else if (b_label_found) { fmt_print("\nBackup label contents:\n"); label = &b_label; } else { free(buf); return (0); } /* * Must put info into partition_info struct for * for print routine. */ bzero(&tmp_pinfo, sizeof (struct partition_info)); for (i = 0; i < NDKMAP; i++) { #if defined(_SUNOS_VTOC_8) tmp_pinfo.pinfo_map[i] = label->dkl_map[i]; #elif defined(_SUNOS_VTOC_16) tmp_pinfo.pinfo_map[i].dkl_cylno = label->dkl_vtoc.v_part[i].p_start / spc(); tmp_pinfo.pinfo_map[i].dkl_nblk = label->dkl_vtoc.v_part[i].p_size; #else #error No VTOC layout defined. #endif /* defined(_SUNOS_VTOC_8) */ } tmp_pinfo.vtoc = label->dkl_vtoc; fmt_print("\n"); fmt_print("Volume name = <%8s>\n", label->dkl_vtoc.v_volume); fmt_print("ascii name = <%s>\n", id_str); fmt_print("pcyl = %4d\n", label->dkl_pcyl); fmt_print("ncyl = %4d\n", label->dkl_ncyl); fmt_print("acyl = %4d\n", label->dkl_acyl); #if defined(_SUNOS_VTOC_16) fmt_print("bcyl = %4d\n", label->dkl_bcyl); #endif /* defined(_SUNOS_VTOC_16) */ fmt_print("nhead = %4d\n", label->dkl_nhead); fmt_print("nsect = %4d\n", label->dkl_nsect); print_map(&tmp_pinfo); free(buf); return (0); } /* * This command implements the inquiry command, for embedded SCSI * disks only, which issues a SCSI inquiry command, and * displays the resulting vendor, product id and revision level. */ int c_inquiry(void) { char inqbuf[255]; struct scsi_inquiry *inq; assert(SCSI); inq = (struct scsi_inquiry *)inqbuf; if (uscsi_inquiry(cur_file, inqbuf, sizeof (inqbuf))) { err_print("Failed\n"); return (-1); } else { fmt_print("Vendor: "); print_buf(inq->inq_vid, sizeof (inq->inq_vid)); fmt_print("\nProduct: "); print_buf(inq->inq_pid, sizeof (inq->inq_pid)); fmt_print("\nRevision: "); print_buf(inq->inq_revision, sizeof (inq->inq_revision)); fmt_print("\n"); } return (0); } /* * This routine allows the user to set the 8-character * volume name in the vtoc. It then writes both the * primary and backup labels onto the current disk. */ int c_volname(void) { int status; char *prompt; union { int xfoo; char defvolname[LEN_DKL_VVOL+1]; } x; char s1[MAXPATHLEN], nclean[MAXPATHLEN]; char *volname; /* * There must be a current disk type (and therefore a current disk). */ if (cur_dtype == NULL) { err_print("Current Disk Type is not set.\n"); return (-1); } /* * The current disk must be formatted to label it. */ if (!(cur_flags & DISK_FORMATTED)) { err_print("Current Disk is unformatted.\n"); return (-1); } /* * Check for a valid fdisk table entry for Solaris */ if (!good_fdisk()) { return (-1); } /* * The current disk must be formatted to label it. */ if (cur_parts == NULL) { err_print( "Please select a partition map for the disk first.\n"); return (-1); } /* * Check to see if there are any mounted file systems anywhere * on the current disk. If so, refuse to label the disk, but * only if the partitions would change for the mounted partitions. * */ if (checkmount((diskaddr_t)-1, (diskaddr_t)-1)) { /* Bleagh, too descriptive */ if (check_label_with_mount()) { err_print( "Cannot label disk while it has mounted partitions.\n\n"); return (-1); } } /* * Check to see if there are partitions being used for swapping * on the current disk. If so, refuse to label the disk, but * only if the partitions would change for the swap partitions. * */ if (checkswap((diskaddr_t)-1, (diskaddr_t)-1)) { /* Bleagh, too descriptive */ if (check_label_with_swap()) { err_print( "Cannot label disk while its partitions are currently \ being used for swapping.\n\n"); return (-1); } } /* * Check to see if any partitions used for svm, vxvm, ZFS zpool * or live upgrade are on the disk. If so, refuse to label the * disk, but only if we are trying to shrink a partition in * use. */ if (checkdevinuse(cur_disk->disk_name, (diskaddr_t)-1, (diskaddr_t)-1, 0, 1)) { err_print("Cannot label disk while its partitions " "are in use as described.\n"); return (-1); } /* * Prompt for the disk volume name. */ prompt = "Enter 8-character volume name (remember quotes)"; bzero(x.defvolname, LEN_DKL_VVOL+1); bcopy(cur_disk->v_volume, x.defvolname, LEN_DKL_VVOL); /* * Get the input using "get_inputline" since * input would never return null string. */ fmt_print("%s[\"%s\"]:", prompt, x.defvolname); /* * Get input from the user. */ get_inputline(nclean, MAXPATHLEN); clean_token(s1, nclean); /* * check for return. */ if (s1[0] == 0) { volname = x.defvolname; } else { /* * remove the " mark from volname. */ if (s1[0] == '"') { int i = 1; volname = &s1[1]; while (s1[i] != '"' && s1[i] != '\0') i++; s1[i] = '\0'; clean_token(nclean, volname); volname = nclean; } else { (void) sscanf(&s1[0], "%1024s", nclean); volname = nclean; }; } /* * Make sure the user is serious. */ if (check("Ready to label disk, continue")) { fmt_print("\n"); return (-1); } /* * Use the volume name chosen above */ bzero(cur_disk->v_volume, LEN_DKL_VVOL); bcopy(volname, cur_disk->v_volume, min((int)strlen(volname), LEN_DKL_VVOL)); if (cur_label == L_TYPE_EFI) { bzero(cur_parts->etoc->efi_parts[8].p_name, LEN_DKL_VVOL); bcopy(volname, cur_parts->etoc->efi_parts[8].p_name, LEN_DKL_VVOL); } /* * Write the labels out (this will also notify unix) and * return status. */ fmt_print("\n"); if (status = write_label()) err_print("Label failed.\n"); return (status); } /* * 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 (c) 1991-2001 by Sun Microsystems, Inc. */ #ifndef _MENU_COMMAND_H #define _MENU_COMMAND_H #ifdef __cplusplus extern "C" { #endif /* * Prototypes for ANSI */ int c_disk(void); int c_type(void); int c_partition(void); int c_current(void); int c_format(void); int c_fdisk(void); int c_repair(void); int c_show(void); int c_label(void); int c_analyze(void); int c_defect(void); int c_backup(void); int c_volname(void); int c_verify(void); int c_inquiry(void); extern slist_t ptag_choices[]; extern slist_t pflag_choices[]; #ifdef __cplusplus } #endif #endif /* _MENU_COMMAND_H */ /* * 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. * Copyright (c) 2016 by Delphix. All rights reserved. */ /* * This file contains functions to implement the defect menu commands. */ #include "global.h" #include #include #include "misc.h" #include "menu_defect.h" #include "param.h" #include "ctlr_scsi.h" /* * This is the working defect list. All the commands here operate on * the working list, except for 'commit'. This way the user can * change their mind at any time without having mangled the current defect * list. */ struct defect_list work_list; static int commit_list(void); /* * This routine implements the 'restore' command. It sets the working * list equal to the current list. */ int d_restore(void) { int i; assert(!EMBEDDED_SCSI); /* * If the working list has not been modified, there's nothing to do. */ if (!(work_list.flags & LIST_DIRTY)) { err_print("working list was not modified.\n"); return (0); } /* * Make sure the user is serious. */ if (check("Ready to update working list, continue")) return (-1); /* * Lock out interrupts so the lists can't get mangled. */ enter_critical(); /* * Kill off the old working list. */ kill_deflist(&work_list); /* * If the current isn't null, set the working list to be a * copy of it. */ if (cur_list.list != NULL) { work_list.header = cur_list.header; work_list.list = (struct defect_entry *)zalloc( deflist_size(cur_blksz, work_list.header.count) * cur_blksz); for (i = 0; i < work_list.header.count; i++) *(work_list.list + i) = *(cur_list.list + i); } /* * Initialize the flags since they are now in sync. */ work_list.flags = 0; if (work_list.list == NULL) fmt_print("working list set to null.\n\n"); else fmt_print("working list updated, total of %d defects.\n\n", work_list.header.count); exit_critical(); return (0); } /* * This routine implements the 'original' command. It extracts the * manufacturer's defect list from the current disk. */ int d_original(void) { int status; /* * If the controller does not support the extraction, we're out * of luck. */ if (cur_ops->op_ex_man == NULL) { err_print("Controller does not support extracting "); err_print("manufacturer's defect list.\n"); return (-1); } /* * Make sure the user is serious. Note, for SCSI disks * since this is instantaneous, we will just do it and * not ask for confirmation. */ if (!(cur_ctype->ctype_flags & CF_CONFIRM) && check( "Ready to update working list. This cannot be interrupted\n\ and may take a long while. Continue")) return (-1); /* * Lock out interrupts so we don't get half the list. */ enter_critical(); /* * Kill off the working list. */ kill_deflist(&work_list); fmt_print("Extracting manufacturer's defect list..."); /* * Do the extraction. */ status = (*cur_ops->op_ex_man)(&work_list); if (status) fmt_print("Extraction failed.\n\n"); else { fmt_print("Extraction complete.\n"); fmt_print("Working list updated, total of %d defects.\n\n", work_list.header.count); } /* * Mark the working list dirty since we modified it. */ work_list.flags |= LIST_DIRTY; exit_critical(); /* * Return status. */ return (status); } /* * This routine implements the 'extract' command. It extracts the * entire defect list from the current disk. */ int d_extract(void) { int status; /* * If the controller does not support the extraction, we are out * of luck. */ if (cur_ops->op_ex_cur == NULL) { err_print("Controller does not support extracting "); err_print("current defect list.\n"); return (-1); } /* * If disk is unformatted, you really shouldn't do this. * However, ask user to be sure. */ if (! (cur_flags & DISK_FORMATTED) && (check( "Cannot extract defect list from an unformatted disk. Continue"))) return (-1); /* * If this takes a long time, let's ask the user if they * doesn't mind waiting. Note, for SCSI disks * this operation is instantaneous so we won't ask for * for confirmation. */ if (! (cur_ctype->ctype_flags & CF_CONFIRM) && check( "Ready to extract working list. This cannot be interrupted\n\ and may take a long while. Continue")) return (-1); /* * Lock out interrupts so we don't get half the list and * Kill off the working list. */ enter_critical(); kill_deflist(&work_list); fmt_print("Extracting defect list..."); /* * Do the extraction. */ status = (*cur_ops->op_ex_cur)(&work_list); if (status) { if (!EMBEDDED_SCSI) { if (cur_flags & DISK_FORMATTED) read_list(&work_list); if (work_list.list != NULL) { status = 0; fmt_print("Extraction complete.\n"); fmt_print( "Working list updated, total of %d defects.\n\n", work_list.header.count); } else { fmt_print("Extraction failed.\n\n"); } } else { fmt_print("Extraction failed.\n\n"); } } else { fmt_print("Extraction complete.\n"); fmt_print("Working list updated, total of %d defects.\n\n", work_list.header.count); } /* * Mark the working list dirty since we modified it. */ work_list.flags |= LIST_DIRTY; exit_critical(); /* * Return status. */ return (status); } /* * This routine implements the 'add' command. It allows the user to * enter the working defect list manually. It loops infinitely until * the user breaks out with a ctrl-C. */ int d_add(void) { int type, deflt, index; diskaddr_t bn; u_ioparam_t ioparam; struct defect_entry def; assert(!EMBEDDED_SCSI); /* * Ask the user which mode of input they'd like to use. */ fmt_print(" 0. bytes-from-index\n"); fmt_print(" 1. logical block\n"); deflt = 0; ioparam.io_bounds.lower = 0; ioparam.io_bounds.upper = 1; type = input(FIO_INT, "Select input format (enter its number)", ':', &ioparam, &deflt, DATA_INPUT); fmt_print("\nEnter Control-C to terminate.\n"); loop: if (type) { /* * Mode selected is logical block. Input the defective block * and fill in the defect entry with the info. */ def.bfi = def.nbits = UNKNOWN; ioparam.io_bounds.lower = 0; if (cur_disk->label_type == L_TYPE_SOLARIS) { ioparam.io_bounds.upper = physsects() - 1; } else { ioparam.io_bounds.upper = cur_parts->etoc->efi_last_lba; } bn = input(FIO_BN, "Enter defective block number", ':', &ioparam, NULL, DATA_INPUT); def.cyl = bn2c(bn); def.head = bn2h(bn); def.sect = bn2s(bn); } else { /* * Mode selected is bytes-from-index. Input the information * about the defect and fill in the defect entry. */ def.sect = UNKNOWN; ioparam.io_bounds.lower = 0; ioparam.io_bounds.upper = pcyl - 1; def.cyl = input(FIO_INT, "Enter defect's cylinder number", ':', &ioparam, NULL, DATA_INPUT); ioparam.io_bounds.upper = nhead - 1; def.head = input(FIO_INT, "Enter defect's head number", ':', &ioparam, NULL, DATA_INPUT); ioparam.io_bounds.upper = cur_dtype->dtype_bpt - 1; def.bfi = input(FIO_INT, "Enter defect's bytes-from-index", ':', &ioparam, NULL, DATA_INPUT); ioparam.io_bounds.lower = -1; ioparam.io_bounds.upper = (cur_dtype->dtype_bpt - def.bfi) * 8; if (ioparam.io_bounds.upper >= 32 * 1024) ioparam.io_bounds.upper = 32 * 1024 - 1; /* * Note: a length of -1 means the length is not known. We * make this the default value. */ deflt = -1; def.nbits = input(FIO_INT, "Enter defect's length (in bits)", ':', &ioparam, &deflt, DATA_INPUT); } /* * Calculate where in the defect list this defect belongs * and print it out. */ index = sort_defect(&def, &work_list); fmt_print(DEF_PRINTHEADER); pr_defect(&def, index); /* * Lock out interrupts so lists don't get mangled. * Also, mark the working list dirty since we are modifying it. */ enter_critical(); work_list.flags |= LIST_DIRTY; /* * If the list is null, create it with zero length. This is * necessary because the routines to add a defect to the list * assume the list is initialized. */ if (work_list.list == NULL) { work_list.header.magicno = (uint_t)DEFECT_MAGIC; work_list.header.count = 0; work_list.list = (struct defect_entry *)zalloc( deflist_size(cur_blksz, 0) * cur_blksz); } /* * Add the defect to the working list. */ add_def(&def, &work_list, index); fmt_print("defect number %d added.\n\n", index + 1); exit_critical(); /* * Loop back for the next defect. */ goto loop; /*NOTREACHED*/ #ifdef lint return (0); #endif } /* * This routine implements the 'delete' command. It allows the user * to manually delete a defect from the working list. */ int d_delete(void) { int i, count, num; u_ioparam_t ioparam; assert(!EMBEDDED_SCSI); /* * If the working list is null or zero length, there's nothing * to delete. */ count = work_list.header.count; if (work_list.list == NULL || count == 0) { err_print("No defects to delete.\n"); return (-1); } /* * Ask the user which defect should be deleted. Bounds are off by * one because user sees a one-relative list. */ ioparam.io_bounds.lower = 1; ioparam.io_bounds.upper = count; num = input(FIO_INT, "Specify defect to be deleted (enter its number)", ':', &ioparam, NULL, DATA_INPUT); /* * * The user thinks it's one relative but it's not really. */ --num; /* * Print the defect selected and ask the user for confirmation. */ fmt_print(DEF_PRINTHEADER); pr_defect(work_list.list + num, num); /* * Lock out interrupts so the lists don't get mangled. */ enter_critical(); /* * Move down all the defects beyond the one deleted so the defect * list is still fully populated. */ for (i = num; i < count - 1; i++) *(work_list.list + i) = *(work_list.list + i + 1); /* * If the size of the list in sectors has changed, reallocate * the list to shrink it appropriately. */ if (deflist_size(cur_blksz, count - 1) < deflist_size(cur_blksz, count)) work_list.list = (struct defect_entry *)rezalloc( (void *)work_list.list, deflist_size(cur_blksz, count - 1) * cur_blksz); /* * Decrement the defect count. */ work_list.header.count--; /* * Recalculate the list's checksum. */ (void) checkdefsum(&work_list, CK_MAKESUM); /* * Mark the working list dirty since we modified it. */ work_list.flags |= LIST_DIRTY; fmt_print("defect number %d deleted.\n\n", ++num); exit_critical(); return (0); } /* * This routine implements the 'print' command. It prints the working * defect list out in human-readable format. */ int d_print(void) { int i, nomore = 0; int c, one_line = 0; int tty_lines = get_tty_lines(); /* * If the working list is null, there's nothing to print. */ if (work_list.list == NULL) { if (EMBEDDED_SCSI) err_print( "No list defined,extract primary or grown or both defects list first.\n"); else err_print("No working list defined.\n"); return (-1); } /* * If we're running from a file, don't use the paging scheme. * If we are running interactive, turn off echoing. */ if (option_f || (!isatty(0)) || (!isatty(1))) nomore++; else { enter_critical(); echo_off(); charmode_on(); exit_critical(); } /* Print out the banner. */ if (work_list.header.count != 0) fmt_print(DEF_PRINTHEADER); /* * Loop through the each defect in the working list. */ for (i = 0; i < work_list.header.count; i++) { /* * If we are paging and hit the end of a page, wait for * the user to hit either space-bar, "q", or return * before going on. */ if (one_line || (!nomore && ((i + 1) % (tty_lines - 1) == 0))) { /* * Get the next character. */ fmt_print("- hit space for more - "); c = getchar(); fmt_print("\015"); one_line = 0; /* Handle display one line command (return key) */ if (c == '\012') { one_line++; } /* Handle Quit command */ if (c == 'q') { fmt_print(" \015"); goto PRINT_EXIT; } /* Handle ^D */ if (c == '\004') fullabort(); } /* * Print the defect. */ pr_defect(work_list.list + i, i); } fmt_print("total of %d defects.\n\n", i); /* * If we were doing paging, turn echoing back on. */ PRINT_EXIT: if (!nomore) { enter_critical(); charmode_off(); echo_on(); exit_critical(); } return (0); } /* * This routine implements the 'dump' command. It writes the working * defect list to a file. */ int d_dump(void) { int i, status = 0; char *str; FILE *fptr; struct defect_entry *dptr; /* * If the working list is null, there's nothing to do. */ if (work_list.list == NULL) { if (EMBEDDED_SCSI) err_print( "No list defined,extract primary or grown or both defects list first.\n"); else err_print("No working list defined.\n"); return (-1); } /* * Ask the user for the name of the defect file. Note that the * input will be in malloc'd space since we are inputting * type OSTR. */ str = (char *)(uintptr_t)input(FIO_OSTR, "Enter name of defect file", ':', NULL, NULL, DATA_INPUT); /* * Lock out interrupts so the file doesn't get half written. */ enter_critical(); /* * Open the file for writing. */ if ((fptr = fopen(str, "w+")) == NULL) { err_print("unable to open defect file.\n"); status = -1; goto out; } /* * Print a header containing the magic number, count, and checksum. */ (void) fprintf(fptr, "0x%08x%8d 0x%08x\n", work_list.header.magicno, work_list.header.count, work_list.header.cksum); /* * Loop through each defect in the working list. Write the * defect info to the defect file. */ for (i = 0; i < work_list.header.count; i++) { dptr = work_list.list + i; (void) fprintf(fptr, "%4d%8d%7d%8d%8d%8d\n", i+1, dptr->cyl, dptr->head, dptr->bfi, dptr->nbits, dptr->sect); } fmt_print("defect file updated, total of %d defects.\n", i); /* * Close the defect file. */ (void) fclose(fptr); out: /* * Destroy the string used for the file name. */ destroy_data(str); exit_critical(); fmt_print("\n"); return (status); } /* * This routine implements the 'load' command. It reads the working * list in from a file. */ int d_load(void) { int i, items, status = 0, count, cksum; uint_t magicno; char *str; TOKEN filename; FILE *fptr; struct defect_entry *dptr; assert(!EMBEDDED_SCSI); /* * Ask the user for the name of the defect file. Note that the * input will be malloc'd space since we inputted type OSTR. */ str = (char *)(uintptr_t)input(FIO_OSTR, "Enter name of defect file", ':', NULL, NULL, DATA_INPUT); /* * Copy the file name into local space then destroy the string * it came in. This is simply a precaution against later having * to remember to destroy this space. */ enter_critical(); (void) strcpy(filename, str); destroy_data(str); exit_critical(); /* * See if the defect file is accessable. If not, we can't load * from it. We do this here just so we can get out before asking * the user for confirmation. */ status = access(filename, 4); if (status) { err_print("defect file not accessable.\n"); return (-1); } /* * Make sure the user is serious. */ if (check("ready to update working list, continue")) return (-1); /* * Lock out interrupts so the list doesn't get half loaded. */ enter_critical(); /* * Open the defect file. */ if ((fptr = fopen(filename, "r")) == NULL) { err_print("unable to open defect file.\n"); exit_critical(); return (-1); } /* * Scan in the header. */ items = fscanf(fptr, "0x%x%d 0x%x\n", &magicno, &count, (uint_t *)&cksum); /* * If the header is wrong, this isn't a good defect file. */ if (items != 3 || count < 0 || (magicno != (uint_t)DEFECT_MAGIC && magicno != (uint_t)NO_CHECKSUM)) { err_print("Defect file is corrupted.\n"); status = -1; goto out; } /* * Kill off any old defects in the working list. */ kill_deflist(&work_list); /* * Load the working list header with the header info. */ if (magicno == NO_CHECKSUM) work_list.header.magicno = (uint_t)DEFECT_MAGIC; else work_list.header.magicno = magicno; work_list.header.count = count; work_list.header.cksum = cksum; /* * Allocate space for the new list. */ work_list.list = (struct defect_entry *)zalloc( deflist_size(cur_blksz, count) * cur_blksz); /* * Mark the working list dirty since we are modifying it. */ work_list.flags |= LIST_DIRTY; /* * Loop through each defect in the defect file. */ for (i = 0; i < count; i++) { dptr = work_list.list + i; /* * Scan the info into the defect entry. */ items = fscanf(fptr, "%*d%hd%hd%d%hd%hd\n", &dptr->cyl, &dptr->head, &dptr->bfi, &dptr->nbits, &dptr->sect); /* * If it didn't scan right, give up. */ if (items != 5) goto bad; } /* * Check to be sure the checksum from the defect file was correct * unless there wasn't supposed to be a checksum. * If there was supposed to be a valid checksum and there isn't * then give up. */ if (magicno != NO_CHECKSUM && checkdefsum(&work_list, CK_CHECKSUM)) goto bad; fmt_print("working list updated, total of %d defects.\n", i); goto out; bad: /* * Some kind of error occurred. Kill off the working list and * mark the status bad. */ err_print("Defect file is corrupted, working list set to NULL.\n"); kill_deflist(&work_list); status = -1; out: /* * Close the defect file. */ (void) fclose(fptr); exit_critical(); fmt_print("\n"); return (status); } /* * This routine implements the 'commit' command. It causes the current * defect list to be set equal to the working defect list. It is the only * way that changes made to the working list can actually take effect in * the next format. */ int d_commit(void) { /* * If the working list wasn't modified, no commit is necessary. */ if (work_list.list != NULL && !(work_list.flags & LIST_DIRTY)) { err_print("working list was not modified.\n"); return (0); } /* * Make sure the user is serious. */ if (check("Ready to update Current Defect List, continue")) return (-1); return (do_commit()); } int do_commit(void) { int status; if ((status = commit_list()) == 0) { /* * Remind the user to format the drive, since changing * the list does nothing unless a format is performed. */ fmt_print(\ "Disk must be reformatted for changes to take effect.\n\n"); } return (status); } static int commit_list(void) { int i; /* * Lock out interrupts so the list doesn't get half copied. */ enter_critical(); /* * Kill off any current defect list. */ kill_deflist(&cur_list); /* * If the working list is null, initialize it to zero length. * This is so the user can do a commit on a null list and get * a zero length list. Otherwise there would be no way to get * a zero length list conveniently. */ if (work_list.list == NULL) { work_list.header.magicno = (uint_t)DEFECT_MAGIC; work_list.header.count = 0; work_list.list = (struct defect_entry *)zalloc( deflist_size(cur_blksz, 0) * cur_blksz); } /* * Copy the working list into the current list. */ cur_list.header = work_list.header; cur_list.list = (struct defect_entry *)zalloc( deflist_size(cur_blksz, cur_list.header.count) * cur_blksz); for (i = 0; i < cur_list.header.count; i++) *(cur_list.list + i) = *(work_list.list + i); /* * Mark the working list clean, since it is now the same as the * current list. Note we do not mark the current list dirty, * even though it has been changed. This is because it does * not reflect the state of disk, so we don't want it written * out until a format has been done. The format will mark it * dirty and write it out. */ work_list.flags &= ~(LIST_DIRTY|LIST_RELOAD); cur_list.flags = work_list.flags; if (EMBEDDED_SCSI) fmt_print("Defect List has a total of %d defects.\n", cur_list.header.count); else fmt_print("Current Defect List updated, total of %d defects.\n", cur_list.header.count); exit_critical(); return (0); } /* * This routine implements the 'create' command. It creates the * manufacturer's defect on the current disk from the defect list */ int d_create(void) { int status; assert(!EMBEDDED_SCSI); /* * If the controller does not support the extraction, we're out * of luck. */ if (cur_ops->op_create == NULL) { err_print("Controller does not support creating "); err_print("manufacturer's defect list.\n"); return (-1); } /* * Make sure the user is serious. Note, for SCSI disks * since this is instantaneous, we will just do it and * not ask for confirmation. */ if (! (cur_ctype->ctype_flags & CF_SCSI) && check( "Ready to create the manufacturers defect information on the disk.\n\ This cannot be interrupted and may take a long while.\n\ IT WILL DESTROY ALL OF THE DATA ON THE DISK! Continue")) return (-1); /* * Lock out interrupts so we don't get half the list. */ enter_critical(); fmt_print("Creating manufacturer's defect list..."); /* * Do the Creation */ status = (*cur_ops->op_create)(&work_list); if (status) { fmt_print("Creation failed.\n\n"); } else { fmt_print("Creation complete.\n"); } exit_critical(); /* * Return status. */ return (status); } /* * Extract primary defect list - SCSI only */ int d_primary(void) { int status; assert(EMBEDDED_SCSI); /* * Lock out interrupts so we don't get half the list and * Kill off the working list. */ enter_critical(); kill_deflist(&work_list); fmt_print("Extracting primary defect list..."); /* * Do the extraction. */ status = scsi_ex_man(&work_list); if (status) { fmt_print("Extraction failed.\n\n"); } else { fmt_print("Extraction complete.\n"); /* * Mark the working list dirty since we modified it. * Automatically commit it, for SCSI only. */ work_list.flags |= LIST_DIRTY; status = commit_list(); fmt_print("\n"); } exit_critical(); /* * Return status. */ return (status); } /* * Extract grown defects list - SCSI only */ int d_grown(void) { int status; assert(EMBEDDED_SCSI); /* * Lock out interrupts so we don't get half the list and * Kill off the working list. */ enter_critical(); kill_deflist(&work_list); fmt_print("Extracting grown defects list..."); /* * Do the extraction. */ status = scsi_ex_grown(&work_list); if (status) { fmt_print("Extraction failed.\n\n"); } else { fmt_print("Extraction complete.\n"); /* * Mark the working list dirty since we modified it. * Automatically commit it, for SCSI only. */ work_list.flags |= LIST_DIRTY; status = commit_list(); fmt_print("\n"); } exit_critical(); /* * Return status. */ return (status); } /* * Extract both primary and grown defects list - SCSI only */ int d_both(void) { int status; assert(EMBEDDED_SCSI); /* * Lock out interrupts so we don't get half the list and * Kill off the working list. */ enter_critical(); kill_deflist(&work_list); fmt_print("Extracting both primary and grown defects lists..."); /* * Do the extraction. */ status = scsi_ex_cur(&work_list); if (status) { fmt_print("Extraction failed.\n\n"); } else { fmt_print("Extraction complete.\n"); /* * Mark the working list dirty since we modified it. * Automatically commit it, for SCSI only. */ work_list.flags |= LIST_DIRTY; status = commit_list(); fmt_print("\n"); } exit_critical(); /* * Return status. */ return (status); } /* * 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 (c) 1991-2001 by Sun Microsystems, Inc. */ #ifndef _MENU_DEFECT_H #define _MENU_DEFECT_H #ifdef __cplusplus extern "C" { #endif /* * Prototypes for ANSI C compilers */ int d_restore(void); int d_original(void); int d_extract(void); int d_add(void); int d_delete(void); int d_print(void); int d_dump(void); int d_load(void); int d_commit(void); int do_commit(void); int d_create(void); int d_primary(void); int d_grown(void); int d_both(void); #ifdef __cplusplus } #endif #endif /* _MENU_DEFECT_H */ /* * 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. */ /* * This file contains functions that implement the fdisk menu commands. */ #include "global.h" #include #include #include #include #include #include #include #include #include "main.h" #include "analyze.h" #include "menu.h" #include "menu_developer.h" #include "param.h" #include "misc.h" #include "label.h" #include "startup.h" #include "partition.h" #include "prompts.h" #include "checkdev.h" #include "io.h" #include "ctlr_scsi.h" #include "auto_sense.h" #include "hardware_structs.h" extern struct menu_item menu_developer[]; int c_developer(void) { cur_menu++; last_menu = cur_menu; /* * Run the menu. */ run_menu(menu_developer, "DEVELOPER", "developer", 0); cur_menu--; return (0); } int dv_disk(void) { struct disk_info *diskp; diskp = disk_list; while (diskp != NULL) { (void) printf("\ndisk_name %s ", diskp->disk_name); (void) printf("disk_path %s\n", diskp->disk_path); (void) printf("ctlr_cname = %s ", diskp->disk_ctlr->ctlr_cname); (void) printf("cltr_dname = %s ", diskp->disk_ctlr->ctlr_dname); (void) printf("ctype_name = %s\n", diskp->disk_ctlr->ctlr_ctype->ctype_name); (void) printf("ctype_ctype = %d\n", diskp->disk_ctlr->ctlr_ctype->ctype_ctype); (void) printf("devfsname = %s\n", diskp->devfs_name); diskp = diskp->disk_next; } return (0); } int dv_cont(void) { struct ctlr_info *contp; contp = ctlr_list; while (contp != NULL) { (void) printf("\nctype_name = %s ", contp->ctlr_ctype->ctype_name); (void) printf("cname = %s dname = %s ", contp->ctlr_cname, contp->ctlr_dname); (void) printf("ctype_ctype = %d\n", contp->ctlr_ctype->ctype_ctype); contp = contp->ctlr_next; } return (0); } int dv_cont_chain(void) { struct mctlr_list *ctlrp; ctlrp = controlp; if (ctlrp == NULL) (void) printf("ctlrp is NULL!!\n"); while (ctlrp != NULL) { (void) printf("ctlrp->ctlr_type->ctype_name = %s\n", ctlrp->ctlr_type->ctype_name); ctlrp = ctlrp->next; } return (0); } int dv_params(void) { (void) printf("ncyl = %d\n", ncyl); (void) printf("acyl = %d\n", acyl); (void) printf("pcyl = %d\n", pcyl); (void) printf("nhead = %d\n", nhead); (void) printf("nsect = %d\n", nsect); return (0); } /* * 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 (c) 1993-2001 by Sun Microsystems, Inc. */ #ifndef _MENU_DEVELOPER_H #define _MENU_DEVELOPER_H #ifdef __cplusplus extern "C" { #endif /* * Prototypes for ANSI */ int c_developer(void); int dv_disk(void); int dv_cont(void); int dv_cont_chain(void); int dv_params(void); #ifdef __cplusplus } #endif #endif /* _MENU_DEVELOPER_H */ /* * 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 (c) 1993, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright 2016 Toomas Soome */ /* * This file contains functions that implement the fdisk menu commands. */ #include "global.h" #include #include #include #include #include #include #include #include #include #include #include #ifdef i386 #include #endif #include "main.h" #include "analyze.h" #include "menu.h" #include "menu_command.h" #include "menu_defect.h" #include "menu_partition.h" #include "menu_fdisk.h" #include "param.h" #include "misc.h" #include "label.h" #include "startup.h" #include "partition.h" #include "prompts.h" #include "checkdev.h" #include "io.h" #include "ctlr_scsi.h" #include "auto_sense.h" extern struct menu_item menu_fdisk[]; struct mboot boot_sec; uint_t xstart; /* * Byte swapping macros for accessing struct ipart * to resolve little endian on Sparc. */ #if defined(sparc) #define les(val) ((((val)&0xFF)<<8)|(((val)>>8)&0xFF)) #define lel(val) (((unsigned)(les((val)&0x0000FFFF))<<16) | \ (les((unsigned)((val)&0xffff0000)>>16))) #elif defined(i386) || defined(__amd64) #define les(val) (val) #define lel(val) (val) #else /* defined(sparc) */ #error No Platform defined #endif /* defined(sparc) */ /* Function prototypes */ #ifdef __STDC__ #if defined(sparc) static int getbyte(uchar_t **); static int getlong(uchar_t **); #endif /* defined(sparc) */ static int get_solaris_part(int fd, struct ipart *ipart); #else /* __STDC__ */ #if defined(sparc) static int getbyte(); static int getlong(); #endif /* defined(sparc) */ static int get_solaris_part(); #endif /* __STDC__ */ #if defined(i386) || defined(__amd64) int extpart_init(ext_part_t **epp); #endif /* * Handling the alignment problem of struct ipart. */ static void fill_ipart(char *bootptr, struct ipart *partp) { #if defined(sparc) /* * Sparc platform: * * Packing short/word for struct ipart to resolve * little endian on Sparc since it is not * properly aligned on Sparc. */ partp->bootid = getbyte((uchar_t **)&bootptr); partp->beghead = getbyte((uchar_t **)&bootptr); partp->begsect = getbyte((uchar_t **)&bootptr); partp->begcyl = getbyte((uchar_t **)&bootptr); partp->systid = getbyte((uchar_t **)&bootptr); partp->endhead = getbyte((uchar_t **)&bootptr); partp->endsect = getbyte((uchar_t **)&bootptr); partp->endcyl = getbyte((uchar_t **)&bootptr); partp->relsect = getlong((uchar_t **)&bootptr); partp->numsect = getlong((uchar_t **)&bootptr); #elif defined(i386) || defined(__amd64) /* * i386/amd64 platform: * * The fdisk table does not begin on a 4-byte boundary within * the master boot record; so, we need to recopy its contents * to another data structure to avoid an alignment exception. */ (void) bcopy(bootptr, partp, sizeof (struct ipart)); #else #error No Platform defined #endif /* defined(sparc) */ } /* * Get a correct byte/short/word routines for Sparc platform. */ #if defined(sparc) static int getbyte(uchar_t **bp) { int b; b = **bp; *bp = *bp + 1; return (b); } #ifdef DEADCODE static int getshort(uchar_t **bp) { int b; b = ((**bp) << 8) | *(*bp + 1); *bp += 2; return (b); } #endif /* DEADCODE */ static int getlong(uchar_t **bp) { int b, bh, bl; bh = ((**bp) << 8) | *(*bp + 1); *bp += 2; bl = ((**bp) << 8) | *(*bp + 1); *bp += 2; b = (bh << 16) | bl; return (b); } #endif /* defined(sparc) */ #ifdef i386 /* * Convert emcpowerN[a-p,p0,p1,p2,p3,p4] to emcpowerNp0 path, * this is specific for emc powerpath driver. */ static void get_emcpower_pname(char *name, char *devname) { char *emcp = "emcpower"; char *npt = NULL; char np[MAXNAMELEN]; int i = strlen(emcp); (void) strcpy(np, devname); npt = strstr(np, emcp); while ((i < strlen(npt)) && (isdigit(npt[i]))) i++; npt[i] = '\0'; (void) snprintf(name, MAXNAMELEN, "/dev/rdsk/%sp0", npt); } #endif /* * Convert cn[tn]dn to cn[tn]dns2 path */ static void get_sname(char *name) { char buf[MAXPATHLEN]; char *devp = "/dev/dsk"; char *rdevp = "/dev/rdsk"; char np[MAXNAMELEN]; char *npt; #ifdef i386 if (emcpower_name(cur_disk->disk_name)) { get_emcpower_pname(name, cur_disk->disk_name); return; } #endif /* * If it is a full path /dev/[r]dsk/cn[tn]dn, use this path */ (void) strcpy(np, cur_disk->disk_name); if (strncmp(rdevp, cur_disk->disk_name, strlen(rdevp)) == 0 || strncmp(devp, cur_disk->disk_name, strlen(devp)) == 0) { /* * Skip if the path is already included with sN */ if (strchr(np, 's') == strrchr(np, 's')) { npt = strrchr(np, 'p'); /* If pN is found, do not include it */ if (npt != NULL) { *npt = '\0'; } (void) snprintf(buf, sizeof (buf), "%ss2", np); } else { (void) snprintf(buf, sizeof (buf), "%s", np); } } else { (void) snprintf(buf, sizeof (buf), "/dev/rdsk/%ss2", np); } (void) strcpy(name, buf); } /* * Convert cn[tn]dnsn to cn[tn]dnp0 path */ static void get_pname(char *name) { char buf[MAXPATHLEN]; char *devp = "/dev/dsk"; char *rdevp = "/dev/rdsk"; char np[MAXNAMELEN]; char *npt; /* * If it is a full path /dev/[r]dsk/cn[tn]dnsn, use this path */ if (cur_disk == NULL) { (void) strcpy(np, x86_devname); } else { (void) strcpy(np, cur_disk->disk_name); } #ifdef i386 if (emcpower_name(np)) { get_emcpower_pname(name, np); return; } #endif if (strncmp(rdevp, np, strlen(rdevp)) == 0 || strncmp(devp, np, strlen(devp)) == 0) { /* * Skip if the path is already included with pN */ if (strchr(np, 'p') == NULL) { npt = strrchr(np, 's'); /* If sN is found, do not include it */ if (isdigit(*++npt)) { *--npt = '\0'; } (void) snprintf(buf, sizeof (buf), "%sp0", np); } else { (void) snprintf(buf, sizeof (buf), "%s", np); } } else { (void) snprintf(buf, sizeof (buf), "/dev/rdsk/%sp0", np); } (void) strcpy(name, buf); } /* * Open file descriptor for current disk (cur_file) * with "p0" path or cur_disk->disk_path */ void open_cur_file(int mode) { char *dkpath; char pbuf[MAXPATHLEN]; switch (mode) { case FD_USE_P0_PATH: (void) get_pname(&pbuf[0]); dkpath = pbuf; break; case FD_USE_CUR_DISK_PATH: if (cur_disk->fdisk_part.systid == SUNIXOS || cur_disk->fdisk_part.systid == SUNIXOS2) { (void) get_sname(&pbuf[0]); dkpath = pbuf; } else { dkpath = cur_disk->disk_path; } break; default: err_print("Error: Invalid mode option for opening " "cur_file\n"); fullabort(); } /* Close previous cur_file */ (void) close(cur_file); /* Open cur_file with the required path dkpath */ if ((cur_file = open_disk(dkpath, O_RDWR | O_NDELAY)) < 0) { err_print( "Error: can't open selected disk '%s'.\n", dkpath); fullabort(); } } /* * This routine implements the 'fdisk' command. It simply runs * the fdisk command on the current disk. * Use of this is restricted to interactive mode only. */ int c_fdisk(void) { char buf[MAXPATHLEN]; char pbuf[MAXPATHLEN]; struct stat statbuf; /* * We must be in interactive mode to use the fdisk command */ if (option_f != NULL || isatty(0) != 1 || isatty(1) != 1) { err_print("Fdisk command is for interactive use only!\n"); return (-1); } /* * There must be a current disk type and a current disk */ if (cur_dtype == NULL) { err_print("Current Disk Type is not set.\n"); return (-1); } /* * Before running the fdisk command, get file status of * /dev/rdsk/cn[tn]dnp0 path to see if this disk * supports fixed disk partition table. */ (void) get_pname(&pbuf[0]); if (stat(pbuf, (struct stat *)&statbuf) == -1 || !S_ISCHR(statbuf.st_mode)) { err_print( "Disk does not support fixed disk partition table\n"); return (0); } /* * Run the fdisk program. */ (void) snprintf(buf, sizeof (buf), "fdisk %s\n", pbuf); (void) system(buf); /* * Open cur_file with "p0" path for accessing the fdisk table */ (void) open_cur_file(FD_USE_P0_PATH); /* * Get solaris partition information in the fdisk partition table */ if (get_solaris_part(cur_file, &cur_disk->fdisk_part) == -1) { err_print("No fdisk solaris partition found\n"); cur_disk->fdisk_part.numsect = 0; /* No Solaris */ } /* * Restore cur_file with cur_disk->disk_path */ (void) open_cur_file(FD_USE_CUR_DISK_PATH); return (0); } /* * Read MBR on the disk * if the Solaris partition has changed, * reread the vtoc */ #ifdef DEADCODE static void update_cur_parts(void) { int i; register struct partition_info *parts; for (i = 0; i < NDKMAP; i++) { #if defined(_SUNOS_VTOC_16) if (cur_parts->vtoc.v_part[i].p_tag && cur_parts->vtoc.v_part[i].p_tag != V_ALTSCTR) { cur_parts->vtoc.v_part[i].p_start = 0; cur_parts->vtoc.v_part[i].p_size = 0; #endif cur_parts->pinfo_map[i].dkl_nblk = 0; cur_parts->pinfo_map[i].dkl_cylno = 0; cur_parts->vtoc.v_part[i].p_tag = default_vtoc_map[i].p_tag; cur_parts->vtoc.v_part[i].p_flag = default_vtoc_map[i].p_flag; #if defined(_SUNOS_VTOC_16) } #endif } cur_parts->pinfo_map[C_PARTITION].dkl_nblk = ncyl * spc(); #if defined(_SUNOS_VTOC_16) /* * Adjust for the boot partitions */ cur_parts->pinfo_map[I_PARTITION].dkl_nblk = spc(); cur_parts->pinfo_map[I_PARTITION].dkl_cylno = 0; cur_parts->vtoc.v_part[C_PARTITION].p_start = cur_parts->pinfo_map[C_PARTITION].dkl_cylno * nhead * nsect; cur_parts->vtoc.v_part[C_PARTITION].p_size = cur_parts->pinfo_map[C_PARTITION].dkl_nblk; cur_parts->vtoc.v_part[I_PARTITION].p_start = cur_parts->pinfo_map[I_PARTITION].dkl_cylno; cur_parts->vtoc.v_part[I_PARTITION].p_size = cur_parts->pinfo_map[I_PARTITION].dkl_nblk; #endif /* defined(_SUNOS_VTOC_16) */ parts = cur_dtype->dtype_plist; cur_dtype->dtype_ncyl = ncyl; cur_dtype->dtype_plist = cur_parts; parts->pinfo_name = cur_parts->pinfo_name; cur_disk->disk_parts = cur_parts; cur_ctype->ctype_dlist = cur_dtype; } #endif /* DEADCODE */ static int get_solaris_part(int fd, struct ipart *ipart) { int i; struct ipart ip; int status; char *mbr; char *bootptr; struct dk_label update_label; ushort_t found = 0; #ifdef i386 uint32_t relsec, numsec; int pno, rval, ext_part_found = 0; ext_part_t *epp; #endif (void) lseek(fd, 0, 0); /* * We may get mbr of different size, but the first 512 bytes * are valid information. */ mbr = malloc(cur_blksz); if (mbr == NULL) { err_print("No memory available.\n"); return (-1); } status = read(fd, mbr, cur_blksz); if (status != cur_blksz) { err_print("Bad read of fdisk partition. Status = %x\n", status); err_print("Cannot read fdisk partition information.\n"); free(mbr); return (-1); } (void) memcpy(&boot_sec, mbr, sizeof (struct mboot)); free(mbr); #ifdef i386 (void) extpart_init(&epp); #endif for (i = 0; i < FD_NUMPART; i++) { int ipc; ipc = i * sizeof (struct ipart); /* Handling the alignment problem of struct ipart */ bootptr = &boot_sec.parts[ipc]; (void) fill_ipart(bootptr, &ip); #ifdef i386 if (fdisk_is_dos_extended(ip.systid) && (ext_part_found == 0)) { /* We support only one extended partition per disk */ ext_part_found = 1; rval = fdisk_get_solaris_part(epp, &pno, &relsec, &numsec); if (rval == FDISK_SUCCESS) { /* * Found a solaris partition inside the * extended partition. Update the statistics. */ if (nhead != 0 && nsect != 0) { pcyl = numsec / (nhead * nsect); xstart = relsec / (nhead * nsect); ncyl = pcyl - acyl; } solaris_offset = relsec; found = 2; ip.bootid = 0; ip.beghead = ip.begsect = ip.begcyl = 0xff; ip.endhead = ip.endsect = ip.endcyl = 0xff; ip.systid = SUNIXOS2; ip.relsect = relsec; ip.numsect = numsec; ipart->bootid = ip.bootid; status = bcmp(&ip, ipart, sizeof (struct ipart)); bcopy(&ip, ipart, sizeof (struct ipart)); } continue; } #endif /* * we are interested in Solaris and EFI partition types */ #ifdef i386 if ((ip.systid == SUNIXOS && (fdisk_is_linux_swap(epp, lel(ip.relsect), NULL) != 0)) || ip.systid == SUNIXOS2 || ip.systid == EFI_PMBR) { #else if (ip.systid == SUNIXOS || ip.systid == SUNIXOS2 || ip.systid == EFI_PMBR) { #endif /* * if the disk has an EFI label, nhead and nsect may * be zero. This test protects us from FPE's, and * format still seems to work fine */ if (nhead != 0 && nsect != 0) { pcyl = lel(ip.numsect) / (nhead * nsect); xstart = lel(ip.relsect) / (nhead * nsect); ncyl = pcyl - acyl; } #ifdef DEBUG else { err_print("Critical geometry values are zero:\n" "\tnhead = %d; nsect = %d\n", nhead, nsect); } #endif /* DEBUG */ solaris_offset = (uint_t)lel(ip.relsect); found = 1; break; } } #ifdef i386 libfdisk_fini(&epp); #endif if (!found) { err_print("Solaris fdisk partition not found\n"); return (-1); } else if (found == 1) { /* * Found a primary solaris partition. * compare the previous and current Solaris partition * but don't use bootid in determination of Solaris partition * changes */ ipart->bootid = ip.bootid; status = bcmp(&ip, ipart, sizeof (struct ipart)); bcopy(&ip, ipart, sizeof (struct ipart)); } /* if the disk partitioning has changed - get the VTOC */ if (status) { struct extvtoc exvtoc; struct vtoc vtoc; status = ioctl(fd, DKIOCGEXTVTOC, &exvtoc); if (status == -1) { i = errno; /* Try the old ioctl DKIOCGVTOC */ status = ioctl(fd, DKIOCGVTOC, &vtoc); if (status == -1) { err_print("Bad ioctl DKIOCGEXTVTOC.\n"); err_print("errno=%d %s\n", i, strerror(i)); err_print("Cannot read vtoc information.\n"); return (-1); } } status = read_label(fd, &update_label); if (status == -1) { err_print("Cannot read label information.\n"); return (-1); } /* copy vtoc information */ cur_parts->vtoc = update_label.dkl_vtoc; #if defined(_SUNOS_VTOC_16) /* * this is to update the slice table on x86 * we don't care about VTOC8 here */ for (i = 0; i < NDKMAP; i ++) { cur_parts->pinfo_map[i].dkl_cylno = update_label.dkl_vtoc.v_part[i].p_start / ((int)(update_label.dkl_nhead * update_label.dkl_nsect)); cur_parts->pinfo_map[i].dkl_nblk = update_label.dkl_vtoc.v_part[i].p_size; } #endif /* defined(_SUNOS_VTOC_16) */ cur_dtype->dtype_ncyl = update_label.dkl_ncyl; cur_dtype->dtype_pcyl = update_label.dkl_pcyl; cur_dtype->dtype_acyl = update_label.dkl_acyl; cur_dtype->dtype_nhead = update_label.dkl_nhead; cur_dtype->dtype_nsect = update_label.dkl_nsect; ncyl = cur_dtype->dtype_ncyl; acyl = cur_dtype->dtype_acyl; pcyl = cur_dtype->dtype_pcyl; nsect = cur_dtype->dtype_nsect; nhead = cur_dtype->dtype_nhead; } return (0); } int copy_solaris_part(struct ipart *ipart) { int status, i, fd; struct mboot mboot; char *mbr; struct ipart ip; char buf[MAXPATHLEN]; char *bootptr; struct stat statbuf; #ifdef i386 uint32_t relsec, numsec; int pno, rval, ext_part_found = 0; ext_part_t *epp; #endif (void) get_pname(&buf[0]); if (stat(buf, &statbuf) == -1 || !S_ISCHR(statbuf.st_mode) || ((cur_label == L_TYPE_EFI) && (cur_disk->disk_flags & DSK_LABEL_DIRTY))) { /* * Make sure to reset solaris_offset to zero if it is * previously set by a selected disk that * supports the fdisk table. */ solaris_offset = 0; /* * Return if this disk does not support fdisk table or * if it uses an EFI label but has not yet been labelled. * If the EFI label has not been written then the open * on the partition will fail. */ return (0); } if ((fd = open(buf, O_RDONLY)) < 0) { /* * Labeled device support in lofi provides p0 partition on * both x86 and sparc. However, sparc does not implement fdisk * partitioning. This workaround will allow format * to ignore fdisk errors in case of lofi devices. */ if (strcmp(cur_disk->disk_dkinfo.dki_cname, "lofi") == 0) { solaris_offset = 0; return (0); } err_print("Error: can't open disk '%s'.\n", buf); return (-1); } /* * We may get mbr of different size, but the first 512 bytes * are valid information. */ mbr = malloc(cur_blksz); if (mbr == NULL) { err_print("No memory available.\n"); return (-1); } status = read(fd, mbr, cur_blksz); if (status != cur_blksz) { err_print("Bad read of fdisk partition.\n"); (void) close(fd); free(mbr); return (-1); } (void) memcpy(&mboot, mbr, sizeof (struct mboot)); #ifdef i386 (void) extpart_init(&epp); #endif for (i = 0; i < FD_NUMPART; i++) { int ipc; ipc = i * sizeof (struct ipart); /* Handling the alignment problem of struct ipart */ bootptr = &mboot.parts[ipc]; (void) fill_ipart(bootptr, &ip); #ifdef i386 if (fdisk_is_dos_extended(ip.systid) && (ext_part_found == 0)) { /* We support only one extended partition per disk */ ext_part_found = 1; rval = fdisk_get_solaris_part(epp, &pno, &relsec, &numsec); if (rval == FDISK_SUCCESS) { /* * Found a solaris partition inside the * extended partition. Update the statistics. */ if (nhead != 0 && nsect != 0) { pcyl = numsec / (nhead * nsect); ncyl = pcyl - acyl; } solaris_offset = relsec; ip.bootid = 0; ip.beghead = ip.begsect = ip.begcyl = 0xff; ip.endhead = ip.endsect = ip.endcyl = 0xff; ip.systid = SUNIXOS2; ip.relsect = relsec; ip.numsect = numsec; bcopy(&ip, ipart, sizeof (struct ipart)); } continue; } #endif #ifdef i386 if ((ip.systid == SUNIXOS && (fdisk_is_linux_swap(epp, lel(ip.relsect), NULL) != 0)) || ip.systid == SUNIXOS2 || ip.systid == EFI_PMBR) { #else if (ip.systid == SUNIXOS || ip.systid == SUNIXOS2 || ip.systid == EFI_PMBR) { #endif solaris_offset = lel(ip.relsect); bcopy(&ip, ipart, sizeof (struct ipart)); /* * if the disk has an EFI label, we typically won't * have values for nhead and nsect. format seems to * work without them, and we need to protect ourselves * from FPE's */ if (nhead != 0 && nsect != 0) { pcyl = lel(ip.numsect) / (nhead * nsect); ncyl = pcyl - acyl; } #ifdef DEBUG else { err_print("Critical geometry values are zero:\n" "\tnhead = %d; nsect = %d\n", nhead, nsect); } #endif /* DEBUG */ break; } } #ifdef i386 libfdisk_fini(&epp); #endif (void) close(fd); free(mbr); return (0); } #if defined(_FIRMWARE_NEEDS_FDISK) int auto_solaris_part(struct dk_label *label) { int status, i, fd; struct mboot mboot; char *mbr; struct ipart ip; char *bootptr; char pbuf[MAXPATHLEN]; #ifdef i386 uint32_t relsec, numsec; int pno, rval, ext_part_found = 0; ext_part_t *epp; #endif (void) get_pname(&pbuf[0]); if ((fd = open_disk(pbuf, O_RDONLY)) < 0) { err_print("Error: can't open selected disk '%s'.\n", pbuf); return (-1); } /* * We may get mbr of different size, but the first 512 bytes * are valid information. */ mbr = malloc(cur_blksz); if (mbr == NULL) { err_print("No memory available.\n"); return (-1); } status = read(fd, mbr, cur_blksz); if (status != cur_blksz) { err_print("Bad read of fdisk partition.\n"); free(mbr); return (-1); } (void) memcpy(&mboot, mbr, sizeof (struct mboot)); #ifdef i386 (void) extpart_init(&epp); #endif for (i = 0; i < FD_NUMPART; i++) { int ipc; ipc = i * sizeof (struct ipart); /* Handling the alignment problem of struct ipart */ bootptr = &mboot.parts[ipc]; (void) fill_ipart(bootptr, &ip); #ifdef i386 if (fdisk_is_dos_extended(ip.systid) && (ext_part_found == 0)) { /* We support only one extended partition per disk */ ext_part_found = 1; rval = fdisk_get_solaris_part(epp, &pno, &relsec, &numsec); if (rval == FDISK_SUCCESS) { /* * Found a solaris partition inside the * extended partition. Update the statistics. */ if ((label->dkl_nhead != 0) && (label->dkl_nsect != 0)) { label->dkl_pcyl = numsec / (label->dkl_nhead * label->dkl_nsect); label->dkl_ncyl = label->dkl_pcyl - label->dkl_acyl; } solaris_offset = relsec; } continue; } #endif /* * if the disk has an EFI label, the nhead and nsect fields * the label may be zero. This protects us from FPE's, and * format still seems to work happily */ #ifdef i386 if ((ip.systid == SUNIXOS && (fdisk_is_linux_swap(epp, lel(ip.relsect), NULL) != 0)) || ip.systid == SUNIXOS2 || ip.systid == EFI_PMBR) { #else if (ip.systid == SUNIXOS || ip.systid == SUNIXOS2 || ip.systid == EFI_PMBR) { #endif if ((label->dkl_nhead != 0) && (label->dkl_nsect != 0)) { label->dkl_pcyl = lel(ip.numsect) / (label->dkl_nhead * label->dkl_nsect); label->dkl_ncyl = label->dkl_pcyl - label->dkl_acyl; } #ifdef DEBUG else { err_print("Critical label fields aren't " "non-zero:\n" "\tlabel->dkl_nhead = %d; " "label->dkl_nsect = " "%d\n", label->dkl_nhead, label->dkl_nsect); } #endif /* DEBUG */ solaris_offset = lel(ip.relsect); break; } } #ifdef i386 libfdisk_fini(&epp); #endif (void) close(fd); free(mbr); return (0); } #endif /* defined(_FIRMWARE_NEEDS_FDISK) */ int good_fdisk(void) { char buf[MAXPATHLEN]; struct stat statbuf; (void) get_pname(&buf[0]); if (stat(buf, &statbuf) == -1 || !S_ISCHR(statbuf.st_mode) || cur_label == L_TYPE_EFI) { /* * Return if this disk does not support fdisk table or * if the disk is labeled with EFI. */ return (1); } if (lel(cur_disk->fdisk_part.numsect) > 0) { return (1); } else { /* * Labeled device support in lofi provides p0 partition on * both x86 and sparc. However, sparc does not implement fdisk * partitioning. This workaround will allow format * to ignore fdisk errors in case of lofi devices. */ if (strcmp(cur_disk->disk_dkinfo.dki_cname, "lofi") == 0) { return (1); } err_print("WARNING - "); err_print("This disk may be in use by an application " "that has\n\t modified the fdisk table. Ensure " "that this disk is\n\t not currently in use " "before proceeding to use fdisk.\n"); return (0); } } #ifdef i386 int extpart_init(ext_part_t **epp) { int rval, lf_op_flag = 0; char p0_path[MAXPATHLEN]; get_pname(&p0_path[0]); lf_op_flag |= FDISK_READ_DISK; if ((rval = libfdisk_init(epp, p0_path, NULL, lf_op_flag)) != FDISK_SUCCESS) { switch (rval) { /* * FDISK_EBADLOGDRIVE, FDISK_ENOLOGDRIVE * and FDISK_EBADMAGIC can be considered * as soft errors and hence we do not exit. */ case FDISK_EBADLOGDRIVE: break; case FDISK_ENOLOGDRIVE: break; case FDISK_EBADMAGIC: break; case FDISK_ENOVGEOM: err_print("Could not get virtual geometry for" " this device\n"); fullabort(); break; case FDISK_ENOPGEOM: err_print("Could not get physical geometry for" " this device\n"); fullabort(); break; case FDISK_ENOLGEOM: err_print("Could not get label geometry for " " this device\n"); fullabort(); break; default: err_print("Failed to initialise libfdisk.\n"); fullabort(); break; } } return (0); } #endif /* * 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 (c) 1993-2001 by Sun Microsystems, Inc. * All rights reserved. */ #ifndef _MENU_FDISK_H #define _MENU_FDISK_H #ifdef __cplusplus extern "C" { #endif /* * Prototypes for ANSI */ int copy_solaris_part(struct ipart *ipart); void open_cur_file(int); int auto_solaris_part(struct dk_label *); /* * These flags are used to open file descriptor for current * disk (cur_file) with "p0" path or cur_disk->disk_path */ #define FD_USE_P0_PATH 0 #define FD_USE_CUR_DISK_PATH 1 #ifdef __cplusplus } #endif #endif /* _MENU_FDISK_H */ /* * 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 (c) 1991, 2010, Oracle and/or its affiliates. All rights reserved. */ /* * This file contains functions to implement the partition menu commands. */ #include "global.h" #include #include #include "partition.h" #include "menu_partition.h" #include "menu_command.h" #include "misc.h" #include "param.h" static void nspaces(int); static int ndigits(uint64_t); /* * This routine implements the 'a' command. It changes the 'a' partition. */ int p_apart(void) { change_partition(0); return (0); } /* * This routine implements the 'b' command. It changes the 'b' partition. */ int p_bpart(void) { change_partition(1); return (0); } /* * This routine implements the 'c' command. It changes the 'c' partition. */ int p_cpart(void) { change_partition(2); return (0); } /* * This routine implements the 'd' command. It changes the 'd' partition. */ int p_dpart(void) { change_partition(3); return (0); } /* * This routine implements the 'e' command. It changes the 'e' partition. */ int p_epart(void) { change_partition(4); return (0); } /* * This routine implements the 'f' command. It changes the 'f' partition. */ int p_fpart(void) { change_partition(5); return (0); } /* * This routine implements the 'g' command. It changes the 'g' partition. */ int p_gpart(void) { change_partition(6); return (0); } /* * This routine implements the 'h' command. It changes the 'h' partition. */ int p_hpart(void) { change_partition(7); return (0); } /* * This routine implements the 'i' command. It is valid only for EFI * labeled disks. This can be used only in expert mode. */ int p_ipart(void) { change_partition(8); return (0); } #if defined(i386) /* * This routine implements the 'j' command. It changes the 'j' partition. */ int p_jpart(void) { change_partition(9); return (0); } #endif /* defined(i386) */ int p_expand(void) { uint64_t delta; uint_t nparts; struct dk_gpt *efi_label = cur_parts->etoc; if (cur_parts->etoc->efi_altern_lba == 1 || (cur_parts->etoc->efi_altern_lba >= cur_parts->etoc->efi_last_lba)) { err_print("Warning: No expanded capacity is found.\n"); return (0); } delta = efi_label->efi_last_lba - efi_label->efi_altern_lba; nparts = efi_label->efi_nparts; enter_critical(); efi_label->efi_parts[nparts - 1].p_start += delta; efi_label->efi_last_u_lba += delta; efi_label->efi_altern_lba = cur_parts->etoc->efi_last_lba; exit_critical(); fmt_print("The expanded capacity is added to the unallocated space.\n"); return (0); } /* * This routine implements the 'select' command. It allows the user * to make a pre-defined partition map the current map. */ int p_select(void) { struct partition_info *pptr, *parts; u_ioparam_t ioparam; int i, index, deflt, *defltptr = NULL; blkaddr_t b_cylno; #if defined(i386) blkaddr_t cyl_offset; #endif parts = cur_dtype->dtype_plist; /* * If there are no pre-defined maps for this disk type, it's * an error. */ if (parts == NULL) { err_print("No defined partition tables.\n"); return (-1); } /* * Loop through the pre-defined maps and list them by name. If * the current map is one of them, make it the default. If any * the maps are unnamed, label them as such. */ for (i = 0, pptr = parts; pptr != NULL; pptr = pptr->pinfo_next) { if (cur_parts == pptr) { deflt = i; defltptr = &deflt; } if (pptr->pinfo_name == NULL) fmt_print(" %d. unnamed\n", i++); else fmt_print(" %d. %s\n", i++, pptr->pinfo_name); } ioparam.io_bounds.lower = 0; ioparam.io_bounds.upper = i - 1; /* * Ask which map should be made current. */ index = input(FIO_INT, "Specify table (enter its number)", ':', &ioparam, defltptr, DATA_INPUT); for (i = 0, pptr = parts; i < index; i++, pptr = pptr->pinfo_next) ; if (cur_label == L_TYPE_EFI) { enter_critical(); cur_disk->disk_parts = cur_parts = pptr; exit_critical(); fmt_print("\n"); return (0); } #if defined(i386) /* * Adjust for the boot and alternate sectors partition - assuming that * the alternate sectors partition physical location follows * immediately the boot partition and partition sizes are * expressed in multiple of cylinder size. */ cyl_offset = pptr->pinfo_map[I_PARTITION].dkl_cylno + 1; if (pptr->pinfo_map[J_PARTITION].dkl_nblk != 0) { cyl_offset = pptr->pinfo_map[J_PARTITION].dkl_cylno + ((pptr->pinfo_map[J_PARTITION].dkl_nblk + (spc() - 1)) / spc()); } #else /* !defined(i386) */ b_cylno = 0; #endif /* defined(i386) */ /* * Before we blow the current map away, do some limits checking. */ for (i = 0; i < NDKMAP; i++) { #if defined(i386) if (i == I_PARTITION || i == J_PARTITION || i == C_PARTITION) { b_cylno = 0; } else if (pptr->pinfo_map[i].dkl_nblk == 0) { /* * Always accept starting cyl 0 if the size is 0 also */ b_cylno = 0; } else { b_cylno = cyl_offset; } #endif /* defined(i386) */ if (pptr->pinfo_map[i].dkl_cylno < b_cylno || pptr->pinfo_map[i].dkl_cylno > (ncyl-1)) { err_print("partition %c: starting cylinder %d is out " "of range\n", (PARTITION_BASE + i), pptr->pinfo_map[i].dkl_cylno); return (0); } if (pptr->pinfo_map[i].dkl_nblk > ((ncyl - pptr->pinfo_map[i].dkl_cylno) * spc())) { err_print( "partition %c: specified # of blocks, %u, " "is out of range\n", (PARTITION_BASE+i), pptr->pinfo_map[i].dkl_nblk); return (0); } } /* * Lock out interrupts so the lists don't get mangled. */ enter_critical(); /* * If the old current map is unnamed, delete it. */ if (cur_parts != NULL && cur_parts != pptr && cur_parts->pinfo_name == NULL) delete_partition(cur_parts); /* * Make the selected map current. */ cur_disk->disk_parts = cur_parts = pptr; #if defined(_SUNOS_VTOC_16) for (i = 0; i < NDKMAP; i++) { cur_parts->vtoc.v_part[i].p_start = (blkaddr_t)(cur_parts->pinfo_map[i].dkl_cylno * (nhead * nsect)); cur_parts->vtoc.v_part[i].p_size = (blkaddr_t)cur_parts->pinfo_map[i].dkl_nblk; } #endif /* defined(_SUNOS_VTOC_16) */ exit_critical(); fmt_print("\n"); return (0); } /* * This routine implements the 'name' command. It allows the user * to name the current partition map. If the map was already named, * the name is changed. Once a map is named, the values of the partitions * cannot be changed. Attempts to change them will cause another map * to be created. */ int p_name(void) { char *name; /* * check if there exists a partition table for the disk. */ if (cur_parts == NULL) { err_print("Current Disk has no partition table.\n"); return (-1); } /* * Ask for the name. Note that the input routine will malloc * space for the name since we are using the OSTR input type. */ name = (char *)(uintptr_t)input(FIO_OSTR, "Enter table name (remember quotes)", ':', NULL, NULL, DATA_INPUT); /* * Lock out interrupts. */ enter_critical(); /* * If it was already named, destroy the old name. */ if (cur_parts->pinfo_name != NULL) destroy_data(cur_parts->pinfo_name); /* * Set the name. */ cur_parts->pinfo_name = name; exit_critical(); fmt_print("\n"); return (0); } /* * This routine implements the 'print' command. It lists the values * for all the partitions in the current partition map. */ int p_print(void) { /* * check if there exists a partition table for the disk. */ if (cur_parts == NULL) { err_print("Current Disk has no partition table.\n"); return (-1); } /* * Print the volume name, if it appears to be set */ if (chk_volname(cur_disk)) { fmt_print("Volume: "); print_volname(cur_disk); fmt_print("\n"); } /* * Print the name of the current map. */ if ((cur_parts->pinfo_name != NULL) && (cur_label == L_TYPE_SOLARIS)) { fmt_print("Current partition table (%s):\n", cur_parts->pinfo_name); fmt_print("Total disk cylinders available: %d + %d " "(reserved cylinders)\n\n", ncyl, acyl); } else if (cur_label == L_TYPE_SOLARIS) { fmt_print("Current partition table (unnamed):\n"); fmt_print("Total disk cylinders available: %d + %d " "(reserved cylinders)\n\n", ncyl, acyl); } else if (cur_label == L_TYPE_EFI) { unsigned reserved; reserved = efi_reserved_sectors(cur_parts->etoc); fmt_print("Current partition table (%s):\n", cur_parts->pinfo_name != NULL ? cur_parts->pinfo_name : "unnamed"); fmt_print("Total disk sectors available: %llu + %u " "(reserved sectors)\n\n", cur_parts->etoc->efi_last_u_lba - reserved - cur_parts->etoc->efi_first_u_lba + 1, reserved); } /* * Print the partition map itself */ print_map(cur_parts); return (0); } /* * Print a partition map */ void print_map(struct partition_info *map) { int i; int want_header; struct dk_gpt *vtoc64; if (cur_label == L_TYPE_EFI) { vtoc64 = map->etoc; want_header = 1; for (i = 0; i < vtoc64->efi_nparts; i++) { /* * we want to print partitions above 7 in expert mode only * or if the partition is reserved */ if (i >= 7 && !expert_mode && ((int)vtoc64->efi_parts[i].p_tag != V_RESERVED)) { continue; } print_efi_partition(vtoc64, i, want_header); want_header = 0; } fmt_print("\n"); return; } /* * Loop through each partition, printing the header * the first time. */ want_header = 1; for (i = 0; i < NDKMAP; i++) { if (i > 9) { break; } print_partition(map, i, want_header); want_header = 0; } fmt_print("\n"); } /* * Print out one line of partition information, * with optional header for EFI type disks. */ /*ARGSUSED*/ void print_efi_partition(struct dk_gpt *map, int partnum, int want_header) { int ncyl2_digits = 0; float scaled; char *s; uint64_t secsize; ncyl2_digits = ndigits(map->efi_last_u_lba); if (want_header) { fmt_print("Part "); fmt_print("Tag Flag "); fmt_print("First Sector"); nspaces(ncyl2_digits); fmt_print("Size"); nspaces(ncyl2_digits); fmt_print("Last Sector\n"); } fmt_print(" %d ", partnum); s = find_string(ptag_choices, (int)map->efi_parts[partnum].p_tag); if (s == NULL) s = "-"; nspaces(10 - (int)strlen(s)); fmt_print("%s", s); s = find_string(pflag_choices, (int)map->efi_parts[partnum].p_flag); if (s == NULL) s = "-"; nspaces(6 - (int)strlen(s)); fmt_print("%s", s); nspaces(2); secsize = map->efi_parts[partnum].p_size; if (secsize == 0) { fmt_print("%16llu", map->efi_parts[partnum].p_start); nspaces(ncyl2_digits); fmt_print(" 0 "); } else { fmt_print("%16llu", map->efi_parts[partnum].p_start); scaled = bn2mb(secsize); nspaces(ncyl2_digits - 5); if (scaled >= (float)1024.0 * 1024) { fmt_print("%8.2fTB", scaled/((float)1024.0 * 1024)); } else if (scaled >= (float)1024.0) { fmt_print("%8.2fGB", scaled/(float)1024.0); } else { fmt_print("%8.2fMB", scaled); } } nspaces(ncyl2_digits); if ((map->efi_parts[partnum].p_start + secsize - 1) == UINT_MAX64) { fmt_print(" 0 \n"); } else { fmt_print(" %llu \n", map->efi_parts[partnum].p_start + secsize - 1); } } /* * Print out one line of partition information, * with optional header. */ /*ARGSUSED*/ void print_partition(struct partition_info *pinfo, int partnum, int want_header) { int i; blkaddr_t nblks; int cyl1; int cyl2; float scaled; int maxcyl2; int ncyl2_digits; char *s; blkaddr_t maxnblks = 0; blkaddr_t len; /* * To align things nicely, we need to know the maximum * width of the number of cylinders field. */ maxcyl2 = 0; for (i = 0; i < NDKMAP; i++) { nblks = (uint_t)pinfo->pinfo_map[i].dkl_nblk; cyl1 = pinfo->pinfo_map[i].dkl_cylno; cyl2 = cyl1 + (nblks / spc()) - 1; if (nblks > 0) { maxcyl2 = max(cyl2, maxcyl2); maxnblks = max(nblks, maxnblks); } } /* * Get the number of digits required */ ncyl2_digits = ndigits(maxcyl2); /* * Print the header, if necessary */ if (want_header) { fmt_print("Part "); fmt_print("Tag Flag "); fmt_print("Cylinders"); nspaces(ncyl2_digits); fmt_print(" Size Blocks\n"); } /* * Print the partition information */ nblks = pinfo->pinfo_map[partnum].dkl_nblk; cyl1 = pinfo->pinfo_map[partnum].dkl_cylno; cyl2 = cyl1 + (nblks / spc()) - 1; fmt_print(" %x ", partnum); /* * Print the partition tag. If invalid, print - */ s = find_string(ptag_choices, (int)pinfo->vtoc.v_part[partnum].p_tag); if (s == NULL) s = "-"; nspaces(10 - (int)strlen(s)); fmt_print("%s", s); /* * Print the partition flag. If invalid print - */ s = find_string(pflag_choices, (int)pinfo->vtoc.v_part[partnum].p_flag); if (s == NULL) s = "-"; nspaces(6 - (int)strlen(s)); fmt_print("%s", s); nspaces(2); if (nblks == 0) { fmt_print("%6d ", cyl1); nspaces(ncyl2_digits); fmt_print(" 0 "); } else { fmt_print("%6d - ", cyl1); nspaces(ncyl2_digits - ndigits(cyl2)); fmt_print("%d ", cyl2); scaled = bn2mb(nblks); if (scaled > (float)1024.0 * 1024.0) { fmt_print("%8.2fTB ", scaled/((float)1024.0 * 1024.0)); } else if (scaled > (float)1024.0) { fmt_print("%8.2fGB ", scaled/(float)1024.0); } else { fmt_print("%8.2fMB ", scaled); } } fmt_print("("); pr_dblock(fmt_print, nblks); fmt_print(")"); nspaces(ndigits(maxnblks/spc()) - ndigits(nblks/spc())); /* * Allocates size of the printf format string. * ndigits(ndigits(maxblks)) gives the byte size of * the printf width field for maxnblks. */ len = strlen(" %") + ndigits(ndigits(maxnblks)) + strlen("d\n") + 1; s = zalloc(len); (void) snprintf(s, len, "%s%u%s", " %", ndigits(maxnblks), "u\n"); fmt_print(s, nblks); (void) free(s); } /* * Return true if a disk has a volume name */ int chk_volname(struct disk_info *disk) { return (disk->v_volume[0] != 0); } /* * Print the volume name, if it appears to be set */ void print_volname(struct disk_info *disk) { int i; char *p; p = disk->v_volume; for (i = 0; i < LEN_DKL_VVOL; i++, p++) { if (*p == 0) break; fmt_print("%c", *p); } } /* * Print a number of spaces */ static void nspaces(int n) { while (n-- > 0) fmt_print(" "); } /* * Return the number of digits required to print a number */ static int ndigits(uint64_t n) { int i; i = 0; while (n > 0) { n /= 10; i++; } return (i == 0 ? 1 : i); } /* * 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 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _MENU_PARTITION_H #define _MENU_PARTITION_H #ifdef __cplusplus extern "C" { #endif /* * Prototypes for ANSI C compilers */ int p_apart(void); int p_bpart(void); int p_cpart(void); int p_dpart(void); int p_epart(void); int p_fpart(void); int p_gpart(void); int p_hpart(void); int p_ipart(void); #if defined(i386) int p_jpart(void); #endif /* defined(i386) */ int p_select(void); int p_expand(void); int p_modify(void); int p_name(void); int p_print(void); void print_map(struct partition_info *map); void print_partition(struct partition_info *pinfo, int partnum, int want_header); void print_efi_partition(struct dk_gpt *map, int partnum, int want_header); int chk_volname(struct disk_info *); void print_volname(struct disk_info *); #ifdef __cplusplus } #endif #endif /* _MENU_PARTITION_H */ /* * 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 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * This file contains functions implementing the scsi menu commands. * * These functions are intended for expert use only, and provide * a raw access to a scsi device's mode pages. The ability to * issue a raw format command is also provided, should a page be * changed that requires a format. */ #include "global.h" #include #include #include "io.h" #include "menu.h" #include "misc.h" #include "menu_scsi.h" #include "ctlr_scsi.h" #include "startup.h" #include "checkdev.h" static int do_mode_sense(int); static int do_mode_sense_all(void); static int do_mode_select(struct chg_list *); static int do_format(void); static void do_list(void); static int do_inquiry(void); static void do_apply(void); static void do_cancel(void); static void do_display(void); static int parse_change_spec(char *, char *, int, struct chg_list *); static void add_new_change_list_item(struct chg_list *); static void free_change_list(void); static void do_default(char *); static void default_all_pages(void); static int default_page(int); /* * Menu data for the SCSI menu display */ static char *scsi_menu_strings[] = { "p - display a mode sense page", "p b [~] - change a byte and issue mode select", "b [~] - add an operation to the mode select list", " for the current page", "", " where: p specifies the page with page code ", " b specifies byte of the page", " can be one of the following operators:", " = (set specified value)", " |= (bitwise OR with current value)", " &= (bitwise AND with current value)", " can be a decimal value in the range 0-255,", " or two hexadecimal digits, in the form 0x.", " [~] complements the specified value", "", "apply - apply mode select list", "cancel - cancel mode select list", "display - display mode select list", "all - display all supported mode sense pages", "default p - mode select page to default values", "default all - mode select all pages to default values", "format - format without standard mode selects", "inquiry - display device's inquiry response", "list - list common SCSI-2 mode pages", "! - execute , then return" }; #define N_SCSI_STRINGS (sizeof (scsi_menu_strings) / sizeof (char *)) /* * Command types */ #define CMD_ALL 0 #define CMD_FORMAT 1 #define CMD_QUIT 2 #define CMD_HELP 3 #define CMD_LIST 4 #define CMD_INQUIRY 5 #define CMD_APPLY 6 #define CMD_CANCEL 7 #define CMD_DISPLAY 8 /* * SCSI menu commands for minimum recognition */ static struct slist cmds_list[] = { { "all", NULL, CMD_ALL }, { "format", NULL, CMD_FORMAT }, { "quit", NULL, CMD_QUIT }, { "help", NULL, CMD_HELP }, { "?", NULL, CMD_HELP }, { "list", NULL, CMD_LIST }, { "inquiry", NULL, CMD_INQUIRY }, { "apply", NULL, CMD_APPLY }, { "cancel", NULL, CMD_CANCEL }, { "display", NULL, CMD_DISPLAY }, { NULL } }; /* * Implied page for mode select change lists */ static int current_page; static struct chg_list *change_list; /* * Manage the SCSI menu. * Parse input and dispatch to the appropriate functions. * The commands we accept are not simple one-word commands, * so we cannot use the standard format menu-handling functions. */ int c_scsi(void) { int i; struct env env; char **menu; struct menu_item scsi_menu[N_SCSI_STRINGS+1]; struct chg_list change_item; struct chg_list *chg_item; char s[MAXPATHLEN], nclean[MAXPATHLEN]; char *p; char *p2; int cmd; int pageno; int help = 1; /* * Warn casual users that maybe they should not be * using this menu. */ fmt_print("\n" "Warning: these functions are intended for expert use only, for\n" "debugging disk devices and for unusual configuration settings.\n" "It is recommended that you do not use this menu for normal disk\n" "configuration and formatting, unless you have explicit instructions,\n" "or know exactly what you are doing.\n"); /* * Initialize change list and current page to empty */ current_page = -1; change_list = NULL; /* * Build and display the menu. * we only use this for display purposes. */ for (i = 0; i < N_SCSI_STRINGS; i++) { scsi_menu[i].menu_cmd = scsi_menu_strings[i]; scsi_menu[i].menu_func = NULL; scsi_menu[i].menu_state = true; } scsi_menu[i].menu_cmd = NULL; menu = create_menu_list(scsi_menu); /* * Save the environment so a ctrl-C out of a command lands here. */ saveenv(env); for (;;) { if (help) { help = 0; fmt_print("\n\nSCSI MENU:\n"); display_menu_list(menu); } /* * Prompt and get next input line. We don't use the * standard input routine, since we need a little * more flexibility in parsing the input. */ fmt_print("scsi> "); get_inputline(nclean, sizeof (nclean)); clean_token(s, nclean); /* * Mark the saved environment active so the user can now * do a ctrl-C to get out of the command. */ useenv(); /* * Figure out what the user chose */ i = find_value(cmds_list, s, &cmd); if (i == 1) { switch (cmd) { case CMD_ALL: (void) do_mode_sense_all(); break; case CMD_FORMAT: (void) do_format(); break; case CMD_QUIT: goto exit; /*NOTREACHED*/ case CMD_HELP: fmt_print("\n\nSCSI MENU:\n"); display_menu_list(menu); break; case CMD_LIST: do_list(); break; case CMD_INQUIRY: (void) do_inquiry(); break; case CMD_APPLY: do_apply(); break; case CMD_CANCEL: do_cancel(); break; case CMD_DISPLAY: do_display(); break; } } else if (s[0] == 'd') { do_default(s); } else if (s[0] == 'p') { p = s + 1; pageno = (int)strtol(p, &p2, 0); if (p2 == p) { err_print("Syntax error: %s\n", s); goto error; } current_page = pageno; for (p = p2; *p == ' '; p++) ; if (*p == 0) { (void) do_mode_sense(pageno); } else if (*p == 'b') { if (parse_change_spec(s, p, pageno, &change_item)) { (void) do_mode_select(&change_item); } } } else if (s[0] == 'b') { if (current_page == -1) { err_print("\ Please display the page on which you'd like to do a mode select\n"); goto error; } chg_item = (struct chg_list *) zalloc(sizeof (struct chg_list)); if (parse_change_spec(s, s, current_page, chg_item)) { add_new_change_list_item(chg_item); } else { destroy_data((char *)chg_item); } } else if (s[0] == '!') { (void) execute_shell(&s[1], sizeof (s) - 1); help = 1; } else if (s[0] != 0) { err_print("Syntax error: %s\n", s); } error: /* * Mark the saved environment inactive so ctrl-C doesn't * work at the menu itself. */ unuseenv(); } exit: /* * Clean up the environment stack and free the menu */ clearenv(); destroy_data((char *)menu); /* * Clean up the change list, if anything left over */ free_change_list(); /* * Make sure user is prompted with previous menu */ last_menu++; return (0); } /* * Do a mode sense on a particular page, and dump the data. * Get all the various flavors: default, current, saved, changeable. */ static int do_mode_sense(int pageno) { struct scsi_ms_header header; struct mode_page *pg; char msbuf[MAX_MODE_SENSE_SIZE]; int result = 0; char *default_msg = "default: "; char *saved_msg = "saved: "; char *current_msg = "current: "; char *changeable_msg = "changeable: "; pg = (struct mode_page *)msbuf; fmt_print("\nPage 0x%x:\n", pageno); if (uscsi_mode_sense(cur_file, pageno, MODE_SENSE_PC_DEFAULT, msbuf, MAX_MODE_SENSE_SIZE, &header)) { err_print("%sfailed\n", default_msg); result = 1; } else { dump(default_msg, msbuf, MODESENSE_PAGE_LEN(pg), HEX_ONLY); } if (uscsi_mode_sense(cur_file, pageno, MODE_SENSE_PC_CURRENT, msbuf, MAX_MODE_SENSE_SIZE, &header)) { err_print("%sfailed\n", current_msg); result = 1; } else { dump(current_msg, msbuf, MODESENSE_PAGE_LEN(pg), HEX_ONLY); } if (uscsi_mode_sense(cur_file, pageno, MODE_SENSE_PC_SAVED, msbuf, MAX_MODE_SENSE_SIZE, &header)) { err_print("%sfailed\n", saved_msg); result = 1; } else { dump(saved_msg, msbuf, MODESENSE_PAGE_LEN(pg), HEX_ONLY); } if (uscsi_mode_sense(cur_file, pageno, MODE_SENSE_PC_CHANGEABLE, msbuf, MAX_MODE_SENSE_SIZE, &header)) { err_print("%sfailed\n", changeable_msg); result = 1; } else { dump(changeable_msg, msbuf, MODESENSE_PAGE_LEN(pg), HEX_ONLY); } fmt_print("\n"); return (result); } /* * Dump all the pages a device supports */ static int do_mode_sense_all(void) { int result = 0; if (scsi_dump_mode_sense_pages(MODE_SENSE_PC_DEFAULT)) { result = 1; } if (scsi_dump_mode_sense_pages(MODE_SENSE_PC_CURRENT)) { result = 1; } if (scsi_dump_mode_sense_pages(MODE_SENSE_PC_SAVED)) { result = 1; } if (scsi_dump_mode_sense_pages(MODE_SENSE_PC_CHANGEABLE)) { result = 1; } fmt_print("\n"); return (result); } /* * Get the current mode sense for a particular page, change * a byte, and issue a mode select. Note that we can only * change a value if the device indicates that those bits * are changeable. */ static int do_mode_select(struct chg_list *change_item) { struct scsi_ms_header header; char saved[MAX_MODE_SENSE_SIZE]; char changeable[MAX_MODE_SENSE_SIZE]; struct mode_page *pg; struct mode_page *pg2; int length; int pageno; int flags; int result = 0; pageno = change_item->pageno; /* * Get changeable mode sense */ if (uscsi_mode_sense(cur_file, pageno, MODE_SENSE_PC_CHANGEABLE, changeable, MAX_MODE_SENSE_SIZE, &header)) { err_print("Mode sense on page %x (changeable) failed\n", pageno); return (1); } /* * Get saved mode sense. If saved fails, use current values. */ if (uscsi_mode_sense(cur_file, pageno, MODE_SENSE_PC_SAVED, saved, MAX_MODE_SENSE_SIZE, &header)) { err_print("Mode sense on page %x (saved) failed\n", pageno); if (uscsi_mode_sense(cur_file, pageno, MODE_SENSE_PC_CURRENT, saved, MAX_MODE_SENSE_SIZE, &header)) { err_print("Mode sense on page %x (current) failed\n", pageno); return (1); } else { err_print("Using current values instead\n"); } } /* * Use the intersection of the saved and changeable */ pg = (struct mode_page *)saved; pg2 = (struct mode_page *)changeable; length = min(MODESENSE_PAGE_LEN(pg), MODESENSE_PAGE_LEN(pg2)); /* * Try making this change to this page */ if (apply_chg_list(pageno, length, (uchar_t *)saved, (uchar_t *)changeable, change_item)) { /* * A change was made. Do a mode select * We always want to set the Page Format bit. * Set the Save Page bit if the drive indicates * that it can save this page. */ flags = MODE_SELECT_PF; if (pg->ps) { flags |= MODE_SELECT_SP; } pg->ps = 0; header.mode_header.length = 0; header.mode_header.device_specific = 0; if (uscsi_mode_select(cur_file, pageno, flags, saved, length, &header)) { /* * Failed - try not saving parameters, * if possible. */ if (flags & MODE_SELECT_SP) { flags &= ~MODE_SELECT_SP; if (uscsi_mode_select(cur_file, pageno, flags, saved, length, &header)) { result = 1; } } else { result = 1; } } if (result) { fmt_print("\n\ Mode select on page %x failed.\n", pageno); } else if ((flags & MODE_SELECT_SP) == 0) { fmt_print("\n\ Mode select on page %x ok, but unable to save change permanently.\n", pageno); } else { fmt_print("\n\ Mode select on page %x ok.\n", pageno); } } else { err_print("\nDevice cannot support this change\n"); } fmt_print("\n"); return (result); } /* * Format a device, without any of the standard mode selects. * Ask if we should format with the P or the P&G lists. */ static int do_format(void) { struct uscsi_cmd ucmd; union scsi_cdb cdb; struct scsi_defect_hdr defect_hdr; int status; u_ioparam_t ioparam; int deflt; int grown_list; fmt_print("\n"); /* * Are there mounted partitions? */ if (checkmount((diskaddr_t)-1, (diskaddr_t)-1)) { err_print("Cannot format disk with mounted partitions\n\n"); return (-1); } /* * Is any of the partitions being used for swapping. */ if (checkswap((diskaddr_t)-1, (diskaddr_t)-1)) { err_print("Cannot format disk while its partitions are \ currently being used for swapping.\n\n"); return (-1); } /* * Are any being used for SVM, VxVM or live upgrade. */ if (checkdevinuse(cur_disk->disk_name, (diskaddr_t)-1, (diskaddr_t)-1, 0, 0)) { err_print("Cannot format disk while its partitions are " "currently being used as described.\n"); return (-1); } /* * Let the user choose between formatting with either * the P, or the P&G lists. Note that yes is 0, no is 1. */ deflt = 0; ioparam.io_charlist = confirm_list; grown_list = !input(FIO_MSTR, "Format with the Grown Defects list", '?', &ioparam, &deflt, DATA_INPUT); /* * Construct the uscsi format ioctl. * To format with the P and G list, we set the fmtData * and cmpLst bits to zero. To format with just the * P list, we set the fmtData bit (meaning that we will * send down a defect list in the data phase) and the * cmpLst bit (meaning that the list we send is the * complete G list), and a defect list header with * a defect list length of zero. */ (void) memset((char *)&ucmd, 0, sizeof (ucmd)); (void) memset((char *)&cdb, 0, sizeof (union scsi_cdb)); cdb.scc_cmd = SCMD_FORMAT; ucmd.uscsi_cdb = (caddr_t)&cdb; ucmd.uscsi_cdblen = CDB_GROUP0; if (!grown_list) { /* * No G list. Send empty defect list to replace it. */ cdb.cdb_opaque[1] = FPB_DATA | FPB_CMPLT | FPB_BFI; (void) memset((char *)&defect_hdr, 0, sizeof (defect_hdr)); ucmd.uscsi_bufaddr = (caddr_t)&defect_hdr; ucmd.uscsi_buflen = sizeof (defect_hdr); } /* * Issue the format ioctl */ fmt_print("Formatting...\n"); (void) fflush(stdout); status = uscsi_cmd(cur_file, &ucmd, F_NORMAL); fmt_print(status ? "Format failed\n\n" : "Format ok\n\n"); return (status); } /* * List common SCSI-2 mode pages */ static void do_list(void) { fmt_print("\n\ Common SCSI-2 pages applicable to direct-access devices:\n\n"); fmt_print("Page 0x1 - Read-Write Error Recovery Page\n"); fmt_print("Page 0x2 - Disconnect-Reconnect Page\n"); fmt_print("Page 0x3 - Format Device Page\n"); fmt_print("Page 0x4 - Rigid Disk Geometry Page\n"); fmt_print("Page 0x7 - Verify Error Recovery Page\n"); fmt_print("Page 0x8 - Caching Page\n"); fmt_print("Page 0xA - Control Mode Page\n"); fmt_print("\n"); } /* * Labels for the various fields of the scsi_inquiry structure */ static char *scsi_inquiry_labels[] = { "Vendor: ", "Product: ", "Revision: ", "Removable media: ", "Device type: ", "ISO version: ", "ECMA version: ", "ANSI version: ", "Async event notification: ", "Terminate i/o process msg: ", "Response data format: ", "Additional length: ", "Relative addressing: ", "32 bit transfers: ", "16 bit transfers: ", "Synchronous transfers: ", "Linked commands: ", "Command queueing: ", "Soft reset option: " }; /* * Dump the full inquiry as returned by the device */ static int do_inquiry(void) { char inqbuf[255]; struct scsi_inquiry *inq; char **p; inq = (struct scsi_inquiry *)inqbuf; if (uscsi_inquiry(cur_file, inqbuf, sizeof (inqbuf))) { err_print("\nInquiry failed\n"); return (1); } fmt_print("\nInquiry:\n"); /* * The SCSI-2 spec defines "Additional length" as (n-4) bytes, * where n is the last byte of the INQUIRY data. Thus * there are n+1 bytes of INQUIRY data. We need to add 5 to * inq_len in order to get all the INQUIRY data. */ dump(" ", inqbuf, inq->inq_len + 5, HEX_ASCII); fmt_print("\n"); p = scsi_inquiry_labels; fmt_print("%s", *p++); print_buf(inq->inq_vid, sizeof (inq->inq_vid)); fmt_print("\n%s", *p++); print_buf(inq->inq_pid, sizeof (inq->inq_pid)); fmt_print("\n%s", *p++); print_buf(inq->inq_revision, sizeof (inq->inq_revision)); fmt_print("\n%s%s\n", *p++, inq->inq_rmb ? "yes" : "no"); fmt_print("%s%d\n", *p++, inq->inq_qual); fmt_print("%s%d\n", *p++, inq->inq_iso); fmt_print("%s%d\n", *p++, inq->inq_ecma); fmt_print("%s%d\n", *p++, inq->inq_ansi); fmt_print("%s%s\n", *p++, inq->inq_aenc ? "yes" : "no"); fmt_print("%s%s\n", *p++, inq->inq_trmiop ? "yes" : "no"); fmt_print("%s%d\n", *p++, inq->inq_rdf); fmt_print("%s%d\n", *p++, inq->inq_len); fmt_print("%s%s\n", *p++, inq->inq_reladdr ? "yes" : "no"); fmt_print("%s%s\n", *p++, inq->inq_wbus32 ? "yes" : "no"); fmt_print("%s%s\n", *p++, inq->inq_wbus16 ? "yes" : "no"); fmt_print("%s%s\n", *p++, inq->inq_sync ? "yes" : "no"); fmt_print("%s%s\n", *p++, inq->inq_linked ? "yes" : "no"); fmt_print("%s%s\n", *p++, inq->inq_cmdque ? "yes" : "no"); fmt_print("%s%s\n", *p++, inq->inq_sftre ? "yes" : "no"); fmt_print("\n"); return (0); } static void do_apply(void) { if (change_list == NULL) { fmt_print("\nlist empty.\n"); } else { (void) do_mode_select(change_list); free_change_list(); } } static void do_cancel(void) { if (change_list == NULL) { fmt_print("\nlist empty.\n"); } else { free_change_list(); } } static void do_display(void) { struct chg_list *cp; if (change_list == NULL) { fmt_print("\nlist empty.\n"); } else { fmt_print("\nPage 0x%x\n", current_page); for (cp = change_list; cp != NULL; cp = cp->next) { fmt_print(" b0x%x ", cp->byteno); switch (cp->mode) { case CHG_MODE_ABS: fmt_print("= 0x%x\n", cp->value); break; case CHG_MODE_SET: fmt_print("|= 0x%x\n", cp->value); break; case CHG_MODE_CLR: fmt_print("&= ~0x%x\n", (~(cp->value)) & 0xff); break; default: impossible("do_display"); /*NOTREACHED*/ } } fmt_print("\n"); } } static int parse_change_spec(char *full_input, char *input, int pageno, struct chg_list *chg_item) { char *p; int tilde; assert(*input == 'b'); chg_item->pageno = pageno; chg_item->next = NULL; input++; chg_item->byteno = (int)strtol(input, &p, 0); if (p == input) { err_print("Syntax error: %s\n", full_input); return (0); } if (chg_item->byteno < 2) { err_print(" Unsupported byte offset: %d\n", chg_item->byteno); return (0); } for (input = p; *input == ' '; input++) ; chg_item->mode = CHG_MODE_UNDEFINED; switch (*input++) { case '=': chg_item->mode = CHG_MODE_ABS; break; case '|': if (*input++ == '=') { chg_item->mode = CHG_MODE_SET; } break; case '&': if (*input++ == '=') { chg_item->mode = CHG_MODE_CLR; } break; } if (chg_item->mode == CHG_MODE_UNDEFINED) { err_print("Syntax error: %s\n", full_input); return (0); } for (; *input == ' '; input++) ; if (*input == '~') { tilde = 1; for (input++; *input == ' '; input++) ; } else { tilde = 0; } chg_item->value = (int)strtol(input, &p, 0); if (p == input || *p != 0) { err_print("Syntax error: %s\n", full_input); return (0); } /* * Apply complement if selected. * Constrain to a byte value. */ if (tilde) { chg_item->value = ~chg_item->value; } chg_item->value &= 0xff; return (1); } static void add_new_change_list_item(struct chg_list *chg_item) { struct chg_list *cp; if (change_list == NULL) { change_list = chg_item; } else { for (cp = change_list; cp->next != NULL; cp = cp->next) ; cp->next = chg_item; } chg_item->next = NULL; } static void free_change_list(void) { struct chg_list *cp; struct chg_list *cp2; cp = change_list; while (cp != NULL) { cp2 = cp->next; destroy_data((char *)cp); cp = cp2; } change_list = NULL; } static void do_default(char *input) { char *s = input; char *p; int n; /* * Reset current page indicator */ current_page = -1; /* * Skip the leading "default" command, which we * must have, or we wouldn't have come here, * and any white space. */ while (isspace(*s)) { s++; } while (*s && isascii(*s) && isalpha(*s)) { s++; } while (isspace(*s)) { s++; } /* * Subsequent modifier must be either "p", or "all". */ if (*s == 'p') { s++; n = (int)strtol(s, &p, 0); if (p == s || *p != 0) { err_print("Syntax error: %s\n", input); } else { fmt_print("\n"); (void) default_page(n); fmt_print("\n"); } } else if (*s == 'a') { default_all_pages(); } else { err_print("Syntax error: %s\n", input); } } static void default_all_pages(void) { char *p; struct mode_header *mh; struct mode_page *mp; int n; struct uscsi_cmd ucmd; union scsi_cdb cdb; char msbuf[MAX_MODE_SENSE_SIZE]; int nbytes = sizeof (msbuf); int status; /* * Build and execute the uscsi ioctl. Note that * we cannot simply call uscsi_mode_sense() here, * since that function attempts to valididate the * returned data, and the page 0x3f has a unique * format. */ nbytes = MAX_MODE_SENSE_SIZE; (void) memset(msbuf, 0, nbytes); (void) memset((char *)&ucmd, 0, sizeof (ucmd)); (void) memset((char *)&cdb, 0, sizeof (union scsi_cdb)); cdb.scc_cmd = SCMD_MODE_SENSE; FORMG0COUNT(&cdb, (uchar_t)nbytes); cdb.cdb_opaque[2] = MODE_SENSE_PC_DEFAULT | 0x3f; ucmd.uscsi_cdb = (caddr_t)&cdb; ucmd.uscsi_cdblen = CDB_GROUP0; ucmd.uscsi_bufaddr = msbuf; ucmd.uscsi_buflen = nbytes; status = uscsi_cmd(cur_file, &ucmd, (option_msg) ? F_NORMAL : F_SILENT); if (status) { if (!option_msg) { err_print("\nMode sense page 0x3f failed\n"); } return; } fmt_print("\n"); /* * Now parse the page 0x3f */ mh = (struct mode_header *)msbuf; nbytes = mh->length - sizeof (struct mode_header) - mh->bdesc_length + 1; p = msbuf + sizeof (struct mode_header) + mh->bdesc_length; while (nbytes > 0) { mp = (struct mode_page *)p; n = mp->length + sizeof (struct mode_page); nbytes -= n; if (nbytes < 0) break; if (default_page(mp->code) == 0) { goto error; } p += n; } if (nbytes < 0) { err_print("Mode sense page 0x3f formatted incorrectly:\n"); } error: fmt_print("\n"); } static int default_page(int pageno) { struct scsi_ms_header header; char saved[MAX_MODE_SENSE_SIZE]; char current[MAX_MODE_SENSE_SIZE]; char dfault[MAX_MODE_SENSE_SIZE]; struct mode_page *sp; struct mode_page *cp; struct mode_page *dp; int length; int flags; int i; int need_mode_select; /* * Get default mode sense */ if (uscsi_mode_sense(cur_file, pageno, MODE_SENSE_PC_DEFAULT, dfault, MAX_MODE_SENSE_SIZE, &header)) { err_print("Mode sense on page %x (dfault) failed\n", pageno); return (0); } /* * Get the current mode sense. */ if (uscsi_mode_sense(cur_file, pageno, MODE_SENSE_PC_CURRENT, current, MAX_MODE_SENSE_SIZE, &header)) { err_print("Mode sense on page %x (current) failed\n", pageno); return (0); } /* * Get saved mode sense. If this fails, assume it is * the same as the current. */ if (uscsi_mode_sense(cur_file, pageno, MODE_SENSE_PC_SAVED, saved, MAX_MODE_SENSE_SIZE, &header)) { (void) memcpy(saved, current, MAX_MODE_SENSE_SIZE); } /* * Determine if we need a mode select on this page. * Just deal with the intersection of the three pages. */ sp = (struct mode_page *)saved; cp = (struct mode_page *)current; dp = (struct mode_page *)dfault; length = min(MODESENSE_PAGE_LEN(sp), MODESENSE_PAGE_LEN(cp)); length = min(length, MODESENSE_PAGE_LEN(dp)); need_mode_select = 0; for (i = 2; i < length; i++) { if (current[i] != dfault[i] || saved[i] != dfault[i]) { current[i] = dfault[i]; need_mode_select = 1; } } if (need_mode_select == 0) { fmt_print("Defaulting page 0x%x: ok\n", pageno); return (1); } /* * A change was made. Do a mode select * We always want to set the Page Format bit. * Set the Save Page bit if the drive indicates * that it can save this page. */ length = MODESENSE_PAGE_LEN(cp); flags = MODE_SELECT_PF; if (cp->ps) { flags |= MODE_SELECT_SP; } cp->ps = 0; header.mode_header.length = 0; header.mode_header.device_specific = 0; if (uscsi_mode_select(cur_file, pageno, flags, current, length, &header)) { /* * Failed - try not saving parameters, * if possible. */ if (flags & MODE_SELECT_SP) { flags &= ~MODE_SELECT_SP; if (uscsi_mode_select(cur_file, pageno, flags, saved, length, &header)) { fmt_print("Defaulting page 0x%x: failed\n", pageno); } else { fmt_print("Defaulting page 0x%x: ", pageno); fmt_print("cannot save page permanently\n"); } } else { fmt_print("Defaulting page 0x%x: ", pageno); fmt_print("cannot save page permanently\n"); } } else { fmt_print("Defaulting page 0x%x: mode select ok\n", pageno); } return (1); } /* * 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 (c) 1991-2001 by Sun Microsystems, Inc. */ #ifndef _MENU_SCSI_H #define _MENU_SCSI_H #ifdef __cplusplus extern "C" { #endif /* * Prototypes for ANSI */ int c_scsi(void); #ifdef __cplusplus } #endif #endif /* _MENU_SCSI_H */ /* * 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 (c) 1991, 2010, Oracle and/or its affiliates. All rights reserved. */ /* * This file contains miscellaneous routines. */ #include "global.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "misc.h" #include "analyze.h" #include "label.h" #include "startup.h" /* Function prototypes for ANSI C Compilers */ static void cleanup(int sig); struct env *current_env = NULL; /* ptr to current environment */ static int stop_pending = 0; /* ctrl-Z is pending */ struct ttystate ttystate; /* tty info */ static int aborting = 0; /* in process of aborting */ /* * For 4.x, limit the choices of valid disk names to this set. */ static char *disk_4x_identifiers[] = { "sd", "id"}; #define N_DISK_4X_IDS (sizeof (disk_4x_identifiers)/sizeof (char *)) /* * This is the list of legal inputs for all yes/no questions. */ char *confirm_list[] = { "yes", "no", NULL, }; /* * This routine is a wrapper for malloc. It allocates pre-zeroed space, * and checks the return value so the caller doesn't have to. */ void * zalloc(int count) { void *ptr; if ((ptr = calloc(1, (unsigned)count)) == NULL) { err_print("Error: unable to calloc more space.\n"); fullabort(); } return (ptr); } /* * This routine is a wrapper for realloc. It reallocates the given * space, and checks the return value so the caller doesn't have to. * Note that the any space added by this call is NOT necessarily * zeroed. */ void * rezalloc(void *ptr, int count) { void *new_ptr; if ((new_ptr = realloc((char *)ptr, (unsigned)count)) == NULL) { err_print("Error: unable to realloc more space.\n"); fullabort(); } return (new_ptr); } /* * This routine is a wrapper for free. */ void destroy_data(char *data) { free(data); } #ifdef not /* * This routine takes the space number returned by an ioctl call and * returns a mnemonic name for that space. */ char * space2str(uint_t space) { char *name; switch (space&SP_BUSMASK) { case SP_VIRTUAL: name = "virtual"; break; case SP_OBMEM: name = "obmem"; break; case SP_OBIO: name = "obio"; break; case SP_MBMEM: name = "mbmem"; break; case SP_MBIO: name = "mbio"; break; default: err_print("Error: unknown address space type encountered.\n"); fullabort(); } return (name); } #endif /* not */ /* * This routine asks the user the given yes/no question and returns * the response. */ int check(char *question) { int answer; u_ioparam_t ioparam; /* * If we are running out of a command file, assume a yes answer. */ if (option_f) return (0); /* * Ask the user. */ ioparam.io_charlist = confirm_list; answer = input(FIO_MSTR, question, '?', &ioparam, NULL, DATA_INPUT); return (answer); } /* * This routine aborts the current command. It is called by a ctrl-C * interrupt and also under certain error conditions. */ void cmdabort(int sig __unused) { /* * If there is no usable saved environment, gracefully exit. This * allows the user to interrupt the program even when input is from * a file, or if there is no current menu, like at the "Select disk:" * prompt. */ if (current_env == NULL || !(current_env->flags & ENV_USE)) fullabort(); /* * If we are in a critical zone, note the attempt and return. */ if (current_env->flags & ENV_CRITICAL) { current_env->flags |= ENV_ABORT; return; } /* * All interruptions when we are running out of a command file * cause the program to gracefully exit. */ if (option_f) fullabort(); fmt_print("\n"); /* * Clean up any state left by the interrupted command. */ cleanup(sig); /* * Jump to the saved environment. */ longjmp(current_env->env, 0); } /* * This routine implements the ctrl-Z suspend mechanism. It is called * when a suspend signal is received. */ void onsusp(int sig __unused) { int fix_term; #ifdef NOT_DEF sigset_t sigmask; #endif /* NOT_DEF */ /* * If we are in a critical zone, note the attempt and return. */ if (current_env != NULL && current_env->flags & ENV_CRITICAL) { stop_pending = 1; return; } /* * If the terminal is mucked up, note that we will need to * re-muck it when we start up again. */ fix_term = ttystate.ttyflags; fmt_print("\n"); /* * Clean up any state left by the interrupted command. */ cleanup(sig); #ifdef NOT_DEF /* Investigate whether all this is necessary */ /* * Stop intercepting the suspend signal, then send ourselves one * to cause us to stop. */ sigmask.sigbits[0] = (ulong_t)0xffffffff; if (sigprocmask(SIG_SETMASK, &sigmask, NULL) == -1) err_print("sigprocmask failed %d\n", errno); #endif /* NOT_DEF */ (void) signal(SIGTSTP, SIG_DFL); (void) kill(0, SIGTSTP); /* * PC stops here */ /* * We are started again. Set us up to intercept the suspend * signal once again. */ (void) signal(SIGTSTP, onsusp); /* * Re-muck the terminal if necessary. */ if (fix_term & TTY_ECHO_OFF) echo_off(); if (fix_term & TTY_CBREAK_ON) charmode_on(); } /* * This routine implements the timing function used during long-term * disk operations (e.g. formatting). It is called when an alarm signal * is received. */ void onalarm(int sig __unused) { } /* * This routine gracefully exits the program. */ void fullabort(void) { fmt_print("\n"); /* * Clean up any state left by an interrupted command. * Avoid infinite loops caused by a clean-up * routine failing again... */ if (!aborting) { aborting = 1; cleanup(SIGKILL); } exit(1); /*NOTREACHED*/ } /* * This routine cleans up the state of the world. It is a hodge-podge * of kludges to allow us to interrupt commands whenever possible. * * Some cleanup actions may depend on the type of signal. */ static void cleanup(int sig) { /* * Lock out interrupts to avoid recursion. */ enter_critical(); /* * Fix up the tty if necessary. */ if (ttystate.ttyflags & TTY_CBREAK_ON) { charmode_off(); } if (ttystate.ttyflags & TTY_ECHO_OFF) { echo_on(); } /* * If the defect list is dirty, write it out. */ if (cur_list.flags & LIST_DIRTY) { cur_list.flags = 0; if (!EMBEDDED_SCSI) write_deflist(&cur_list); } /* * If the label is dirty, write it out. */ if (cur_flags & LABEL_DIRTY) { cur_flags &= ~LABEL_DIRTY; (void) write_label(); } /* * If we are logging and just interrupted a scan, print out * some summary info to the log file. */ if (log_file && scan_cur_block >= 0) { pr_dblock(log_print, scan_cur_block); log_print("\n"); } if (scan_blocks_fixed >= 0) fmt_print("Total of %lld defective blocks repaired.\n", scan_blocks_fixed); if (sig != SIGSTOP) { /* Don't reset on suspend (converted to stop) */ scan_cur_block = scan_blocks_fixed = -1; } exit_critical(); } /* * This routine causes the program to enter a critical zone. Within the * critical zone, no interrupts are allowed. Note that calls to this * routine for the same environment do NOT nest, so there is not * necessarily pairing between calls to enter_critical() and exit_critical(). */ void enter_critical(void) { /* * If there is no saved environment, interrupts will be ignored. */ if (current_env == NULL) return; /* * Mark the environment to be in a critical zone. */ current_env->flags |= ENV_CRITICAL; } /* * This routine causes the program to exit a critical zone. Note that * calls to enter_critical() for the same environment do NOT nest, so * one call to exit_critical() will erase any number of such calls. */ void exit_critical(void) { /* * If there is a saved environment, mark it to be non-critical. */ if (current_env != NULL) current_env->flags &= ~ENV_CRITICAL; /* * If there is a stop pending, execute the stop. */ if (stop_pending) { stop_pending = 0; onsusp(SIGSTOP); } /* * If there is an abort pending, execute the abort. */ if (current_env == NULL) return; if (current_env->flags & ENV_ABORT) { current_env->flags &= ~ENV_ABORT; cmdabort(SIGINT); } } /* * This routine turns off echoing on the controlling tty for the program. */ void echo_off(void) { /* * Open the tty and store the file pointer for later. */ if (ttystate.ttyflags == 0) { if ((ttystate.ttyfile = open("/dev/tty", O_RDWR | O_NDELAY)) < 0) { err_print("Unable to open /dev/tty.\n"); fullabort(); } } /* * Get the parameters for the tty, turn off echoing and set them. */ if (tcgetattr(ttystate.ttyfile, &ttystate.ttystate) < 0) { err_print("Unable to get tty parameters.\n"); fullabort(); } ttystate.ttystate.c_lflag &= ~ECHO; if (tcsetattr(ttystate.ttyfile, TCSANOW, &ttystate.ttystate) < 0) { err_print("Unable to set tty to echo off state.\n"); fullabort(); } /* * Remember that we've successfully turned * ECHO mode off, so we know to fix it later. */ ttystate.ttyflags |= TTY_ECHO_OFF; } /* * This routine turns on echoing on the controlling tty for the program. */ void echo_on(void) { /* * Using the saved parameters, turn echoing on and set them. */ ttystate.ttystate.c_lflag |= ECHO; if (tcsetattr(ttystate.ttyfile, TCSANOW, &ttystate.ttystate) < 0) { err_print("Unable to set tty to echo on state.\n"); fullabort(); } /* * Close the tty and mark it ok again. */ ttystate.ttyflags &= ~TTY_ECHO_OFF; if (ttystate.ttyflags == 0) { (void) close(ttystate.ttyfile); } } /* * This routine turns off single character entry mode for tty. */ void charmode_on(void) { /* * If tty unopened, open the tty and store the file pointer for later. */ if (ttystate.ttyflags == 0) { if ((ttystate.ttyfile = open("/dev/tty", O_RDWR | O_NDELAY)) < 0) { err_print("Unable to open /dev/tty.\n"); fullabort(); } } /* * Get the parameters for the tty, turn on char mode. */ if (tcgetattr(ttystate.ttyfile, &ttystate.ttystate) < 0) { err_print("Unable to get tty parameters.\n"); fullabort(); } ttystate.vmin = ttystate.ttystate.c_cc[VMIN]; ttystate.vtime = ttystate.ttystate.c_cc[VTIME]; ttystate.ttystate.c_lflag &= ~ICANON; ttystate.ttystate.c_cc[VMIN] = 1; ttystate.ttystate.c_cc[VTIME] = 0; if (tcsetattr(ttystate.ttyfile, TCSANOW, &ttystate.ttystate) < 0) { err_print("Unable to set tty to cbreak on state.\n"); fullabort(); } /* * Remember that we've successfully turned * CBREAK mode on, so we know to fix it later. */ ttystate.ttyflags |= TTY_CBREAK_ON; } /* * This routine turns on single character entry mode for tty. * Note, this routine must be called before echo_on. */ void charmode_off(void) { /* * Using the saved parameters, turn char mode on. */ ttystate.ttystate.c_lflag |= ICANON; ttystate.ttystate.c_cc[VMIN] = ttystate.vmin; ttystate.ttystate.c_cc[VTIME] = ttystate.vtime; if (tcsetattr(ttystate.ttyfile, TCSANOW, &ttystate.ttystate) < 0) { err_print("Unable to set tty to cbreak off state.\n"); fullabort(); } /* * Close the tty and mark it ok again. */ ttystate.ttyflags &= ~TTY_CBREAK_ON; if (ttystate.ttyflags == 0) { (void) close(ttystate.ttyfile); } } /* * 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(char *s) { char *ns; if (s == NULL) { ns = zalloc(1); } else { ns = zalloc(strlen(s) + 1); (void) strcpy(ns, s); } return (ns); } /* * This function can be used to build up an array of strings * dynamically, with a trailing NULL to terminate the list. * * Parameters: * argvlist: a pointer to the base of the current list. * does not have to be initialized. * size: pointer to an integer, indicating the number * of string installed in the list. Must be * initialized to zero. * alloc: pointer to an integer, indicating the amount * of space allocated. Must be initialized to * zero. For efficiency, we allocate the list * in chunks and use it piece-by-piece. * str: the string to be inserted in the list. * A copy of the string is malloc'ed, and * appended at the end of the list. * Returns: * a pointer to the possibly-moved argvlist. * * No attempt to made to free unused memory when the list is * completed, although this would not be hard to do. For * reasonably small lists, this should suffice. */ #define INITIAL_LISTSIZE 32 #define INCR_LISTSIZE 32 char ** build_argvlist(char **argvlist, int *size, int *alloc, char *str) { if (*size + 2 > *alloc) { if (*alloc == 0) { *alloc = INITIAL_LISTSIZE; argvlist = zalloc(sizeof (char *) * (*alloc)); } else { *alloc += INCR_LISTSIZE; argvlist = rezalloc((void *) argvlist, sizeof (char *) * (*alloc)); } } argvlist[*size] = alloc_string(str); *size += 1; argvlist[*size] = NULL; return (argvlist); } /* * Useful parsing macros */ #define must_be(s, c) if (*s++ != c) return (0) #define skip_digits(s) while (isdigit(*s)) s++ /* Parsing macro below is created to handle fabric devices which contains */ /* upper hex digits like c2t210000203708B8CEd0s0. */ /* To get the target id(tid) the digit and hex upper digit need to */ /* be processed. */ #define skip_digit_or_hexupper(s) while (isdigit(*s) || \ (isxdigit(*s) && isupper(*s))) s++ /* * Return true if a device name matches the conventions * for the particular system. */ int conventional_name(char *name) { must_be(name, 'c'); skip_digits(name); if (*name == 't') { name++; skip_digit_or_hexupper(name); } must_be(name, 'd'); skip_digits(name); must_be(name, 's'); skip_digits(name); return (*name == 0); } #ifdef i386 /* * Return true if a device name match the emc powerpath name scheme: * emcpowerN[a-p,p0,p1,p2,p3,p4] */ int emcpower_name(char *name) { char *emcp = "emcpower"; char *devp = "/dev/dsk"; char *rdevp = "/dev/rdsk"; if (strncmp(devp, name, strlen(devp)) == 0) { name += strlen(devp) + 1; } else if (strncmp(rdevp, name, strlen(rdevp)) == 0) { name += strlen(rdevp) + 1; } if (strncmp(emcp, name, strlen(emcp)) == 0) { name += strlen(emcp); if (isdigit(*name)) { skip_digits(name); if ((*name >= 'a') && (*name <= 'p')) { name ++; if ((*name >= '0') && (*name <= '4')) { name++; } } return (*name == '\0'); } } return (0); } #endif /* * Return true if a device name matches the intel physical name conventions * for the particular system. */ int fdisk_physical_name(char *name) { must_be(name, 'c'); skip_digits(name); if (*name == 't') { name++; skip_digit_or_hexupper(name); } must_be(name, 'd'); skip_digits(name); must_be(name, 'p'); skip_digits(name); return (*name == 0); } /* * Return true if a device name matches the conventions * for a "whole disk" name for the particular system. * The name in this case must match exactly that which * would appear in the device directory itself. */ int whole_disk_name(char *name) { must_be(name, 'c'); skip_digits(name); if (*name == 't') { name++; skip_digit_or_hexupper(name); } must_be(name, 'd'); skip_digits(name); must_be(name, 's'); must_be(name, '2'); return (*name == 0); } /* * Return true if a name is in the internal canonical form */ int canonical_name(char *name) { must_be(name, 'c'); skip_digits(name); if (*name == 't') { name++; skip_digit_or_hexupper(name); } must_be(name, 'd'); skip_digits(name); return (*name == 0); } /* * Return true if a name is in the internal canonical form for 4.x * Used to support 4.x naming conventions under 5.0. */ int canonical4x_name(char *name) { char **p; int i; p = disk_4x_identifiers; for (i = N_DISK_4X_IDS; i > 0; i--, p++) { if (match_substr(name, *p)) { name += strlen(*p); break; } } if (i == 0) return (0); skip_digits(name); return (*name == 0); } /* * Map a conventional name into the internal canonical form: * * /dev/rdsk/c0t0d0s0 -> c0t0d0 */ void canonicalize_name(char *dst, char *src) { char *s; /* * Copy from the 'c' to the end to the destination string... */ s = strchr(src, 'c'); if (s != NULL) { (void) strcpy(dst, s); /* * Remove the trailing slice (partition) reference */ s = dst + strlen(dst) - 2; if (*s == 's') { *s = 0; } } else { *dst = 0; /* be tolerant of garbage input */ } } /* * Return true if we find an occurance of s2 at the * beginning of s1. We don't have to match all of * s1, but we do have to match all of s2 */ int match_substr(char *s1, char *s2) { while (*s2 != 0) { if (*s1++ != *s2++) return (0); } return (1); } /* * Dump a structure in hexadecimal, for diagnostic purposes */ #define BYTES_PER_LINE 16 void dump(char *hdr, caddr_t src, int nbytes, int format) { int i; int n; char *p; char s[256]; assert(format == HEX_ONLY || format == HEX_ASCII); (void) strcpy(s, hdr); for (p = s; *p; p++) { *p = ' '; } p = hdr; while (nbytes > 0) { err_print("%s", p); p = s; n = min(nbytes, BYTES_PER_LINE); for (i = 0; i < n; i++) { err_print("%02x ", src[i] & 0xff); } if (format == HEX_ASCII) { for (i = BYTES_PER_LINE-n; i > 0; i--) { err_print(" "); } err_print(" "); for (i = 0; i < n; i++) { err_print("%c", isprint(src[i]) ? src[i] : '.'); } } err_print("\n"); nbytes -= n; src += n; } } float bn2mb(uint64_t nblks) { float n; n = (float)nblks / 1024.0; return ((n / 1024.0) * cur_blksz); } diskaddr_t mb2bn(float mb) { diskaddr_t n; n = (diskaddr_t)(mb * 1024.0 * (1024.0 / cur_blksz)); return (n); } float bn2gb(uint64_t nblks) { float n; n = (float)nblks / (1024.0 * 1024.0); return ((n/1024.0) * cur_blksz); } float bn2tb(uint64_t nblks) { float n; n = (float)nblks / (1024.0 * 1024.0 * 1024.0); return ((n/1024.0) * cur_blksz); } diskaddr_t gb2bn(float gb) { diskaddr_t n; n = (diskaddr_t)(gb * 1024.0 * 1024.0 * (1024.0 / cur_blksz)); return (n); } /* * This routine finds out the number of lines (rows) in a terminal * window. The default value of TTY_LINES is returned on error. */ int get_tty_lines(void) { int tty_lines = TTY_LINES; struct winsize winsize; if ((option_f == NULL) && isatty(0) == 1 && isatty(1) == 1) { /* * We have a real terminal for std input and output */ winsize.ws_row = 0; if (ioctl(1, TIOCGWINSZ, &winsize) == 0) { if (winsize.ws_row > 2) { /* * Should be atleast 2 lines, for division * by (tty_lines - 1, tty_lines - 2) to work. */ tty_lines = winsize.ws_row; } } } return (tty_lines); } /* * 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 (c) 1991, 2010, Oracle and/or its affiliates. All rights reserved. */ #ifndef _MISC_H #define _MISC_H #ifdef __cplusplus extern "C" { #endif /* * This file contains declarations pertaining to the miscellaneous routines. */ #include #include /* * Define macros bzero and bcopy for convenience */ #ifndef bzero #define bzero(p, n) (void) memset((p), 0, (n)) #endif #ifndef bcopy #define bcopy(src, dst, n) (void) memcpy((dst), (src), (n)) #endif #ifndef bcmp #define bcmp(p1, p2, n) memcmp((p1), (p2), (n)) #endif /* * Minimum and maximum macros */ #ifndef min #define min(x, y) ((x) < (y) ? (x) : (y)) #endif /* min */ #ifndef max #define max(x, y) ((x) > (y) ? (x) : (y)) #endif /* max */ /* * This defines the structure of a saved environment. It consists of the * environment itself, a pointer to the next environment on the stack, and * flags to tell whether the environment is active, etc. */ struct env { jmp_buf env; /* environment buf */ struct env *ptr; /* ptr to next on list */ char flags; /* flags */ }; extern struct env *current_env; /* * This macro saves the current environment in the given structure and * pushes the structure onto our enivornment stack. It initializes the * flags to zero (inactive). */ #define saveenv(x) { \ x.ptr = current_env; \ current_env = &x; \ (void) setjmp(x.env); \ x.flags = 0; \ } /* * This macro marks the environment on the top of the stack active. It * assumes that there is an environment on the stack. */ #define useenv() (current_env->flags |= ENV_USE) /* * This macro marks the environment on the top of the stack inactive. It * assumes that there is an environment on the stack. */ #define unuseenv() (current_env->flags &= ~ENV_USE) /* * This macro pops an environment off the top of the stack. It * assumes that there is an environment on the stack. */ #define clearenv() (current_env = current_env->ptr) /* * These are the flags for the environment struct. */ #define ENV_USE 0x01 /* active */ #define ENV_CRITICAL 0x02 /* in critical zone */ #define ENV_ABORT 0x04 /* abort pending */ /* * This structure is used to keep track of the state of the tty. This * is necessary because some of the commands turn off echoing. */ struct ttystate { struct termios ttystate; /* buffer for ioctls */ int ttyflags; /* changes to tty state */ int ttyfile; /* file for ioctls */ int vmin; /* min read satisfier */ int vtime; /* read timing */ }; /* * ttyflags - changes we can make to the tty state. */ #define TTY_ECHO_OFF 0x01 /* turned echo off */ #define TTY_CBREAK_ON 0x02 /* turned cbreak on */ /* * This is the number lines assumed for the tty. It is designed to work * on terminals as well as sun monitors. */ #define TTY_LINES 24 /* * format parameter to dump() */ #define HEX_ONLY 0 /* print hex only */ #define HEX_ASCII 1 /* hex and ascii */ /* * Prototypes for ANSI C */ void *zalloc(int count); void *rezalloc(void *ptr, int count); void destroy_data(char *data); int check(char *question); void cmdabort(int sig); void onsusp(int sig); void onalarm(int sig); void fullabort(void) __NORETURN; void enter_critical(void); void exit_critical(void); void echo_off(void); void echo_on(void); void charmode_on(void); void charmode_off(void); char *alloc_string(char *s); char **build_argvlist(char **, int *, int *, char *); int conventional_name(char *name); #ifdef i386 int emcpower_name(char *name); #endif #if defined(_FIRMWARE_NEEDS_FDISK) int fdisk_physical_name(char *name); #endif /* defined(_FIRMWARE_NEEDS_FDISK) */ int whole_disk_name(char *name); int canonical_name(char *name); int canonical4x_name(char *name); void canonicalize_name(char *dst, char *src); int match_substr(char *s1, char *s2); void dump(char *, caddr_t, int, int); float bn2mb(uint64_t); diskaddr_t mb2bn(float); float bn2gb(uint64_t); float bn2tb(uint64_t); diskaddr_t gb2bn(float); int get_tty_lines(); /* * Macro to handle internal programming errors that * should "never happen". */ #define impossible(msg) {err_print("Internal error: file %s, line %d: %s\n", \ __FILE__, __LINE__, msg); \ fullabort(); } extern char *confirm_list[]; /* * This defines the size of the blind selection verfication prompt */ #define BLIND_SELECT_VER_PROMPT (43 + MAXNAMELEN) #ifdef __cplusplus } #endif #endif /* _MISC_H */ /* * 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 (c) 1991, 2010, Oracle and/or its affiliates. All rights reserved. */ /* * This file contains functions to implement the partition menu commands. */ #include #include #include "global.h" #include "partition.h" #include "menu_partition.h" #include "menu_command.h" #include "modify_partition.h" #include "checkdev.h" #include "misc.h" #include "label.h" #include "auto_sense.h" static void adj_cyl_offset(struct dk_map32 *map); static int check_map(struct dk_map32 *map); static void get_user_map(struct dk_map32 *map, int float_part); static void get_user_map_efi(struct dk_gpt *map, int float_part); static char *partn_list[] = { "0", "1", "2", "3", "4", "5", "6", "7", NULL }; static char *sel_list[] = { "0", "1", "2", "3", NULL }; #define MBYTE (1024*1024) /* * Modify/Create a predefined partition table. */ int p_modify(void) { struct partition_info tmp_pinfo[1]; struct dk_map32 *map = tmp_pinfo->pinfo_map; u_ioparam_t ioparam; int inpt_dflt = 0; int free_hog = -1; int i; char tmpstr[80]; char tmpstr2[300]; int sel_type = 0; /* * There must be a current disk type (and therefore a current disk). */ if (cur_dtype == NULL) { err_print("Current Disk Type is not set.\n"); return (-1); } /* * check if there exists a partition table for the disk. */ if (cur_parts == NULL) { err_print("Current Disk has no partition table.\n"); return (-1); } /* * If the disk has mounted partitions, cannot modify */ if (checkmount((diskaddr_t)-1, (diskaddr_t)-1)) { err_print( "Cannot modify disk partitions while it has mounted partitions.\n\n"); return (-1); } /* * If the disk has partitions currently being used for * swapping, cannot modify */ if (checkswap((diskaddr_t)-1, (diskaddr_t)-1)) { err_print( "Cannot modify disk partitions while it is \ currently being used for swapping.\n"); return (-1); } /* * Check to see if any partitions used for svm, vxvm, ZFS zpool * or live upgrade are on the disk. */ if (checkdevinuse(cur_disk->disk_name, (diskaddr_t)-1, (diskaddr_t)-1, 0, 0)) { err_print("Cannot modify disk partition when " "partitions are in use as described.\n"); return (-1); } /* * prompt user for a partition table base */ if (cur_parts->pinfo_name != NULL) { (void) snprintf(tmpstr, sizeof (tmpstr), "\t0. Current partition table (%s)", cur_parts->pinfo_name); } else { (void) sprintf(tmpstr, "\t0. Current partition table (unnamed)"); } (void) snprintf(tmpstr2, sizeof (tmpstr2), "Select partitioning base:\n%s\n" "\t1. All Free Hog\n" "Choose base (enter number) ", tmpstr); ioparam.io_charlist = sel_list; sel_type = input(FIO_MSTR, tmpstr2, '?', &ioparam, &sel_type, DATA_INPUT); switch (cur_label) { case L_TYPE_SOLARIS: if (sel_type == 0) { /* * Check for invalid parameters but do * not modify the table. */ if (check_map(cur_parts->pinfo_map)) { err_print("Warning: Fix, or select a " "different partition table.\n"); return (0); } /* * Create partition map from existing map */ tmp_pinfo->vtoc = cur_parts->vtoc; for (i = 0; i < NDKMAP; i++) { map[i].dkl_nblk = cur_parts->pinfo_map[i].dkl_nblk; map[i].dkl_cylno = cur_parts->pinfo_map[i].dkl_cylno; } } else { /* * Make an empty partition map, with all the space * in the c partition. */ set_vtoc_defaults(tmp_pinfo); for (i = 0; i < NDKMAP; i++) { map[i].dkl_nblk = 0; map[i].dkl_cylno = 0; } map[C_PARTITION].dkl_nblk = ncyl * spc(); #if defined(i386) /* * Adjust for the boot and possibly alternates * partitions. */ map[I_PARTITION].dkl_nblk = spc(); map[I_PARTITION].dkl_cylno = 0; if (cur_ctype->ctype_ctype != DKC_SCSI_CCS) { map[J_PARTITION].dkl_nblk = 2 * spc(); map[J_PARTITION].dkl_cylno = spc() / spc(); } #endif /* defined(i386) */ } break; case L_TYPE_EFI: if (sel_type == 1) { for (i = 0; i < cur_parts->etoc->efi_nparts; i++) { cur_parts->etoc->efi_parts[i].p_start = 0; cur_parts->etoc->efi_parts[i].p_size = 0; } } break; } fmt_print("\n"); if (cur_label == L_TYPE_SOLARIS) { print_map(tmp_pinfo); } else { print_map(cur_parts); } ioparam.io_charlist = confirm_list; if (input(FIO_MSTR, "Do you wish to continue creating a new " "partition\ntable based on above table", '?', &ioparam, &inpt_dflt, DATA_INPUT)) { return (0); } /* * get Free Hog partition */ inpt_dflt = 1; while ((free_hog < 0) && (cur_label == L_TYPE_SOLARIS)) { free_hog = G_PARTITION; /* default to g partition */ ioparam.io_charlist = partn_list; free_hog = input(FIO_MSTR, "Free Hog partition", '?', &ioparam, &free_hog, DATA_INPUT); /* disallow c partition */ if (free_hog == C_PARTITION) { fmt_print("'%c' cannot be the 'Free Hog' partition.\n", C_PARTITION + PARTITION_BASE); free_hog = -1; continue; } /* * If user selected all float set the * float to be the whole disk. */ if (sel_type == 1) { map[free_hog].dkl_nblk = map[C_PARTITION].dkl_nblk; #if defined(i386) map[free_hog].dkl_nblk -= map[I_PARTITION].dkl_nblk; if (cur_ctype->ctype_ctype != DKC_SCSI_CCS) { map[free_hog].dkl_nblk -= map[J_PARTITION].dkl_nblk; } #endif /* defined(i386) */ break; } /* * Warn the user if there is no free space in * the float partition. */ if (map[free_hog].dkl_nblk == 0) { err_print("Warning: No space available from Free Hog " "partition.\n"); ioparam.io_charlist = confirm_list; if (input(FIO_MSTR, "Continue", '?', &ioparam, &inpt_dflt, DATA_INPUT)) { free_hog = -1; } } } inpt_dflt = 0; if (cur_label == L_TYPE_EFI) { free_hog = G_PARTITION; /* default to g partition */ ioparam.io_charlist = partn_list; free_hog = input(FIO_MSTR, "Free Hog partition", '?', &ioparam, &free_hog, DATA_INPUT); /* disallow c partition */ if (free_hog == C_PARTITION) { fmt_print("'%c' cannot be the 'Free Hog' partition.\n", C_PARTITION + PARTITION_BASE); return (-1); } get_user_map_efi(cur_parts->etoc, free_hog); print_map(cur_parts); if (check("Ready to label disk, continue")) { return (-1); } fmt_print("\n"); if (write_label()) { err_print("Writing label failed\n"); return (-1); } return (0); } /* * get user modified partition table */ get_user_map(map, free_hog); /* * Update cylno offsets */ adj_cyl_offset(map); fmt_print("\n"); print_map(tmp_pinfo); ioparam.io_charlist = confirm_list; if (input(FIO_MSTR, "Okay to make this the current partition table", '?', &ioparam, &inpt_dflt, DATA_INPUT)) { return (0); } else { make_partition(); /* * Update new partition map */ for (i = 0; i < NDKMAP; i++) { cur_parts->pinfo_map[i].dkl_nblk = map[i].dkl_nblk; cur_parts->pinfo_map[i].dkl_cylno = map[i].dkl_cylno; #ifdef i386 cur_parts->vtoc.v_part[i].p_start = map[i].dkl_cylno * nhead * nsect; cur_parts->vtoc.v_part[i].p_size = map[i].dkl_nblk; #endif } (void) p_name(); /* * Label the disk now */ if (check("Ready to label disk, continue")) { return (-1); } fmt_print("\n"); if (write_label()) { err_print("Writing label failed\n"); return (-1); } return (0); } } /* * Adjust cylinder offsets */ static void adj_cyl_offset(struct dk_map32 *map) { int i; int cyloffset = 0; /* * Update cylno offsets */ #if defined(_SUNOS_VTOC_16) /* * Correct cylinder allocation for having the boot and alternates * slice in the beginning of the disk */ for (i = NDKMAP/2; i < NDKMAP; i++) { if (i != C_PARTITION && map[i].dkl_nblk) { map[i].dkl_cylno = cyloffset; cyloffset += (map[i].dkl_nblk + (spc()-1))/spc(); } else if (map[i].dkl_nblk == 0) { map[i].dkl_cylno = 0; } } for (i = 0; i < NDKMAP/2; i++) { #else /* !defined(_SUNOS_VTOC_16) */ for (i = 0; i < NDKMAP; i++) { #endif /* defined(_SUNOS_VTOC_16) */ if (i != C_PARTITION && map[i].dkl_nblk) { map[i].dkl_cylno = cyloffset; cyloffset += (map[i].dkl_nblk + (spc()-1))/spc(); } else if (map[i].dkl_nblk == 0) { map[i].dkl_cylno = 0; } } } /* * Check partition table */ static int check_map(struct dk_map32 *map) { int i; int cyloffset = 0; blkaddr32_t tot_blks = 0; #ifdef i386 /* * On x86, we must account for the boot and alternates */ cyloffset = map[0].dkl_cylno; tot_blks = map[0].dkl_nblk; #endif /* * Do some checks for invalid parameters but do * not modify the table. */ for (i = 0; i < NDKMAP; i++) { if (map[i].dkl_cylno > (blkaddr32_t)ncyl-1) { err_print("Warning: Partition %c starting cylinder " "%d is out of range.\n", (PARTITION_BASE+i), map[i].dkl_cylno); return (-1); } if (map[i].dkl_nblk > (blkaddr32_t)(ncyl - map[i].dkl_cylno) * spc()) { err_print("Warning: Partition %c, specified # of " "blocks, %u, is out of range.\n", (PARTITION_BASE+i), map[i].dkl_nblk); return (-1); } if (i != C_PARTITION && map[i].dkl_nblk) { #ifdef i386 if (i == I_PARTITION || i == J_PARTITION) continue; #endif if (map[i].dkl_cylno < cyloffset) { err_print("Warning: Overlapping partition " "(%c) in table.\n", PARTITION_BASE+i); return (-1); } else if (map[i].dkl_cylno > cyloffset) { err_print("Warning: Non-contiguous partition " "(%c) in table.\n", PARTITION_BASE+i); } cyloffset += (map[i].dkl_nblk + (spc()-1))/spc(); tot_blks = map[i].dkl_nblk; } } if (tot_blks > map[C_PARTITION].dkl_nblk) { err_print("Warning: Total blocks used is greater than number " "of blocks in '%c'\n\tpartition.\n", C_PARTITION + PARTITION_BASE); return (-1); } return (0); } /* * get user defined partitions */ static void get_user_map(struct dk_map32 *map, int float_part) { int i; blkaddr32_t newsize; blkaddr32_t deflt; char tmpstr[80]; u_ioparam_t ioparam; /* * Get partition sizes */ for (i = 0; i < NDKMAP; i++) { if (partn_list[i] == NULL) break; if ((i == C_PARTITION) || (i == float_part)) { continue; } else { ioparam.io_bounds.lower = 0; ioparam.io_bounds.upper = map[i].dkl_nblk + map[float_part].dkl_nblk; deflt = map[i].dkl_nblk; if (ioparam.io_bounds.upper == 0) { err_print("Warning: no space available for " "'%s' from Free Hog partition\n", partn_list[i]); continue; } (void) snprintf(tmpstr, sizeof (tmpstr), "Enter size of partition '%s' ", partn_list[i]); newsize = (blkaddr32_t)input(FIO_CYL, tmpstr, ':', &ioparam, (int *)&deflt, DATA_INPUT); map[float_part].dkl_nblk -= (newsize - map[i].dkl_nblk); map[i].dkl_nblk = newsize; } } } static struct partition_info * build_partition(struct disk_type *tptr) { struct partition_info *part; struct dk_label *label; int i; #ifdef DEBUG fmt_print("Creating Default Partition for the disk \n"); #endif /* * construct a label and pass it on to * build_default_partition() which builds the * default partition list. */ label = zalloc(sizeof (struct dk_label)); label->dkl_pcyl = tptr->dtype_pcyl; label->dkl_ncyl = tptr->dtype_ncyl; label->dkl_acyl = tptr->dtype_acyl; label->dkl_nhead = tptr->dtype_nhead; label->dkl_nsect = tptr->dtype_nsect; label->dkl_apc = apc; label->dkl_intrlv = 1; label->dkl_rpm = tptr->dtype_rpm; if (!build_default_partition(label, cur_ctype->ctype_ctype)) return (NULL); part = zalloc(sizeof (struct partition_info)); part->pinfo_name = alloc_string(tptr->dtype_asciilabel); /* * Fill in the partition info from the label */ for (i = 0; i < NDKMAP; i++) { #if defined(_SUNOS_VTOC_8) part->pinfo_map[i] = label->dkl_map[i]; #else part->pinfo_map[i].dkl_cylno = label->dkl_vtoc.v_part[i].p_start / (blkaddr32_t)(tptr->dtype_nhead * tptr->dtype_nsect - apc); part->pinfo_map[i].dkl_nblk = label->dkl_vtoc.v_part[i].p_size; #endif /* ifdefined(_SUNOS_VTOC_8) */ } part->vtoc = label->dkl_vtoc; return (part); } /* * build new partition table for given disk type */ static void get_user_map_efi(struct dk_gpt *map, int float_part) { int i; efi_deflt_t efi_deflt; u_ioparam_t ioparam; char tmpstr[80]; uint64_t i64; uint64_t start_lba = map->efi_first_u_lba; uint64_t reserved; reserved = efi_reserved_sectors(map); for (i = 0; i < map->efi_nparts - 1; i++) { /* GPT partition 7 is whole disk device, minor node "wd" */ if (i == float_part || i == 7) continue; ioparam.io_bounds.lower = start_lba; ioparam.io_bounds.upper = map->efi_last_u_lba; efi_deflt.start_sector = ioparam.io_bounds.lower; efi_deflt.end_sector = map->efi_parts[i].p_size; (void) sprintf(tmpstr, "Enter size of partition %d ", i); i64 = input(FIO_EFI, tmpstr, ':', &ioparam, (int *)&efi_deflt, DATA_INPUT); if (i64 == 0) { map->efi_parts[i].p_tag = V_UNASSIGNED; } else if ((i64 != 0) && (map->efi_parts[i].p_tag == V_UNASSIGNED)) { map->efi_parts[i].p_tag = V_USR; } if (i64 == 0) { map->efi_parts[i].p_start = 0; } else { map->efi_parts[i].p_start = start_lba; } map->efi_parts[i].p_size = i64; start_lba += i64; } map->efi_parts[float_part].p_start = start_lba; map->efi_parts[float_part].p_size = map->efi_last_u_lba + 1 - start_lba - reserved; map->efi_parts[float_part].p_tag = V_USR; if (map->efi_parts[float_part].p_size == 0) { map->efi_parts[float_part].p_size = 0; map->efi_parts[float_part].p_start = 0; map->efi_parts[float_part].p_tag = V_UNASSIGNED; fmt_print("Warning: No space left for HOG\n"); } for (i = 0; i < map->efi_nparts; i++) { if (map->efi_parts[i].p_tag == V_RESERVED) { map->efi_parts[i].p_start = map->efi_last_u_lba - reserved + 1; map->efi_parts[i].p_size = reserved; break; } } } void new_partitiontable(struct disk_type *tptr, struct disk_type *oldtptr) { struct partition_info *part; /* * check if disk geometry has changed , if so add new * partition table else copy the old partition table.(best guess). */ if ((oldtptr != NULL) && (tptr->dtype_ncyl == oldtptr->dtype_ncyl) && (tptr->dtype_nhead == oldtptr->dtype_nhead) && (tptr->dtype_nsect == oldtptr->dtype_nsect)) { part = (struct partition_info *) zalloc(sizeof (struct partition_info)); bcopy((char *)cur_parts, (char *)part, sizeof (struct partition_info)); part->pinfo_next = tptr->dtype_plist; tptr->dtype_plist = part; } else { #ifdef DEBUG if (cur_parts != NULL) { fmt_print("Warning: Partition Table is set"); fmt_print("to default partition table. \n"); } #endif if (tptr->dtype_plist == NULL) { part = (struct partition_info *)build_partition(tptr); if (part != NULL) { part->pinfo_next = tptr->dtype_plist; tptr->dtype_plist = part; } } } } /* * 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 (c) 1991-2001 by Sun Microsystems, Inc. * All rights reserved. */ #ifndef _MODIFY_PARTITION_H #define _MODIFY_PARTITION_H #ifdef __cplusplus extern "C" { #endif /* * Prototypes for ANSI C compilers */ int p_modify(void); void new_partitiontable(struct disk_type *tptr, struct disk_type *oldtype); #ifdef __cplusplus } #endif #endif /* _MODIFY_PARTITION_H */ /* * 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 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _PARAM_H #define _PARAM_H #ifdef __cplusplus extern "C" { #endif #ifndef UINT16_MAX #define UINT16_MAX 0xffffU #endif #ifndef UINT32_MAX #define UINT32_MAX 0xffffffffU #endif #ifndef INT32_MAX #define INT32_MAX 0x7fffffff #endif /* * This file contains declarations of miscellaneous parameters. */ #ifndef SECSIZE #define SECSIZE DEV_BSIZE #endif #define MAX_CYLS (0x10000 - 1) /* max legal cylinder count */ #define MAX_HEADS (0x10000 - 1) /* max legal head count */ #define MAX_SECTS (0x10000 - 1) /* max legal sector count */ #define MIN_RPM 2000 /* min legal rpm */ #define AVG_RPM 3600 /* default rpm */ #define MAX_RPM 76000 /* max legal rpm */ #define MIN_BPS 512 /* min legal bytes/sector */ #define AVG_BPS 600 /* default bytes/sector */ #define MAX_BPS 1000 /* max legal bytes/sector */ #define INFINITY 0xffffffffU /* a big number */ #define MAXBLKS(heads, spt) UINT16_MAX * heads * spt, heads, spt #ifdef __cplusplus } #endif #endif /* _PARAM_H */ /* * 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 (c) 1991, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2016 by Delphix. All rights reserved. */ /* * This file contains functions that operate on partition tables. */ #include #include #include "global.h" #include "partition.h" #include "misc.h" #include "menu_command.h" #include "menu_partition.h" /* * Default vtoc information for non-SVr4 partitions */ struct dk_map2 default_vtoc_map[NDKMAP] = { { V_ROOT, 0 }, /* a - 0 */ { V_SWAP, V_UNMNT }, /* b - 1 */ { V_BACKUP, V_UNMNT }, /* c - 2 */ { V_UNASSIGNED, 0 }, /* d - 3 */ { V_UNASSIGNED, 0 }, /* e - 4 */ { V_UNASSIGNED, 0 }, /* f - 5 */ { V_USR, 0 }, /* g - 6 */ { V_UNASSIGNED, 0 }, /* h - 7 */ #if defined(_SUNOS_VTOC_16) #if defined(i386) { V_BOOT, V_UNMNT }, /* i - 8 */ { V_ALTSCTR, 0 }, /* j - 9 */ #else #error No VTOC format defined. #endif /* defined(i386) */ { V_UNASSIGNED, 0 }, /* k - 10 */ { V_UNASSIGNED, 0 }, /* l - 11 */ { V_UNASSIGNED, 0 }, /* m - 12 */ { V_UNASSIGNED, 0 }, /* n - 13 */ { V_UNASSIGNED, 0 }, /* o - 14 */ { V_UNASSIGNED, 0 }, /* p - 15 */ #endif /* defined(_SUNOS_VTOC_16) */ }; /* * This routine finds the last usable sector in the partition table. * It skips the BACKUP partition. */ static uint64_t maxofN(struct dk_gpt *map) { uint64_t max; uint64_t sec_no[2], start[2], size[2]; int i; for (i = 0; i < map->efi_nparts - 1; i++) { start[0] = map->efi_parts[i].p_start; size[0] = map->efi_parts[i].p_size; sec_no[0] = start[0] + size[0]; start[1] = map->efi_parts[i + 1].p_start; size[1] = map->efi_parts[i + 1].p_size; sec_no[1] = start[1] + size[1]; if (map->efi_parts[i].p_tag == V_BACKUP) { sec_no[0] = 0; } if (map->efi_parts[i+1].p_tag == V_BACKUP) { sec_no[1] = 0; } if (i == 0) { max = sec_no[1]; } if (sec_no[0] > max) { max = sec_no[0]; } else { max = max; } } if (max == 0) max = map->efi_first_u_lba; return (max); } /* * This routine allows the user to change the boundaries of the given * partition in the current partition map. */ void change_partition(int num) { uint_t i; uint64_t i64, j64; uint_t j; int deflt; part_deflt_t p_deflt; u_ioparam_t ioparam; int tag; int flag; char msg[256]; blkaddr32_t cyl_offset = 0; efi_deflt_t efi_deflt; /* * check if there exists a partition table for the disk. */ if (cur_parts == NULL) { err_print("Current Disk has no partition table.\n"); return; } if (cur_label == L_TYPE_EFI) { if (num > cur_parts->etoc->efi_nparts - 1) { err_print("Invalid partition for EFI label\n"); return; } print_efi_partition(cur_parts->etoc, num, 1); fmt_print("\n"); /* * Prompt for p_tag and p_flag values for this partition */ deflt = cur_parts->etoc->efi_parts[num].p_tag; if (deflt == V_UNASSIGNED) { deflt = V_USR; } (void) sprintf(msg, "Enter partition id tag"); ioparam.io_slist = ptag_choices; tag = input(FIO_SLIST, msg, ':', &ioparam, &deflt, DATA_INPUT); deflt = cur_parts->etoc->efi_parts[num].p_flag; (void) sprintf(msg, "Enter partition permission flags"); ioparam.io_slist = pflag_choices; flag = input(FIO_SLIST, msg, ':', &ioparam, &deflt, DATA_INPUT); ioparam.io_bounds.lower = cur_parts->etoc->efi_first_u_lba; ioparam.io_bounds.upper = cur_parts->etoc->efi_last_u_lba; efi_deflt.start_sector = maxofN(cur_parts->etoc); if ((cur_parts->etoc->efi_parts[num].p_start != 0) && (cur_parts->etoc->efi_parts[num].p_size != 0)) { efi_deflt.start_sector = cur_parts->etoc->efi_parts[num].p_start; } efi_deflt.end_sector = ioparam.io_bounds.upper - efi_deflt.start_sector; i64 = input(FIO_INT64, "Enter new starting Sector", ':', &ioparam, (int *)&efi_deflt, DATA_INPUT); ioparam.io_bounds.lower = 0; ioparam.io_bounds.upper = cur_parts->etoc->efi_last_u_lba; efi_deflt.end_sector = cur_parts->etoc->efi_parts[num].p_size; efi_deflt.start_sector = i64; j64 = input(FIO_EFI, "Enter partition size", ':', &ioparam, (int *)&efi_deflt, DATA_INPUT); if (j64 == 0) { tag = V_UNASSIGNED; i64 = 0; } else if ((j64 != 0) && (tag == V_UNASSIGNED)) { tag = V_USR; } if (cur_parts->pinfo_name != NULL) make_partition(); cur_parts->etoc->efi_parts[num].p_tag = tag; cur_parts->etoc->efi_parts[num].p_flag = flag; cur_parts->etoc->efi_parts[num].p_start = i64; cur_parts->etoc->efi_parts[num].p_size = j64; /* * We are now done with EFI part, so return now */ return; } /* * Print out the given partition so the user knows what they're * getting into. */ print_partition(cur_parts, num, 1); fmt_print("\n"); /* * Prompt for p_tag and p_flag values for this partition. */ assert(cur_parts->vtoc.v_version == V_VERSION); deflt = cur_parts->vtoc.v_part[num].p_tag; (void) sprintf(msg, "Enter partition id tag"); ioparam.io_slist = ptag_choices; tag = input(FIO_SLIST, msg, ':', &ioparam, &deflt, DATA_INPUT); deflt = cur_parts->vtoc.v_part[num].p_flag; (void) sprintf(msg, "Enter partition permission flags"); ioparam.io_slist = pflag_choices; flag = input(FIO_SLIST, msg, ':', &ioparam, &deflt, DATA_INPUT); /* * Ask for the new values. The old values are the defaults, and * strict bounds checking is done on the values given. */ #if defined(i386) if (tag != V_UNASSIGNED && tag != V_BACKUP && tag != V_BOOT) { /* * Determine cyl offset for boot and alternate partitions. * Assuming that the alternate sectors partition (slice) * physical location immediately follows the boot * partition and partition sizes are expressed in multiples * of cylinder size. */ cyl_offset = cur_parts->pinfo_map[I_PARTITION].dkl_cylno + 1; if (tag != V_ALTSCTR) { if (cur_parts->pinfo_map[J_PARTITION].dkl_nblk != 0) { cyl_offset = cur_parts-> pinfo_map[J_PARTITION].dkl_cylno + ((cur_parts-> pinfo_map[J_PARTITION].dkl_nblk + (spc() - 1)) / spc()); } } } #endif /* defined(i386) */ ioparam.io_bounds.lower = 0; ioparam.io_bounds.upper = ncyl - 1; deflt = max(cur_parts->pinfo_map[num].dkl_cylno, cyl_offset); i = (uint_t)input(FIO_INT, "Enter new starting cyl", ':', &ioparam, &deflt, DATA_INPUT); ioparam.io_bounds.lower = 0; ioparam.io_bounds.upper = (ncyl - i) * spc(); /* fill in defaults for the current partition */ p_deflt.start_cyl = i; p_deflt.deflt_size = min(cur_parts->pinfo_map[num].dkl_nblk, ioparam.io_bounds.upper); /* call input, passing p_deflt's address, typecast to (int *) */ j = (uint_t)input(FIO_ECYL, "Enter partition size", ':', &ioparam, (int *)&p_deflt, DATA_INPUT); /* * If the current partition has a size of zero change the * tag to Unassigned and the starting cylinder to zero */ if (j == 0) { tag = V_UNASSIGNED; i = 0; } #if defined(i386) if (i < cyl_offset && tag != V_UNASSIGNED && tag != V_BACKUP && tag != V_BOOT) { /* * This slice overlaps boot and/or alternates slice * Check if it's the boot or alternates slice and warn * accordingly */ if (i < cur_parts->pinfo_map[I_PARTITION].dkl_cylno + 1) { fmt_print("\nWarning: Partition overlaps boot "); fmt_print("partition. Specify different start cyl.\n"); return; } /* * Cyl offset for alternates partition was calculated before */ if (i < cyl_offset) { fmt_print("\nWarning: Partition overlaps alternates "); fmt_print("partition. Specify different start cyl.\n"); return; } } #endif /* defined(i386) */ /* * If user has entered a V_BACKUP tag then the partition * size should specify full disk capacity else * return an Error. */ if (tag == V_BACKUP) { uint_t fullsz; fullsz = ncyl * nhead * nsect; if (fullsz != j) { /* * V_BACKUP Tag Partition != full disk capacity. * print useful messages. */ fmt_print("\nWarning: Partition with V_BACKUP tag should "); fmt_print("specify full disk capacity. \n"); return; } } /* * If the current partition is named, we can't change it. * We create a new current partition map instead. */ if (cur_parts->pinfo_name != NULL) make_partition(); /* * Change the values. */ cur_parts->pinfo_map[num].dkl_cylno = i; cur_parts->pinfo_map[num].dkl_nblk = j; #if defined(_SUNOS_VTOC_16) cur_parts->vtoc.v_part[num].p_start = (daddr_t)(i * (nhead * nsect)); cur_parts->vtoc.v_part[num].p_size = (long)j; #endif /* defined(_SUNOS_VTOC_16) */ /* * Install the p_tag and p_flag values for this partition */ assert(cur_parts->vtoc.v_version == V_VERSION); cur_parts->vtoc.v_part[num].p_tag = (ushort_t)tag; cur_parts->vtoc.v_part[num].p_flag = (ushort_t)flag; } /* * This routine picks to closest partition table which matches the * selected disk type. It is called each time the disk type is * changed. If no match is found, it uses the first element * of the partition table. If no table exists, a dummy is * created. */ int get_partition(void) { register struct partition_info *pptr; register struct partition_info *parts; /* * If there are no pre-defined maps for this disk type, it's * an error. */ parts = cur_dtype->dtype_plist; if (parts == NULL) { err_print("No defined partition tables.\n"); make_partition(); return (-1); } /* * Loop through the pre-defined maps searching for one which match * disk type. If found copy it into unmamed partition. */ enter_critical(); for (pptr = parts; pptr != NULL; pptr = pptr->pinfo_next) { if (cur_dtype->dtype_asciilabel) { if (pptr->pinfo_name != NULL && strcmp(pptr->pinfo_name, cur_dtype->dtype_asciilabel) == 0) { /* * Set current partition and name it. */ cur_disk->disk_parts = cur_parts = pptr; cur_parts->pinfo_name = pptr->pinfo_name; exit_critical(); return (0); } } } /* * If we couldn't find a match, take the first one. * Set current partition and name it. */ cur_disk->disk_parts = cur_parts = cur_dtype->dtype_plist; cur_parts->pinfo_name = parts->pinfo_name; exit_critical(); return (0); } /* * This routine creates a new partition map and sets it current. If there * was a current map, the new map starts out identical to it. Otherwise * the new map starts out all zeroes. */ void make_partition(void) { register struct partition_info *pptr, *parts; int i; /* * Lock out interrupts so the lists don't get mangled. */ enter_critical(); /* * Get space for for the new map and link it into the list * of maps for the current disk type. */ pptr = (struct partition_info *)zalloc(sizeof (struct partition_info)); parts = cur_dtype->dtype_plist; if (parts == NULL) { cur_dtype->dtype_plist = pptr; } else { while (parts->pinfo_next != NULL) { parts = parts->pinfo_next; } parts->pinfo_next = pptr; pptr->pinfo_next = NULL; } /* * If there was a current map, copy its values. */ if (cur_label == L_TYPE_EFI) { struct dk_gpt *map; int nparts; int size; nparts = cur_parts->etoc->efi_nparts; size = sizeof (struct dk_part) * nparts + sizeof (struct dk_gpt); map = zalloc(size); (void) memcpy(map, cur_parts->etoc, size); pptr->etoc = map; cur_disk->disk_parts = cur_parts = pptr; exit_critical(); return; } if (cur_parts != NULL) { for (i = 0; i < NDKMAP; i++) { pptr->pinfo_map[i] = cur_parts->pinfo_map[i]; } pptr->vtoc = cur_parts->vtoc; } else { /* * Otherwise set initial default vtoc values */ set_vtoc_defaults(pptr); } /* * Make the new one current. */ cur_disk->disk_parts = cur_parts = pptr; exit_critical(); } /* * This routine deletes a partition map from the list of maps for * the given disk type. */ void delete_partition(struct partition_info *parts) { struct partition_info *pptr; /* * If there isn't a current map, it's an error. */ if (cur_dtype->dtype_plist == NULL) { err_print("Error: unexpected null partition list.\n"); fullabort(); } /* * Remove the map from the list. */ if (cur_dtype->dtype_plist == parts) cur_dtype->dtype_plist = parts->pinfo_next; else { for (pptr = cur_dtype->dtype_plist; pptr->pinfo_next != parts; pptr = pptr->pinfo_next) ; pptr->pinfo_next = parts->pinfo_next; } /* * Free the space it was using. */ destroy_data((char *)parts); } /* * Set all partition vtoc fields to defaults */ void set_vtoc_defaults(struct partition_info *part) { int i; bzero((caddr_t)&part->vtoc, sizeof (struct dk_vtoc)); part->vtoc.v_version = V_VERSION; part->vtoc.v_nparts = NDKMAP; part->vtoc.v_sanity = VTOC_SANE; for (i = 0; i < NDKMAP; i++) { part->vtoc.v_part[i].p_tag = default_vtoc_map[i].p_tag; part->vtoc.v_part[i].p_flag = default_vtoc_map[i].p_flag; } } /* * 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 (c) 1991-2001 by Sun Microsystems, Inc. */ #ifndef _PARTITION_H #define _PARTITION_H #ifdef __cplusplus extern "C" { #endif /* * Mnemonics for the partitions we recognize */ #define A_PARTITION 0 #define B_PARTITION 1 #define C_PARTITION 2 #define D_PARTITION 3 #define E_PARTITION 4 #define F_PARTITION 5 #define G_PARTITION 6 #define H_PARTITION 7 #if defined(i386) /* * the boot and alts slices only exist in the x86 disk layout. */ #define I_PARTITION 8 #define J_PARTITION 9 #endif /* defined(i386) */ /* * Prototypes for ANSI C compilers */ void change_partition(int num); int get_partition(void); void make_partition(void); void delete_partition(struct partition_info *parts); void set_vtoc_defaults(struct partition_info *part); extern struct dk_map2 default_vtoc_map[NDKMAP]; #ifdef __cplusplus } #endif #endif /* _PARTITION_H */ /* * 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. */ /* * This file contains functions to prompt the user for various * disk characteristics. By isolating these into functions, * we can guarantee that prompts, defaults, etc are identical. */ #include "global.h" #include "prompts.h" #include "io.h" #include "param.h" #include "startup.h" #ifdef sparc #include #endif /* * Prompt for max number of LBA */ uint64_t get_mlba(void) { u_ioparam_t ioparam; ioparam.io_bounds.lower = (1024 * 16) + 68; ioparam.io_bounds.upper = UINT_MAX64; return (input(FIO_INT64, "Enter maximum number of LBAs", ':', &ioparam, NULL, DATA_INPUT)); } /* * Prompt for number of cylinders */ int get_ncyl(void) { u_ioparam_t ioparam; ioparam.io_bounds.lower = 1; ioparam.io_bounds.upper = MAX_CYLS; return (input(FIO_INT, "Enter number of data cylinders", ':', &ioparam, NULL, DATA_INPUT)); } /* * Prompt for number of alternate cylinders */ int get_acyl(int n_cyls) { u_ioparam_t ioparam; int deflt; ioparam.io_bounds.lower = 2; ioparam.io_bounds.upper = MAX_CYLS - n_cyls; deflt = 2; return (input(FIO_INT, "Enter number of alternate cylinders", ':', &ioparam, &deflt, DATA_INPUT)); } /* * Prompt for number of physical cylinders */ int get_pcyl(int n_cyls, int a_cyls) { u_ioparam_t ioparam; int deflt; ioparam.io_bounds.lower = n_cyls + a_cyls; ioparam.io_bounds.upper = MAX_CYLS; deflt = n_cyls + a_cyls; return (input(FIO_INT, "Enter number of physical cylinders", ':', &ioparam, &deflt, DATA_INPUT)); } /* * Prompt for number of heads */ int get_nhead(void) { u_ioparam_t ioparam; ioparam.io_bounds.lower = 1; ioparam.io_bounds.upper = MAX_HEADS; return (input(FIO_INT, "Enter number of heads", ':', &ioparam, NULL, DATA_INPUT)); } /* * Prompt for number of physical heads */ int get_phead(int n_heads, ulong_t *options) { u_ioparam_t ioparam; int deflt; if (SCSI) { ioparam.io_bounds.lower = n_heads; ioparam.io_bounds.upper = INFINITY; if (input(FIO_OPINT, "Enter physical number of heads", ':', &ioparam, &deflt, DATA_INPUT)) { *options |= SUP_PHEAD; return (deflt); } } return (0); } /* * Prompt for number of sectors per track */ int get_nsect(void) { u_ioparam_t ioparam; ioparam.io_bounds.lower = 1; ioparam.io_bounds.upper = MAX_SECTS; return (input(FIO_INT, "Enter number of data sectors/track", ':', &ioparam, NULL, DATA_INPUT)); } /* * Prompt for number of physical sectors per track */ int get_psect(ulong_t *options) { u_ioparam_t ioparam; int deflt; if (SCSI) { ioparam.io_bounds.lower = 0; ioparam.io_bounds.upper = INFINITY; if (input(FIO_OPINT, "Enter number of physical sectors/track", ':', &ioparam, &deflt, DATA_INPUT)) { *options |= SUP_PSECT; return (deflt); } } return (0); } /* * Prompt for bytes per track */ int get_bpt(int n_sects, ulong_t *options) { u_ioparam_t ioparam; int deflt; if (SMD) { *options |= SUP_BPT; ioparam.io_bounds.lower = 1; ioparam.io_bounds.upper = INFINITY; deflt = n_sects * cur_blksz; return (input(FIO_INT, "Enter number of bytes/track", ':', &ioparam, &deflt, DATA_INPUT)); } return (0); } /* * Prompt for rpm */ int get_rpm(void) { u_ioparam_t ioparam; int deflt; ioparam.io_bounds.lower = MIN_RPM; ioparam.io_bounds.upper = MAX_RPM; deflt = AVG_RPM; return (input(FIO_INT, "Enter rpm of drive", ':', &ioparam, &deflt, DATA_INPUT)); } /* * Prompt for formatting time */ int get_fmt_time(ulong_t *options) { u_ioparam_t ioparam; int deflt; ioparam.io_bounds.lower = 0; ioparam.io_bounds.upper = INFINITY; if (input(FIO_OPINT, "Enter format time", ':', &ioparam, &deflt, DATA_INPUT)) { *options |= SUP_FMTTIME; return (deflt); } return (0); } /* * Prompt for cylinder skew */ int get_cyl_skew(ulong_t *options) { u_ioparam_t ioparam; int deflt; ioparam.io_bounds.lower = 0; ioparam.io_bounds.upper = INFINITY; if (input(FIO_OPINT, "Enter cylinder skew", ':', &ioparam, &deflt, DATA_INPUT)) { *options |= SUP_CYLSKEW; return (deflt); } return (0); } /* * Prompt for track skew */ int get_trk_skew(ulong_t *options) { u_ioparam_t ioparam; int deflt; ioparam.io_bounds.lower = 0; ioparam.io_bounds.upper = INFINITY; if (input(FIO_OPINT, "Enter track skew", ':', &ioparam, &deflt, DATA_INPUT)) { *options |= SUP_TRKSKEW; return (deflt); } return (0); } /* * Prompt for tracks per zone */ int get_trks_zone(ulong_t *options) { u_ioparam_t ioparam; int deflt; ioparam.io_bounds.lower = 0; ioparam.io_bounds.upper = INFINITY; if (input(FIO_OPINT, "Enter tracks per zone", ':', &ioparam, &deflt, DATA_INPUT)) { *options |= SUP_TRKS_ZONE; return (deflt); } return (0); } /* * Prompt for alternate tracks */ int get_atrks(ulong_t *options) { u_ioparam_t ioparam; int deflt; ioparam.io_bounds.lower = 0; ioparam.io_bounds.upper = INFINITY; if (input(FIO_OPINT, "Enter alternate tracks", ':', &ioparam, &deflt, DATA_INPUT)) { *options |= SUP_ATRKS; return (deflt); } return (0); } /* * Prompt for alternate sectors */ int get_asect(ulong_t *options) { u_ioparam_t ioparam; int deflt; ioparam.io_bounds.lower = 0; ioparam.io_bounds.upper = INFINITY; if (input(FIO_OPINT, "Enter alternate sectors", ':', &ioparam, &deflt, DATA_INPUT)) { *options |= SUP_ASECT; return (deflt); } return (0); } /* * Prompt for cache setting */ int get_cache(ulong_t *options) { u_ioparam_t ioparam; int deflt; ioparam.io_bounds.lower = 0; ioparam.io_bounds.upper = 0xff; if (input(FIO_OPINT, "Enter cache control", ':', &ioparam, &deflt, DATA_INPUT)) { *options |= SUP_CACHE; return (deflt); } return (0); } /* * Prompt for prefetch threshold */ int get_threshold(ulong_t *options) { u_ioparam_t ioparam; int deflt; ioparam.io_bounds.lower = 0; ioparam.io_bounds.upper = INFINITY; if (input(FIO_OPINT, "Enter prefetch threshold", ':', &ioparam, &deflt, DATA_INPUT)) { *options |= SUP_PREFETCH; return (deflt); } return (0); } /* * Prompt for minimum prefetch */ int get_min_prefetch(ulong_t *options) { u_ioparam_t ioparam; int deflt; ioparam.io_bounds.lower = 0; ioparam.io_bounds.upper = INFINITY; if (input(FIO_OPINT, "Enter minimum prefetch", ':', &ioparam, &deflt, DATA_INPUT)) { *options |= SUP_CACHE_MIN; return (deflt); } return (0); } /* * Prompt for maximum prefetch */ int get_max_prefetch(int min_prefetch, ulong_t *options) { u_ioparam_t ioparam; int deflt; ioparam.io_bounds.lower = min_prefetch; ioparam.io_bounds.upper = INFINITY; if (input(FIO_OPINT, "Enter maximum prefetch", ':', &ioparam, &deflt, DATA_INPUT)) { *options |= SUP_CACHE_MAX; return (deflt); } return (0); } /* * Prompt for bytes per sector */ int get_bps(void) { u_ioparam_t ioparam; int deflt; if (cur_ctype->ctype_flags & CF_SMD_DEFS) { ioparam.io_bounds.lower = MIN_BPS; ioparam.io_bounds.upper = MAX_BPS; deflt = AVG_BPS; return (input(FIO_INT, "Enter bytes per sector", ':', &ioparam, &deflt, DATA_INPUT)); } return (0); } /* * Prompt for ascii label */ char * get_asciilabel(void) { return ((char *)(uintptr_t)input(FIO_OSTR, "Enter disk type name (remember quotes)", ':', NULL, NULL, DATA_INPUT)); } /* * 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 1991-2002 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _PROMPTS_H #define _PROMPTS_H #ifdef __cplusplus extern "C" { #endif int get_ncyl(void); uint64_t get_mlba(void); int get_acyl(int n_cyls); int get_pcyl(int n_cyls, int p_cyls); int get_nhead(void); int get_phead(int n_heads, ulong_t *option); int get_nsect(void); int get_psect(ulong_t *option); int get_bpt(int n_sects, ulong_t *option); int get_rpm(void); int get_fmt_time(ulong_t *option); int get_cyl_skew(ulong_t *option); int get_trk_skew(ulong_t *option); int get_trks_zone(ulong_t *option); int get_atrks(ulong_t *option); int get_asect(ulong_t *option); int get_cache(ulong_t *option); int get_threshold(ulong_t *option); int get_min_prefetch(ulong_t *option); int get_max_prefetch(int min_prefetch, ulong_t *option); int get_bps(void); char *get_asciilabel(void); #ifdef __cplusplus } #endif #endif /* _PROMPTS_H */ /* * 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 (c) 1991-2001 by Sun Microsystems, Inc. */ #ifndef _SCSI_COM_H #define _SCSI_COM_H #ifdef __cplusplus extern "C" { #endif /* * Common definitions for SCSI routines. */ /* * Possible error levels. */ #define ERRLVL_COR 1 /* corrected error */ #define ERRLVL_RETRY 2 /* retryable error */ #define ERRLVL_FAULT 3 /* drive faulted */ #define ERRLVL_FATAL 4 /* fatal error */ #ifdef __cplusplus } #endif #endif /* _SCSI_COM_H */ /* * 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 2015 Nexenta Systems, Inc. All rights reserved. * * Copyright (c) 2011 Gary Mills * * Copyright (c) 1993, 2010, Oracle and/or its affiliates. All rights reserved. */ /* * This file contains the code to perform program startup. This * includes reading the data file and the search for disks. */ #include "global.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "startup.h" #include "param.h" #include "label.h" #include "misc.h" #include "menu_command.h" #include "partition.h" #include "ctlr_scsi.h" #include "auto_sense.h" extern struct ctlr_type ctlr_types[]; extern int nctypes; extern struct ctlr_ops genericops; extern long strtol(); extern int errno; char *file_name; char *option_d; char *option_f; char *option_l; char *option_p; char option_s; char *option_t; char *option_x; char diag_msg; char option_msg; int need_newline; int dev_expert; int expert_mode; uint_t cur_blksz; struct ctlr_info *ctlr_list; struct disk_info *disk_list; struct mctlr_list *controlp; char x86_devname[MAXNAMELEN]; FILE *data_file; #ifdef __STDC__ /* Function prototypes for ANSI C Compilers */ static void usage(void); static int sup_prxfile(void); static void sup_setpath(void); static void sup_setdtype(void); static int sup_change_spec(struct disk_type *, char *); static void sup_setpart(void); static void search_for_logical_dev(char *devname); static void add_device_to_disklist(char *devname, char *devpath); static int disk_is_known(struct dk_cinfo *dkinfo); static void datafile_error(char *errmsg, char *token); static void search_duplicate_dtypes(void); static void search_duplicate_pinfo(void); static void check_dtypes_for_inconsistency(struct disk_type *dp1, struct disk_type *dp2); static void check_pinfo_for_inconsistency(struct partition_info *pp1, struct partition_info *pp2); static uint_t str2blks(char *str); static int str2cyls(char *str); static struct chg_list *new_chg_list(struct disk_type *); static char *get_physical_name(char *); static void sort_disk_list(void); static int disk_name_compare(const void *, const void *); static void make_controller_list(void); static void check_for_duplicate_disknames(char *arglist[]); #else /* __STDC__ */ /* Function prototypes for non-ANSI C Compilers */ static void usage(); static int sup_prxfile(); static void sup_setpath(); static void sup_setdtype(); static int sup_change_spec(); static void sup_setpart(); static void search_for_logical_dev(); static void add_device_to_disklist(); static int disk_is_known(); static void datafile_error(); static void search_duplicate_dtypes(); static void search_duplicate_pinfo(); static void check_dtypes_for_inconsistency(); static void check_pinfo_for_inconsistency(); static uint_t str2blks(); static int str2cyls(); static struct chg_list *new_chg_list(); static char *get_physical_name(); static void sort_disk_list(); static int disk_name_compare(); static void make_controller_list(); static void check_for_duplicate_disknames(); #endif /* __STDC__ */ #if defined(sparc) static char *other_ctlrs[] = { "ata" }; #define OTHER_CTLRS 1 #elif defined(i386) || defined(__amd64) static char *other_ctlrs[] = { "ISP-80" }; #define OTHER_CTLRS 2 #else #error No Platform defined. #endif /* * This global is used to store the current line # in the data file. * It must be global because the I/O routines are allowed to side * effect it to keep track of backslashed newlines. */ int data_lineno; /* current line # in data file */ /* * Search path as defined in the format.dat files */ static char **search_path = NULL; static int name_represents_wholedisk(char *name); static void get_disk_name(int fd, char *disk_name, struct disk_info *disk_info); /* * This routine digests the options on the command line. It returns * the index into argv of the first string that is not an option. If * there are none, it returns -1. */ int do_options(int argc, char *argv[]) { char *ptr; int i; int next; /* * Default is no extended messages. Can be enabled manually. */ option_msg = 0; diag_msg = 0; expert_mode = 0; need_newline = 0; dev_expert = 0; /* * Loop through the argument list, incrementing each time by * an amount determined by the options found. */ for (i = 1; i < argc; i = next) { /* * Start out assuming an increment of 1. */ next = i + 1; /* * As soon as we hit a non-option, we're done. */ if (*argv[i] != '-') return (i); /* * Loop through all the characters in this option string. */ for (ptr = argv[i] + 1; *ptr != '\0'; ptr++) { /* * Determine each option represented. For options * that use a second string, increase the increment * of the main loop so they aren't re-interpreted. */ switch (*ptr) { case 's': case 'S': option_s = 1; break; case 'f': case 'F': option_f = argv[next++]; if (next > argc) goto badopt; break; case 'l': case 'L': option_l = argv[next++]; if (next > argc) goto badopt; break; case 'x': case 'X': option_x = argv[next++]; if (next > argc) goto badopt; break; case 'd': case 'D': option_d = argv[next++]; if (next > argc) goto badopt; break; case 't': case 'T': option_t = argv[next++]; if (next > argc) goto badopt; break; case 'p': case 'P': option_p = argv[next++]; if (next > argc) goto badopt; break; case 'm': option_msg = 1; break; case 'M': option_msg = 1; diag_msg = 1; break; case 'e': expert_mode = 1; break; #ifdef DEBUG case 'z': dev_expert = 1; break; #endif default: badopt: usage(); break; } } } /* * All the command line strings were options. Return that fact. */ return (-1); } static void usage(void) { err_print("Usage: format [-s][-d disk_name]"); err_print("[-t disk_type][-p partition_name]\n"); err_print("\t[-f cmd_file][-l log_file]"); err_print("[-x data_file] [-m] [-M] [-e] disk_list\n"); fullabort(); } /* * This routine reads in and digests the data file. The data file contains * definitions for the search path, known disk types, and known partition * maps. * * Note: for each file being processed, file_name is a pointer to that * file's name. We are careful to make sure that file_name points to * globally-accessible data, not data on the stack, because each * disk/partition/controller definition now keeps a pointer to the * filename in which it was defined. In the case of duplicate, * conflicting definitions, we can thus tell the user exactly where * the problem is occurring. */ void sup_init(void) { int nopened_files = 0; char fname[MAXPATHLEN]; char *path; char *p; struct stat stbuf; /* * Create a singly-linked list of controller types so that we may * dynamically add unknown controllers to this for 3'rd * party disk support. */ make_controller_list(); /* * If a data file was specified on the command line, use it first * If the file cannot be opened, fail. We want to guarantee * that, if the user explicitly names a file, they can * access it. * * option_x is already global, no need to dup it on the heap. */ if (option_x) { file_name = option_x; if (sup_prxfile()) { nopened_files++; } else { err_print("Unable to open data file '%s' - %s.\n", file_name, strerror(errno)); fullabort(); } } /* * Now look for an environment variable FORMAT_PATH. * If found, we use it as a colon-separated list * of directories. If no such environment variable * is defined, use a default path of "/etc". */ path = getenv("FORMAT_PATH"); if (path == NULL) { path = "/etc"; } /* * Traverse the path one file at a time. Pick off * the file name, and append the name "format.dat" * at the end of the pathname. * Whatever string we construct, duplicate it on the * heap, so that file_name is globally accessible. */ while (*path != 0) { p = fname; while (*path != 0 && *path != ':') *p++ = *path++; if (p == fname) continue; *p = 0; if (*path == ':') path++; /* * If the path we have so far is a directory, * look for a format.dat file in that directory, * otherwise try using the path name specified. * This permits arbitrary file names in the * path specification, if this proves useful. */ if (stat(fname, &stbuf) == -1) { err_print("Unable to access '%s' - %s.\n", fname, strerror(errno)); } else { if (S_ISDIR(stbuf.st_mode)) { if (*(p-1) != '/') *p++ = '/'; (void) strcpy(p, "format.dat"); } file_name = alloc_string(fname); if (sup_prxfile()) { nopened_files++; } } } /* * Check for duplicate disk or partitions definitions * that are inconsistent - this would be very confusing. */ search_duplicate_dtypes(); search_duplicate_pinfo(); } /* * Open and process a format data file. Unfortunately, we use * globals: file_name for the file name, and data_file * for the descriptor. Return true if able to open the file. */ static int sup_prxfile(void) { int status; TOKEN token; TOKEN cleaned; /* * Open the data file. Return 0 if unable to do so. */ data_file = fopen(file_name, "r"); if (data_file == NULL) { return (0); } /* * Step through the data file a meta-line at a time. There are * typically several backslashed newlines in each meta-line, * so data_lineno will be getting side effected along the way. */ data_lineno = 0; for (;;) { data_lineno++; /* * Get the keyword. */ status = sup_gettoken(token); /* * If we hit the end of the data file, we're done. */ if (status == SUP_EOF) break; /* * If the line is blank, skip it. */ if (status == SUP_EOL) continue; /* * If the line starts with some key character, it's an error. */ if (status != SUP_STRING) { datafile_error("Expecting keyword, found '%s'", token); continue; } /* * Clean up the token and see which keyword it is. Call * the appropriate routine to process the rest of the line. */ clean_token(cleaned, token); if (strcmp(cleaned, "search_path") == 0) sup_setpath(); else if (strcmp(cleaned, "disk_type") == 0) sup_setdtype(); else if (strcmp(cleaned, "partition") == 0) sup_setpart(); else { datafile_error("Unknown keyword '%s'", cleaned); } } /* * Close the data file. */ (void) fclose(data_file); return (1); } /* * This routine processes a 'search_path' line in the data file. The * search path is a list of disk names that will be searched for by the * program. * * The static path_size and path_alloc are used to build up the * list of files comprising the search path. The static definitions * enable supporting multiple search path definitions. */ static void sup_setpath(void) { TOKEN token; TOKEN cleaned; int status; static int path_size; static int path_alloc; /* * Pull in some grammar. */ status = sup_gettoken(token); if (status != SUP_EQL) { datafile_error("Expecting '=', found '%s'", token); return; } /* * Loop through the entries. */ for (;;) { /* * Pull in the disk name. */ status = sup_gettoken(token); /* * If we hit end of line, we're done. */ if (status == SUP_EOL) break; /* * If we hit some key character, it's an error. */ if (status != SUP_STRING) { datafile_error("Expecting value, found '%s'", token); break; } clean_token(cleaned, token); /* * Build the string into an argvlist. This array * is dynamically sized, as necessary, and terminated * with a null. Each name is alloc'ed on the heap, * so no dangling references. */ search_path = build_argvlist(search_path, &path_size, &path_alloc, cleaned); /* * Pull in some grammar. */ status = sup_gettoken(token); if (status == SUP_EOL) break; if (status != SUP_COMMA) { datafile_error("Expecting ', ', found '%s'", token); break; } } } /* * This routine processes a 'disk_type' line in the data file. It defines * the physical attributes of a brand of disk when connected to a specific * controller type. */ static void sup_setdtype(void) { TOKEN token, cleaned, ident; int val, status, i; ulong_t flags = 0; struct disk_type *dtype, *type; struct ctlr_type *ctype; char *dtype_name, *ptr; struct mctlr_list *mlp; /* * Pull in some grammar. */ status = sup_gettoken(token); if (status != SUP_EQL) { datafile_error("Expecting '=', found '%s'", token); return; } /* * Pull in the name of the disk type. */ status = sup_gettoken(token); if (status != SUP_STRING) { datafile_error("Expecting value, found '%s'", token); return; } clean_token(cleaned, token); /* * Allocate space for the disk type and copy in the name. */ dtype_name = (char *)zalloc(strlen(cleaned) + 1); (void) strcpy(dtype_name, cleaned); dtype = (struct disk_type *)zalloc(sizeof (struct disk_type)); dtype->dtype_asciilabel = dtype_name; /* * Save the filename/linenumber where this disk was defined */ dtype->dtype_filename = file_name; dtype->dtype_lineno = data_lineno; /* * Loop for each attribute. */ for (;;) { /* * Pull in some grammar. */ status = sup_gettoken(token); /* * If we hit end of line, we're done. */ if (status == SUP_EOL) break; if (status != SUP_COLON) { datafile_error("Expecting ':', found '%s'", token); return; } /* * Pull in the attribute. */ status = sup_gettoken(token); /* * If we hit end of line, we're done. */ if (status == SUP_EOL) break; /* * If we hit a key character, it's an error. */ if (status != SUP_STRING) { datafile_error("Expecting keyword, found '%s'", token); return; } clean_token(ident, token); /* * Check to see if we've got a change specification * If so, this routine will parse the entire * specification, so just restart at top of loop */ if (sup_change_spec(dtype, ident)) { continue; } /* * Pull in more grammar. */ status = sup_gettoken(token); if (status != SUP_EQL) { datafile_error("Expecting '=', found '%s'", token); return; } /* * Pull in the value of the attribute. */ status = sup_gettoken(token); if (status != SUP_STRING) { datafile_error("Expecting value, found '%s'", token); return; } clean_token(cleaned, token); /* * If the attribute defined the ctlr... */ if (strcmp(ident, "ctlr") == 0) { /* * Match the value with a ctlr type. */ mlp = controlp; while (mlp != NULL) { if (strcmp(mlp->ctlr_type->ctype_name, cleaned) == 0) break; mlp = mlp->next; } /* * If we couldn't match it, it's an error. */ if (mlp == NULL) { for (i = 0; i < OTHER_CTLRS; i++) { if (strcmp(other_ctlrs[i], cleaned) == 0) { datafile_error(NULL, NULL); return; } } if (i == OTHER_CTLRS) { datafile_error( "Unknown controller '%s'", cleaned); return; } } /* * Found a match. Add this disk type to the list * for the ctlr type if we can complete the * disk specification correctly. */ ctype = mlp->ctlr_type; flags |= SUP_CTLR; continue; } /* * All other attributes require a numeric value. Convert * the value to a number. */ val = (int)strtol(cleaned, &ptr, 0); if (*ptr != '\0') { datafile_error("Expecting an integer, found '%s'", cleaned); return; } /* * Figure out which attribute it was and fill in the * appropriate value. Also note that the attribute * has been defined. */ if (strcmp(ident, "ncyl") == 0) { dtype->dtype_ncyl = val; flags |= SUP_NCYL; } else if (strcmp(ident, "acyl") == 0) { dtype->dtype_acyl = val; flags |= SUP_ACYL; } else if (strcmp(ident, "pcyl") == 0) { dtype->dtype_pcyl = val; flags |= SUP_PCYL; } else if (strcmp(ident, "nhead") == 0) { dtype->dtype_nhead = val; flags |= SUP_NHEAD; } else if (strcmp(ident, "nsect") == 0) { dtype->dtype_nsect = val; flags |= SUP_NSECT; } else if (strcmp(ident, "rpm") == 0) { dtype->dtype_rpm = val; flags |= SUP_RPM; } else if (strcmp(ident, "bpt") == 0) { dtype->dtype_bpt = val; flags |= SUP_BPT; } else if (strcmp(ident, "bps") == 0) { dtype->dtype_bps = val; flags |= SUP_BPS; } else if (strcmp(ident, "drive_type") == 0) { dtype->dtype_dr_type = val; flags |= SUP_DRTYPE; } else if (strcmp(ident, "cache") == 0) { dtype->dtype_cache = val; flags |= SUP_CACHE; } else if (strcmp(ident, "prefetch") == 0) { dtype->dtype_threshold = val; flags |= SUP_PREFETCH; } else if (strcmp(ident, "read_retries") == 0) { dtype->dtype_read_retries = val; flags |= SUP_READ_RETRIES; } else if (strcmp(ident, "write_retries") == 0) { dtype->dtype_write_retries = val; flags |= SUP_WRITE_RETRIES; } else if (strcmp(ident, "min_prefetch") == 0) { dtype->dtype_prefetch_min = val; flags |= SUP_CACHE_MIN; } else if (strcmp(ident, "max_prefetch") == 0) { dtype->dtype_prefetch_max = val; flags |= SUP_CACHE_MAX; } else if (strcmp(ident, "trks_zone") == 0) { dtype->dtype_trks_zone = val; flags |= SUP_TRKS_ZONE; } else if (strcmp(ident, "atrks") == 0) { dtype->dtype_atrks = val; flags |= SUP_ATRKS; } else if (strcmp(ident, "asect") == 0) { dtype->dtype_asect = val; flags |= SUP_ASECT; } else if (strcmp(ident, "psect") == 0) { dtype->dtype_psect = val; flags |= SUP_PSECT; } else if (strcmp(ident, "phead") == 0) { dtype->dtype_phead = val; flags |= SUP_PHEAD; } else if (strcmp(ident, "fmt_time") == 0) { dtype->dtype_fmt_time = val; flags |= SUP_FMTTIME; } else if (strcmp(ident, "cyl_skew") == 0) { dtype->dtype_cyl_skew = val; flags |= SUP_CYLSKEW; } else if (strcmp(ident, "trk_skew") == 0) { dtype->dtype_trk_skew = val; flags |= SUP_TRKSKEW; } else { datafile_error("Unknown keyword '%s'", ident); } } /* * Check to be sure all the necessary attributes have been defined. * If any are missing, it's an error. Also, log options for later * use by specific driver. */ dtype->dtype_options = flags; if ((flags & SUP_MIN_DRIVE) != SUP_MIN_DRIVE) { datafile_error("Incomplete specification", ""); return; } if ((!(ctype->ctype_flags & CF_SCSI)) && (!(flags & SUP_BPT)) && (!(ctype->ctype_flags & CF_NOFORMAT))) { datafile_error("Incomplete specification", ""); return; } if ((ctype->ctype_flags & CF_SMD_DEFS) && (!(flags & SUP_BPS))) { datafile_error("Incomplete specification", ""); return; } /* * Add this disk type to the list for the ctlr type */ assert(flags & SUP_CTLR); type = ctype->ctype_dlist; if (type == NULL) { ctype->ctype_dlist = dtype; } else { while (type->dtype_next != NULL) type = type->dtype_next; type->dtype_next = dtype; } } /* * Parse a SCSI mode page change specification. * * Return: * 0: not change specification, continue parsing * 1: was change specification, it was ok, * or we already handled the error. */ static int sup_change_spec(struct disk_type *disk, char *id) { char *p; char *p2; int pageno; int byteno; int mode; int value; TOKEN token; TOKEN ident; struct chg_list *cp; int tilde; int i; /* * Syntax: p[|0x] */ if (*id != 'p') { return (0); } pageno = (int)strtol(id+1, &p2, 0); if (*p2 != 0) { return (0); } /* * Once we get this far, we know we have the * beginnings of a change specification. * If there's a problem now, report the problem, * and return 1, so that the caller can restart * parsing at the next expression. */ if (!scsi_supported_page(pageno)) { datafile_error("Unsupported mode page '%s'", id); return (1); } /* * Next token should be the byte offset */ if (sup_gettoken(token) != SUP_STRING) { datafile_error("Unexpected value '%s'", token); return (1); } clean_token(ident, token); /* * Syntax: b[|0x] */ p = ident; if (*p++ != 'b') { datafile_error("Unknown keyword '%s'", ident); return (1); } byteno = (int)strtol(p, &p2, 10); if (*p2 != 0) { datafile_error("Unknown keyword '%s'", ident); return (1); } if (byteno == 0 || byteno == 1) { datafile_error("Unsupported byte offset '%s'", ident); return (1); } /* * Get the operator for this expression */ mode = CHG_MODE_UNDEFINED; switch (sup_gettoken(token)) { case SUP_EQL: mode = CHG_MODE_ABS; break; case SUP_OR: if (sup_gettoken(token) == SUP_EQL) mode = CHG_MODE_SET; break; case SUP_AND: if (sup_gettoken(token) == SUP_EQL) mode = CHG_MODE_CLR; break; } if (mode == CHG_MODE_UNDEFINED) { datafile_error("Unexpected operator: '%s'", token); return (1); } /* * Get right-hand of expression - accept optional tilde */ tilde = 0; if ((i = sup_gettoken(token)) == SUP_TILDE) { tilde = 1; i = sup_gettoken(token); } if (i != SUP_STRING) { datafile_error("Expecting value, found '%s'", token); return (1); } clean_token(ident, token); value = (int)strtol(ident, &p, 0); if (*p != 0) { datafile_error("Expecting value, found '%s'", token); return (1); } /* * Apply the tilde operator, if found. * Constrain to a byte value. */ if (tilde) { value = ~value; } value &= 0xff; /* * We parsed a successful change specification expression. * Add it to the list for this disk type. */ cp = new_chg_list(disk); cp->pageno = pageno; cp->byteno = byteno; cp->mode = mode; cp->value = value; return (1); } /* * This routine processes a 'partition' line in the data file. It defines * a known partition map for a particular disk type on a particular * controller type. */ static void sup_setpart(void) { TOKEN token, cleaned, disk, ctlr, ident; struct disk_type *dtype = NULL; struct ctlr_type *ctype = NULL; struct partition_info *pinfo, *parts; char *pinfo_name; int i, index, status, flags = 0; uint_t val1, val2; ushort_t vtoc_tag; ushort_t vtoc_flag; struct mctlr_list *mlp; /* * Pull in some grammar. */ status = sup_gettoken(token); if (status != SUP_EQL) { datafile_error("Expecting '=', found '%s'", token); return; } /* * Pull in the name of the map. */ status = sup_gettoken(token); if (status != SUP_STRING) { datafile_error("Expecting value, found '%s'", token); return; } clean_token(cleaned, token); /* * Allocate space for the partition map and fill in the name. */ pinfo_name = (char *)zalloc(strlen(cleaned) + 1); (void) strcpy(pinfo_name, cleaned); pinfo = (struct partition_info *)zalloc(sizeof (struct partition_info)); pinfo->pinfo_name = pinfo_name; /* * Save the filename/linenumber where this partition was defined */ pinfo->pinfo_filename = file_name; pinfo->pinfo_lineno = data_lineno; /* * Install default vtoc information into the new partition table */ set_vtoc_defaults(pinfo); /* * Loop for each attribute in the line. */ for (;;) { /* * Pull in some grammar. */ status = sup_gettoken(token); /* * If we hit end of line, we're done. */ if (status == SUP_EOL) break; if (status != SUP_COLON) { datafile_error("Expecting ':', found '%s'", token); return; } /* * Pull in the attribute. */ status = sup_gettoken(token); /* * If we hit end of line, we're done. */ if (status == SUP_EOL) break; if (status != SUP_STRING) { datafile_error("Expecting keyword, found '%s'", token); return; } clean_token(ident, token); /* * Pull in more grammar. */ status = sup_gettoken(token); if (status != SUP_EQL) { datafile_error("Expecting '=', found '%s'", token); return; } /* * Pull in the value of the attribute. */ status = sup_gettoken(token); /* * If we hit a key character, it's an error. */ if (status != SUP_STRING) { datafile_error("Expecting value, found '%s'", token); return; } clean_token(cleaned, token); /* * If the attribute is the ctlr, save the ctlr name and * mark it defined. */ if (strcmp(ident, "ctlr") == 0) { (void) strcpy(ctlr, cleaned); flags |= SUP_CTLR; continue; /* * If the attribute is the disk, save the disk name and * mark it defined. */ } else if (strcmp(ident, "disk") == 0) { (void) strcpy(disk, cleaned); flags |= SUP_DISK; continue; } /* * If we now know both the controller name and the * disk name, let's see if we can find the controller * and disk type. This will give us the geometry, * which can permit us to accept partitions specs * in cylinders or blocks. */ if (((flags & (SUP_DISK|SUP_CTLR)) == (SUP_DISK|SUP_CTLR)) && dtype == NULL && ctype == NULL) { /* * Attempt to match the specified ctlr to a known type. */ mlp = controlp; while (mlp != NULL) { if (strcmp(mlp->ctlr_type->ctype_name, ctlr) == 0) break; mlp = mlp->next; } /* * If no match is found, it's an error. */ if (mlp == NULL) { for (i = 0; i < OTHER_CTLRS; i++) { if (strcmp(other_ctlrs[i], ctlr) == 0) { datafile_error(NULL, NULL); return; } } if (i == OTHER_CTLRS) { datafile_error( "Unknown controller '%s'", ctlr); return; } } ctype = mlp->ctlr_type; /* * Attempt to match the specified disk to a known type. */ for (dtype = ctype->ctype_dlist; dtype != NULL; dtype = dtype->dtype_next) { if (strcmp(dtype->dtype_asciilabel, disk) == 0) break; } /* * If no match is found, it's an error. */ if (dtype == NULL) { datafile_error("Unknown disk '%s'", disk); return; } /* * Now that we know the disk type, set up the * globals that let that magic macro "spc()" * do it's thing. Sorry that this is glued * together so poorly... */ nhead = dtype->dtype_nhead; nsect = dtype->dtype_nsect; acyl = dtype->dtype_acyl; ncyl = dtype->dtype_ncyl; } /* * By now, the disk and controller type must be defined */ if (dtype == NULL || ctype == NULL) { datafile_error("Incomplete specification", ""); return; } /* * The rest of the attributes are all single letters. * Make sure the specified attribute is a single letter. */ if (strlen(ident) != 1) { datafile_error("Unknown keyword '%s'", ident); return; } /* * Also make sure it is within the legal range of letters. */ if (ident[0] < PARTITION_BASE || ident[0] > PARTITION_BASE+9) { datafile_error("Unknown keyword '%s'", ident); return; } /* * Here's the index of the partition we're dealing with */ index = ident[0] - PARTITION_BASE; /* * For SunOS 5.0, we support the additional syntax: * [, ] [, ] , * instead of: * , * * may be one of: boot, root, swap, etc. * consists of two characters: * W (writable) or R (read-only) * M (mountable) or U (unmountable) * * Start with the defaults assigned above: */ vtoc_tag = pinfo->vtoc.v_part[index].p_tag; vtoc_flag = pinfo->vtoc.v_part[index].p_flag; /* * First try to match token against possible tag values */ if (find_value(ptag_choices, cleaned, &i) == 1) { /* * Found valid tag. Use it and advance parser */ vtoc_tag = (ushort_t)i; status = sup_gettoken(token); if (status != SUP_COMMA) { datafile_error( "Expecting ', ', found '%s'", token); return; } status = sup_gettoken(token); if (status != SUP_STRING) { datafile_error("Expecting value, found '%s'", token); return; } clean_token(cleaned, token); } /* * Try to match token against possible flag values */ if (find_value(pflag_choices, cleaned, &i) == 1) { /* * Found valid flag. Use it and advance parser */ vtoc_flag = (ushort_t)i; status = sup_gettoken(token); if (status != SUP_COMMA) { datafile_error("Expecting ', ', found '%s'", token); return; } status = sup_gettoken(token); if (status != SUP_STRING) { datafile_error("Expecting value, found '%s'", token); return; } clean_token(cleaned, token); } /* * All other attributes have a pair of numeric values. * Convert the first value to a number. This value * is the starting cylinder number of the partition. */ val1 = str2cyls(cleaned); if (val1 == (uint_t)(-1)) { datafile_error("Expecting an integer, found '%s'", cleaned); return; } /* * Pull in some grammar. */ status = sup_gettoken(token); if (status != SUP_COMMA) { datafile_error("Expecting ', ', found '%s'", token); return; } /* * Pull in the second value. */ status = sup_gettoken(token); if (status != SUP_STRING) { datafile_error("Expecting value, found '%s'", token); return; } clean_token(cleaned, token); /* * Convert the second value to a number. This value * is the number of blocks composing the partition. * If the token is terminated with a 'c', the units * are cylinders, not blocks. Also accept a 'b', if * they choose to be so specific. */ val2 = str2blks(cleaned); if (val2 == (uint_t)(-1)) { datafile_error("Expecting an integer, found '%s'", cleaned); return; } /* * Fill in the appropriate map entry with the values. */ pinfo->pinfo_map[index].dkl_cylno = val1; pinfo->pinfo_map[index].dkl_nblk = val2; pinfo->vtoc.v_part[index].p_tag = vtoc_tag; pinfo->vtoc.v_part[index].p_flag = vtoc_flag; #if defined(_SUNOS_VTOC_16) pinfo->vtoc.v_part[index].p_start = val1 * (nhead * nsect); pinfo->vtoc.v_part[index].p_size = val2; if (val2 == 0) { pinfo->vtoc.v_part[index].p_tag = 0; pinfo->vtoc.v_part[index].p_flag = 0; pinfo->vtoc.v_part[index].p_start = 0; pinfo->pinfo_map[index].dkl_cylno = 0; } #endif /* defined(_SUNOS_VTOC_16) */ } /* * Check to be sure that all necessary attributes were defined. */ if ((flags & SUP_MIN_PART) != SUP_MIN_PART) { datafile_error("Incomplete specification", ""); return; } /* * Add this partition map to the list of known maps for the * specified disk/ctlr. */ parts = dtype->dtype_plist; if (parts == NULL) dtype->dtype_plist = pinfo; else { while (parts->pinfo_next != NULL) parts = parts->pinfo_next; parts->pinfo_next = pinfo; } } /* * Open the disk device - just a wrapper for open. */ int open_disk(char *diskname, int flags) { return (open(diskname, flags)); } /* * This routine performs the disk search during startup. It looks for * all the disks in the search path, and creates a list of those that * are found. */ void do_search(char *arglist[]) { char **sp; DIR *dir; struct dirent *dp; char s[MAXPATHLEN]; char path[MAXPATHLEN]; char curdir[MAXPATHLEN]; char *directory = "/dev/rdsk"; struct disk_info *disk; int i; /* * Change directory to the device directory. This * gives us the most efficient access to that directory. * Remember where we were, and return there when finished. */ if (getcwd(curdir, sizeof (curdir)) == NULL) { err_print("Cannot get current directory - %s\n", strerror(errno)); fullabort(); } if (chdir(directory) == -1) { err_print("Cannot set directory to %s - %s\n", directory, strerror(errno)); fullabort(); } /* * If there were disks specified on the command line, * use those disks, and nothing but those disks. */ if (arglist != NULL) { check_for_duplicate_disknames(arglist); for (; *arglist != NULL; arglist++) { search_for_logical_dev(*arglist); } } else { /* * If there were no disks specified on the command line, * search for all disks attached to the system. */ fmt_print("Searching for disks..."); (void) fflush(stdout); need_newline = 1; /* * Find all disks specified in search_path definitions * in whatever format.dat files were processed. */ sp = search_path; if (sp != NULL) { while (*sp != NULL) { search_for_logical_dev(*sp++); } } /* * Open the device directory */ if ((dir = opendir(".")) == NULL) { err_print("Cannot open %s - %s\n", directory, strerror(errno)); fullabort(); } /* * Now find all usable nodes in /dev/rdsk (or /dev, if 4.x) * First find all nodes which do not conform to * standard disk naming conventions. This permits * all user-defined names to override the default names. */ while ((dp = readdir(dir)) != NULL) { if (strcmp(dp->d_name, ".") == 0 || strcmp(dp->d_name, "..") == 0) continue; if (!conventional_name(dp->d_name)) { if (!fdisk_physical_name(dp->d_name)) { /* * If non-conventional name represents * a link to non-s2 slice , ignore it. */ if (!name_represents_wholedisk (dp->d_name)) { (void) strcpy(path, directory); (void) strcat(path, "/"); (void) strcat(path, dp->d_name); add_device_to_disklist( dp->d_name, path); } } } } rewinddir(dir); /* * Now find all nodes corresponding to the standard * device naming conventions. */ while ((dp = readdir(dir)) != NULL) { if (strcmp(dp->d_name, ".") == 0 || strcmp(dp->d_name, "..") == 0) continue; if (whole_disk_name(dp->d_name)) { (void) strcpy(path, directory); (void) strcat(path, "/"); (void) strcat(path, dp->d_name); canonicalize_name(s, dp->d_name); add_device_to_disklist(s, path); } } /* * Close the directory */ if (closedir(dir) == -1) { err_print("Cannot close directory %s - %s\n", directory, strerror(errno)); fullabort(); } need_newline = 0; fmt_print("done\n"); } /* * Return to whence we came */ if (chdir(curdir) == -1) { err_print("Cannot set directory to %s - %s\n", curdir, strerror(errno)); fullabort(); } /* * If we didn't find any disks, give up. */ if (disk_list == NULL) { if (geteuid() == 0) { err_print("No disks found!\n"); } else { err_print("No permission (or no disks found)!\n"); } (void) fflush(stdout); fullabort(); } sort_disk_list(); /* * Tell user the results of the auto-configure process */ i = 0; for (disk = disk_list; disk != NULL; disk = disk->disk_next) { float scaled; diskaddr_t nblks; struct disk_type *type; if (disk->disk_flags & DSK_AUTO_CONFIG) { if (i++ == 0) { fmt_print("\n"); } fmt_print("%s: ", disk->disk_name); if (disk->disk_flags & DSK_LABEL_DIRTY) { fmt_print("configured "); } else { fmt_print("configured and labeled "); } type = disk->disk_type; nblks = type->dtype_ncyl * type->dtype_nhead * type->dtype_nsect; if (disk->label_type == L_TYPE_SOLARIS) scaled = bn2mb(nblks); else scaled = bn2mb(type->capacity); fmt_print("with capacity of "); if (scaled > 1024.0) { fmt_print("%1.2fGB\n", scaled/1024.0); } else { fmt_print("%1.2fMB\n", scaled); } } } } /* * For a given "logical" disk name as specified in a format.dat * search path, try to find the device it actually refers to. * Since we are trying to maintain 4.x naming convention * compatibility in 5.0, this involves a little bit of work. * We also want to be able to function under 4.x, if needed. * * canonical: standard name reference. append a partition * reference, and open that file in the device directory. * examples: SVR4: c0t0d0 * 4.x: sd0 * * absolute: begins with a '/', and is assumed to be an * absolute pathname to some node. * * relative: non-canonical, doesn't begin with a '/'. * assumed to be the name of a file in the appropriate * device directory. */ static void search_for_logical_dev(char *devname) { char path[MAXPATHLEN]; char *directory = "/dev/rdsk/"; char *partition = "s2"; /* * If the name is an absolute path name, accept it as is */ if (*devname == '/') { (void) strcpy(path, devname); } else if (canonical_name(devname)) { /* * If canonical name, construct a standard path name. */ (void) strcpy(path, directory); (void) strcat(path, devname); (void) strcat(path, partition); } else if (canonical4x_name(devname)) { /* * Check to see if it's a 4.x file name in the /dev * directory on 5.0. Here, we only accept the * canonicalized form: sd0. */ (void) strcpy(path, "/dev/r"); (void) strcat(path, devname); (void) strcat(path, "c"); } else { /* * If it's not a canonical name, then it may be a * reference to an actual file name in the device * directory itself. */ (void) strcpy(path, directory); (void) strcat(path, devname); } /* now add the device */ add_device_to_disklist(devname, path); } /* * Get the disk name from the inquiry data */ static void get_disk_name(int fd, char *disk_name, struct disk_info *disk_info) { struct scsi_inquiry inquiry; char *vid, *pid, *rid; if (get_disk_inquiry_prop(disk_info->devfs_name, &vid, &pid, &rid) == 0) { (void) snprintf(disk_name, MAXNAMELEN - 1, "%s-%s-%s", vid, pid, rid); free(vid); free(pid); free(rid); return; } if (uscsi_inquiry(fd, (char *)&inquiry, sizeof (inquiry))) { if (option_msg) err_print("\nInquiry failed - %s\n", strerror(errno)); (void) strcpy(disk_name, "Unknown-Unknown-0001"); return; } (void) get_generic_disk_name(disk_name, &inquiry); } /* * Add a device to the disk list, if it appears to be a disk, * and we haven't already found it under some other name. */ static void add_device_to_disklist(char *devname, char *devpath) { struct disk_info *search_disk; struct ctlr_info *search_ctlr; struct disk_type *search_dtype, *efi_disk; struct partition_info *search_parts; struct disk_info *dptr; struct ctlr_info *cptr; struct disk_type *type; struct partition_info *parts; struct dk_label search_label; struct dk_cinfo dkinfo; struct stat stbuf; struct ctlr_type *ctlr, *tctlr; struct mctlr_list *mlp; struct efi_info efi_info; struct dk_minfo mediainfo; int search_file; int status; int i; int access_flags = 0; char disk_name[MAXNAMELEN]; /* * Attempt to open the disk. If it fails, skip it. */ if ((search_file = open_disk(devpath, O_RDWR | O_NDELAY)) < 0) { return; } /* * Must be a character device */ if (fstat(search_file, &stbuf) == -1 || !S_ISCHR(stbuf.st_mode)) { (void) close(search_file); return; } /* * Attempt to read the configuration info on the disk. * Again, if it fails, we assume the disk's not there. * Note we must close the file for the disk before we * continue. */ if (ioctl(search_file, DKIOCINFO, &dkinfo) < 0) { (void) close(search_file); return; } /* If it is a removable media, skip it. */ if (!expert_mode) { int isremovable, ret; ret = ioctl(search_file, DKIOCREMOVABLE, &isremovable); if ((ret >= 0) && (isremovable != 0)) { (void) close(search_file); return; } } if (ioctl(search_file, DKIOCGMEDIAINFO, &mediainfo) == -1) { cur_blksz = DEV_BSIZE; } else { cur_blksz = mediainfo.dki_lbsize; } /* * If the type of disk is one we don't know about, * add it to the list. */ mlp = controlp; while (mlp != NULL) { if (mlp->ctlr_type->ctype_ctype == dkinfo.dki_ctype) { break; } mlp = mlp->next; } if (mlp == NULL) { if (dkinfo.dki_ctype == DKC_CDROM) { if (ioctl(search_file, DKIOCGMEDIAINFO, &mediainfo) < 0) { mediainfo.dki_media_type = DK_UNKNOWN; } } /* * Skip CDROM devices, they are read only. * But not devices like Iomega Rev Drive which * identifies itself as a CDROM, but has a removable * disk. */ if ((dkinfo.dki_ctype == DKC_CDROM) && (mediainfo.dki_media_type != DK_REMOVABLE_DISK)) { (void) close(search_file); return; } /* * create the new ctlr_type structure and fill it in. */ tctlr = zalloc(sizeof (struct ctlr_type)); tctlr->ctype_ctype = dkinfo.dki_ctype; tctlr->ctype_name = zalloc(DK_DEVLEN); if (strlcpy(tctlr->ctype_name, dkinfo.dki_cname, DK_DEVLEN) > DK_DEVLEN) { /* * DKIOCINFO returned a controller name longer * than DK_DEVLEN bytes, which means more of the * dk_cinfo structure may be corrupt. We don't * allow the user to perform any operations on * the device in this case */ err_print("\nError: Device %s: controller " "name (%s)\nis invalid. Device will not " "be displayed.\n", devname, dkinfo.dki_cname); (void) close(search_file); destroy_data(tctlr->ctype_name); destroy_data((char *)tctlr); return; } else { tctlr->ctype_ops = zalloc(sizeof (struct ctlr_ops)); /* * copy the generic disk ops structure into local copy. */ *(tctlr->ctype_ops) = genericops; tctlr->ctype_flags = CF_WLIST; mlp = controlp; while (mlp->next != NULL) { mlp = mlp->next; } mlp->next = zalloc(sizeof (struct mctlr_list)); mlp->next->ctlr_type = tctlr; } } /* * Search through all disks known at this time, to * determine if we're already identified this disk. * If so, then there's no need to include it a * second time. This permits the user-defined names * to supercede the standard conventional names. */ if (disk_is_known(&dkinfo)) { (void) close(search_file); return; } #if defined(sparc) /* * Because opening id with FNDELAY always succeeds, * read the label early on to see whether the device * really exists. A result of DSK_RESERVED * means the disk may be reserved. * In the future, it will be good * to move these into controller specific files and have a common * generic check for reserved disks here, including intel disks. */ if (dkinfo.dki_ctype == DKC_SCSI_CCS) { char *first_sector; first_sector = zalloc(cur_blksz); i = scsi_rdwr(DIR_READ, search_file, (diskaddr_t)0, 1, first_sector, F_SILENT, NULL); switch (i) { case DSK_RESERVED: access_flags |= DSK_RESERVED; break; case DSK_UNAVAILABLE: access_flags |= DSK_UNAVAILABLE; break; default: break; } free(first_sector); } #endif /* defined(sparc) */ /* * The disk appears to be present. Allocate space for the * disk structure and add it to the list of found disks. */ search_disk = (struct disk_info *)zalloc(sizeof (struct disk_info)); if (disk_list == NULL) disk_list = search_disk; else { for (dptr = disk_list; dptr->disk_next != NULL; dptr = dptr->disk_next) ; dptr->disk_next = search_disk; } /* * Fill in some info from the ioctls. */ search_disk->disk_dkinfo = dkinfo; if (is_efi_type(search_file)) { search_disk->label_type = L_TYPE_EFI; } else { search_disk->label_type = L_TYPE_SOLARIS; } /* * Remember the names of the disk */ search_disk->disk_name = alloc_string(devname); search_disk->disk_path = alloc_string(devpath); /* * Remember the lba size of the disk */ search_disk->disk_lbasize = cur_blksz; (void) strcpy(x86_devname, devname); /* * Determine if this device is linked to a physical name. */ search_disk->devfs_name = get_physical_name(devpath); /* * Try to match the ctlr for this disk with a ctlr we * have already found. A match is assumed if the ctlrs * are at the same address && ctypes agree */ for (search_ctlr = ctlr_list; search_ctlr != NULL; search_ctlr = search_ctlr->ctlr_next) if (search_ctlr->ctlr_addr == dkinfo.dki_addr && search_ctlr->ctlr_space == dkinfo.dki_space && search_ctlr->ctlr_ctype->ctype_ctype == dkinfo.dki_ctype) break; /* * If no match was found, we need to identify this ctlr. */ if (search_ctlr == NULL) { /* * Match the type of the ctlr to a known type. */ mlp = controlp; while (mlp != NULL) { if (mlp->ctlr_type->ctype_ctype == dkinfo.dki_ctype) break; mlp = mlp->next; } /* * If no match was found, it's an error. * Close the disk and report the error. */ if (mlp == NULL) { err_print("\nError: found disk attached to "); err_print("unsupported controller type '%d'.\n", dkinfo.dki_ctype); (void) close(search_file); return; } /* * Allocate space for the ctlr structure and add it * to the list of found ctlrs. */ search_ctlr = (struct ctlr_info *) zalloc(sizeof (struct ctlr_info)); search_ctlr->ctlr_ctype = mlp->ctlr_type; if (ctlr_list == NULL) ctlr_list = search_ctlr; else { for (cptr = ctlr_list; cptr->ctlr_next != NULL; cptr = cptr->ctlr_next) ; cptr->ctlr_next = search_ctlr; } /* * Fill in info from the ioctl. */ for (i = 0; i < DK_DEVLEN; i++) { search_ctlr->ctlr_cname[i] = dkinfo.dki_cname[i]; search_ctlr->ctlr_dname[i] = dkinfo.dki_dname[i]; } /* * Make sure these can be used as simple strings */ search_ctlr->ctlr_cname[i] = 0; search_ctlr->ctlr_dname[i] = 0; search_ctlr->ctlr_flags = dkinfo.dki_flags; search_ctlr->ctlr_num = dkinfo.dki_cnum; search_ctlr->ctlr_addr = dkinfo.dki_addr; search_ctlr->ctlr_space = dkinfo.dki_space; search_ctlr->ctlr_prio = dkinfo.dki_prio; search_ctlr->ctlr_vec = dkinfo.dki_vec; } /* * By this point, we have a known ctlr. Link the disk * to the ctlr. */ search_disk->disk_ctlr = search_ctlr; if (access_flags & (DSK_RESERVED | DSK_UNAVAILABLE)) { if (access_flags & DSK_RESERVED) search_disk->disk_flags |= DSK_RESERVED; else search_disk->disk_flags |= DSK_UNAVAILABLE; (void) close(search_file); return; } else { search_disk->disk_flags &= ~(DSK_RESERVED | DSK_UNAVAILABLE); } /* * Attempt to read the primary label. * (Note that this is really through the DKIOCGVTOC * ioctl, then converted from vtoc to label.) */ if (search_disk->label_type == L_TYPE_SOLARIS) { status = read_label(search_file, &search_label); } else { status = read_efi_label(search_file, &efi_info, search_disk); } /* * If reading the label failed, and this is a SCSI * disk, we can attempt to auto-sense the disk * Configuration. */ ctlr = search_ctlr->ctlr_ctype; if ((status == -1) && (ctlr->ctype_ctype == DKC_SCSI_CCS || ctlr->ctype_ctype == DKC_BLKDEV)) { if (option_msg && diag_msg) { err_print("%s: attempting auto configuration\n", search_disk->disk_name); } switch (search_disk->label_type) { case (L_TYPE_SOLARIS): if (auto_sense(search_file, 0, &search_label) != NULL) { /* * Auto config worked, so we now have * a valid label for the disk. Mark * the disk as needing the label flushed. */ status = 0; search_disk->disk_flags |= (DSK_LABEL_DIRTY | DSK_AUTO_CONFIG); break; } /* With SOLARIS label type failed, try EFI. */ /* FALLTHROUGH */ case (L_TYPE_EFI): efi_disk = auto_efi_sense(search_file, &efi_info); if (efi_disk != NULL) { /* * Auto config worked, so we now have * a valid label for the disk. */ search_disk->label_type = L_TYPE_EFI; status = 0; search_disk->disk_flags |= (DSK_LABEL_DIRTY | DSK_AUTO_CONFIG); } break; default: /* Should never happen */ break; } } /* * If we didn't successfully read the label, or the label * appears corrupt, just leave the disk as an unknown type. */ if (status == -1) { (void) close(search_file); return; } if (search_disk->label_type == L_TYPE_SOLARIS) { if (!checklabel(&search_label)) { (void) close(search_file); return; } if (trim_id(search_label.dkl_asciilabel)) { (void) close(search_file); return; } } /* * The label looks ok. Mark the disk as labeled. */ search_disk->disk_flags |= DSK_LABEL; if (search_disk->label_type == L_TYPE_EFI) { search_dtype = (struct disk_type *) zalloc(sizeof (struct disk_type)); type = search_ctlr->ctlr_ctype->ctype_dlist; if (type == NULL) { search_ctlr->ctlr_ctype->ctype_dlist = search_dtype; } else { while (type->dtype_next != NULL) { type = type->dtype_next; } type->dtype_next = search_dtype; } search_dtype->dtype_next = NULL; search_dtype->vendor = strdup(efi_info.vendor); search_dtype->product = strdup(efi_info.product); search_dtype->revision = strdup(efi_info.revision); if (search_dtype->vendor == NULL || search_dtype->product == NULL || search_dtype->revision == NULL) { free(search_dtype->vendor); free(search_dtype->product); free(search_dtype->revision); free(search_dtype); goto out; } search_dtype->capacity = efi_info.capacity; search_disk->disk_type = search_dtype; search_parts = (struct partition_info *) zalloc(sizeof (struct partition_info)); search_dtype->dtype_plist = search_parts; search_parts->pinfo_name = alloc_string("original"); search_parts->pinfo_next = NULL; search_parts->etoc = efi_info.e_parts; search_disk->disk_parts = search_parts; /* * Copy the volume name, if present */ for (i = 0; i < search_parts->etoc->efi_nparts; i++) { if (search_parts->etoc->efi_parts[i].p_tag == V_RESERVED) { bcopy(search_parts->etoc->efi_parts[i].p_name, search_disk->v_volume, LEN_DKL_VVOL); break; } } out: (void) close(search_file); free(efi_info.vendor); free(efi_info.product); free(efi_info.revision); return; } /* * Attempt to match the disk type in the label with a * known disk type. */ for (search_dtype = search_ctlr->ctlr_ctype->ctype_dlist; search_dtype != NULL; search_dtype = search_dtype->dtype_next) if (dtype_match(&search_label, search_dtype)) break; /* * If no match was found, we need to create a disk type * for this disk. */ if (search_dtype == NULL) { /* * Allocate space for the disk type and add it * to the list of disk types for this ctlr type. */ search_dtype = (struct disk_type *) zalloc(sizeof (struct disk_type)); type = search_ctlr->ctlr_ctype->ctype_dlist; if (type == NULL) search_ctlr->ctlr_ctype->ctype_dlist = search_dtype; else { while (type->dtype_next != NULL) type = type->dtype_next; type->dtype_next = search_dtype; } /* * Fill in the drive info from the disk label. */ search_dtype->dtype_next = NULL; if (strncmp(search_label.dkl_asciilabel, "DEFAULT", strlen("DEFAULT")) == 0) { (void) get_disk_name(search_file, disk_name, search_disk); search_dtype->dtype_asciilabel = (char *) zalloc(strlen(disk_name) + 1); (void) strcpy(search_dtype->dtype_asciilabel, disk_name); } else { search_dtype->dtype_asciilabel = (char *) zalloc(strlen(search_label.dkl_asciilabel) + 1); (void) strcpy(search_dtype->dtype_asciilabel, search_label.dkl_asciilabel); } search_dtype->dtype_pcyl = search_label.dkl_pcyl; search_dtype->dtype_ncyl = search_label.dkl_ncyl; search_dtype->dtype_acyl = search_label.dkl_acyl; search_dtype->dtype_nhead = search_label.dkl_nhead; search_dtype->dtype_nsect = search_label.dkl_nsect; search_dtype->dtype_rpm = search_label.dkl_rpm; /* * Mark the disk as needing specification of * ctlr specific attributes. This is necessary * because the label doesn't contain these attributes, * and they aren't known at this point. They will * be asked for if this disk is ever selected by * the user. * Note: for SCSI, we believe the label. */ if ((search_ctlr->ctlr_ctype->ctype_ctype != DKC_SCSI_CCS) && (search_ctlr->ctlr_ctype->ctype_ctype != DKC_DIRECT) && (search_ctlr->ctlr_ctype->ctype_ctype != DKC_VBD) && (search_ctlr->ctlr_ctype->ctype_ctype != DKC_PCMCIA_ATA) && (search_ctlr->ctlr_ctype->ctype_ctype != DKC_BLKDEV)) { search_dtype->dtype_flags |= DT_NEED_SPEFS; } } /* * By this time we have a known disk type. Link the disk * to the disk type. */ search_disk->disk_type = search_dtype; /* * Close the file for this disk */ (void) close(search_file); /* * Attempt to match the partition map in the label with * a known partition map for this disk type. */ for (search_parts = search_dtype->dtype_plist; search_parts != NULL; search_parts = search_parts->pinfo_next) if (parts_match(&search_label, search_parts)) { break; } /* * If no match was made, we need to create a partition * map for this disk. */ if (search_parts == NULL) { /* * Allocate space for the partition map and add * it to the list of maps for this disk type. */ search_parts = (struct partition_info *) zalloc(sizeof (struct partition_info)); parts = search_dtype->dtype_plist; if (parts == NULL) search_dtype->dtype_plist = search_parts; else { while (parts->pinfo_next != NULL) parts = parts->pinfo_next; parts->pinfo_next = search_parts; } search_parts->pinfo_next = NULL; /* * Fill in the name of the map with a name derived * from the name of this disk. This is necessary * because the label contains no name for the * partition map. */ search_parts->pinfo_name = alloc_string("original"); /* * Fill in the partition info from the disk label. */ for (i = 0; i < NDKMAP; i++) { #if defined(_SUNOS_VTOC_8) search_parts->pinfo_map[i] = search_label.dkl_map[i]; #elif defined(_SUNOS_VTOC_16) search_parts->pinfo_map[i].dkl_cylno = search_label.dkl_vtoc.v_part[i].p_start / ((blkaddr32_t)(search_label.dkl_nhead * search_label.dkl_nsect)); search_parts->pinfo_map[i].dkl_nblk = search_label.dkl_vtoc.v_part[i].p_size; #else #error No VTOC format defined. #endif } } /* * If the vtoc looks valid, copy the volume name and vtoc * info from the label. Otherwise, install a default vtoc. * This permits vtoc info to automatically appear in the sun * label, without requiring an upgrade procedure. */ if (search_label.dkl_vtoc.v_version == V_VERSION) { bcopy(search_label.dkl_vtoc.v_volume, search_disk->v_volume, LEN_DKL_VVOL); search_parts->vtoc = search_label.dkl_vtoc; } else { bzero(search_disk->v_volume, LEN_DKL_VVOL); set_vtoc_defaults(search_parts); } /* * By this time we have a known partitition map. Link the * disk to the partition map. */ search_disk->disk_parts = search_parts; } /* * Search the disk list for a disk with the identical configuration. * Return true if one is found. */ static int disk_is_known(struct dk_cinfo *dkinfo) { struct disk_info *dp; dp = disk_list; while (dp != NULL) { if (dp->disk_dkinfo.dki_ctype == dkinfo->dki_ctype && dp->disk_dkinfo.dki_cnum == dkinfo->dki_cnum && dp->disk_dkinfo.dki_unit == dkinfo->dki_unit && strcmp(dp->disk_dkinfo.dki_dname, dkinfo->dki_dname) == 0) { return (1); } dp = dp->disk_next; } return (0); } /* * This routine checks to see if a given disk type matches the type * in the disk label. */ int dtype_match(struct dk_label *label, struct disk_type *dtype) { if (dtype->dtype_asciilabel == NULL) { return (0); } /* * If the any of the physical characteristics are different, or * the name is different, it doesn't match. */ if ((strcmp(label->dkl_asciilabel, dtype->dtype_asciilabel) != 0) || (label->dkl_ncyl != dtype->dtype_ncyl) || (label->dkl_acyl != dtype->dtype_acyl) || (label->dkl_nhead != dtype->dtype_nhead) || (label->dkl_nsect != dtype->dtype_nsect)) { return (0); } /* * If those are all identical, assume it's a match. */ return (1); } /* * This routine checks to see if a given partition map matches the map * in the disk label. */ int parts_match(struct dk_label *label, struct partition_info *pinfo) { int i; /* * If any of the partition entries is different, it doesn't match. */ for (i = 0; i < NDKMAP; i++) #if defined(_SUNOS_VTOC_8) if ((label->dkl_map[i].dkl_cylno != pinfo->pinfo_map[i].dkl_cylno) || (label->dkl_map[i].dkl_nblk != pinfo->pinfo_map[i].dkl_nblk)) #elif defined(_SUNOS_VTOC_16) if ((pinfo->pinfo_map[i].dkl_cylno != label->dkl_vtoc.v_part[i].p_start / (label->dkl_nhead * label->dkl_nsect)) || (pinfo->pinfo_map[i].dkl_nblk != label->dkl_vtoc.v_part[i].p_size)) #else #error No VTOC format defined. #endif return (0); /* * Compare the vtoc information for a match * Do not require the volume name to be equal, for a match! */ if (label->dkl_vtoc.v_version != pinfo->vtoc.v_version) return (0); if (label->dkl_vtoc.v_nparts != pinfo->vtoc.v_nparts) return (0); for (i = 0; i < NDKMAP; i++) { if (label->dkl_vtoc.v_part[i].p_tag != pinfo->vtoc.v_part[i].p_tag) return (0); if (label->dkl_vtoc.v_part[i].p_flag != pinfo->vtoc.v_part[i].p_flag) return (0); } /* * If they are all identical, it's a match. */ return (1); } /* * This routine checks to see if the given disk name refers to the disk * in the given disk structure. */ int diskname_match(char *name, struct disk_info *disk) { struct dk_cinfo dkinfo; char s[MAXPATHLEN]; int fd; /* * Match the name of the disk in the disk_info structure */ if (strcmp(name, disk->disk_name) == 0) { return (1); } /* * Check to see if it's a 4.x file name in the /dev * directory on 5.0. Here, we only accept the * canonicalized form: sd0. */ if (canonical4x_name(name) == 0) { return (0); } (void) strcpy(s, "/dev/r"); (void) strcat(s, name); (void) strcat(s, "c"); if ((fd = open_disk(s, O_RDWR | O_NDELAY)) < 0) { return (0); } if (ioctl(fd, DKIOCINFO, &dkinfo) < 0) { (void) close(fd); return (0); } (void) close(fd); if (disk->disk_dkinfo.dki_ctype == dkinfo.dki_ctype && disk->disk_dkinfo.dki_cnum == dkinfo.dki_cnum && disk->disk_dkinfo.dki_unit == dkinfo.dki_unit && strcmp(disk->disk_dkinfo.dki_dname, dkinfo.dki_dname) == 0) { return (1); } return (0); } static void datafile_error(char *errmsg, char *token) { int token_type; TOKEN token_buf; /* * Allow us to get by controllers that the other platforms don't * know about. */ if (errmsg != NULL) { err_print(errmsg, token); err_print(" - %s (%d)\n", file_name, data_lineno); } /* * Re-sync the parsing at the beginning of the next line * unless of course we're already there. */ if (last_token_type != SUP_EOF && last_token_type != SUP_EOL) { do { token_type = sup_gettoken(token_buf); } while (token_type != SUP_EOF && token_type != SUP_EOL); if (token_type == SUP_EOF) { sup_pushtoken(token_buf, token_type); } } } /* * Search through all defined disk types for duplicate entries * that are inconsistent with each other. Disks with different * characteristics should be named differently. * Note that this function only checks for duplicate disks * for the same controller. It's possible to have two disks with * the same name, but defined for different controllers. * That may or may not be a problem... */ static void search_duplicate_dtypes(void) { struct disk_type *dp1; struct disk_type *dp2; struct mctlr_list *mlp; mlp = controlp; while (mlp != NULL) { dp1 = mlp->ctlr_type->ctype_dlist; while (dp1 != NULL) { dp2 = dp1->dtype_next; while (dp2 != NULL) { check_dtypes_for_inconsistency(dp1, dp2); dp2 = dp2->dtype_next; } dp1 = dp1->dtype_next; } mlp = mlp->next; } } /* * Search through all defined partition types for duplicate entries * that are inconsistent with each other. Partitions with different * characteristics should be named differently. * Note that this function only checks for duplicate partitions * for the same disk. It's possible to have two partitions with * the same name, but defined for different disks. * That may or may not be a problem... */ static void search_duplicate_pinfo(void) { struct disk_type *dp; struct partition_info *pp1; struct partition_info *pp2; struct mctlr_list *mlp; mlp = controlp; while (mlp != NULL) { dp = mlp->ctlr_type->ctype_dlist; while (dp != NULL) { pp1 = dp->dtype_plist; while (pp1 != NULL) { pp2 = pp1->pinfo_next; while (pp2 != NULL) { check_pinfo_for_inconsistency(pp1, pp2); pp2 = pp2->pinfo_next; } pp1 = pp1->pinfo_next; } dp = dp->dtype_next; } mlp = mlp->next; } } /* * Determine if two particular disk definitions are inconsistent. * Ie: same name, but different characteristics. * If so, print an error message and abort. */ static void check_dtypes_for_inconsistency(struct disk_type *dp1, struct disk_type *dp2) { int i; int result; struct chg_list *cp1; struct chg_list *cp2; /* * If the name's different, we're ok */ if (strcmp(dp1->dtype_asciilabel, dp2->dtype_asciilabel) != 0) { return; } /* * Compare all the disks' characteristics */ result = 0; result |= (dp1->dtype_flags != dp2->dtype_flags); result |= (dp1->dtype_options != dp2->dtype_options); result |= (dp1->dtype_fmt_time != dp2->dtype_fmt_time); result |= (dp1->dtype_bpt != dp2->dtype_bpt); result |= (dp1->dtype_ncyl != dp2->dtype_ncyl); result |= (dp1->dtype_acyl != dp2->dtype_acyl); result |= (dp1->dtype_pcyl != dp2->dtype_pcyl); result |= (dp1->dtype_nhead != dp2->dtype_nhead); result |= (dp1->dtype_nsect != dp2->dtype_nsect); result |= (dp1->dtype_rpm != dp2->dtype_rpm); result |= (dp1->dtype_cyl_skew != dp2->dtype_cyl_skew); result |= (dp1->dtype_trk_skew != dp2->dtype_trk_skew); result |= (dp1->dtype_trks_zone != dp2->dtype_trks_zone); result |= (dp1->dtype_atrks != dp2->dtype_atrks); result |= (dp1->dtype_asect != dp2->dtype_asect); result |= (dp1->dtype_cache != dp2->dtype_cache); result |= (dp1->dtype_threshold != dp2->dtype_threshold); result |= (dp1->dtype_read_retries != dp2->dtype_read_retries); result |= (dp1->dtype_write_retries != dp2->dtype_write_retries); result |= (dp1->dtype_prefetch_min != dp2->dtype_prefetch_min); result |= (dp1->dtype_prefetch_max != dp2->dtype_prefetch_max); for (i = 0; i < NSPECIFICS; i++) { result |= (dp1->dtype_specifics[i] != dp2->dtype_specifics[i]); } cp1 = dp1->dtype_chglist; cp2 = dp2->dtype_chglist; while (cp1 != NULL && cp2 != NULL) { if (cp1 == NULL || cp2 == NULL) { result = 1; break; } result |= (cp1->pageno != cp2->pageno); result |= (cp1->byteno != cp2->byteno); result |= (cp1->mode != cp2->mode); result |= (cp1->value != cp2->value); cp1 = cp1->next; cp2 = cp2->next; } if (result) { err_print("Inconsistent definitions for disk type '%s'\n", dp1->dtype_asciilabel); if (dp1->dtype_filename != NULL && dp2->dtype_filename != NULL) { err_print("%s (%d) - %s (%d)\n", dp1->dtype_filename, dp1->dtype_lineno, dp2->dtype_filename, dp2->dtype_lineno); } fullabort(); } } /* * Determine if two particular partition definitions are inconsistent. * Ie: same name, but different characteristics. * If so, print an error message and abort. */ static void check_pinfo_for_inconsistency(struct partition_info *pp1, struct partition_info *pp2) { int i; int result; struct dk_map32 *map1; struct dk_map32 *map2; #if defined(_SUNOS_VTOC_8) struct dk_map2 *vp1; struct dk_map2 *vp2; #elif defined(_SUNOS_VTOC_16) struct dkl_partition *vp1; struct dkl_partition *vp2; #else #error No VTOC layout defined. #endif /* defined(_SUNOS_VTOC_8) */ /* * If the name's different, we're ok */ if (strcmp(pp1->pinfo_name, pp2->pinfo_name) != 0) { return; } /* * Compare all the partitions' characteristics */ result = 0; map1 = pp1->pinfo_map; map2 = pp2->pinfo_map; for (i = 0; i < NDKMAP; i++, map1++, map2++) { result |= (map1->dkl_cylno != map2->dkl_cylno); result |= (map1->dkl_nblk != map2->dkl_nblk); } /* * Compare the significant portions of the vtoc information */ vp1 = pp1->vtoc.v_part; vp2 = pp2->vtoc.v_part; for (i = 0; i < NDKMAP; i++, vp1++, vp2++) { result |= (vp1->p_tag != vp2->p_tag); result |= (vp1->p_flag != vp2->p_flag); } if (result) { err_print("Inconsistent definitions for partition type '%s'\n", pp1->pinfo_name); if (pp1->pinfo_filename != NULL && pp2->pinfo_filename != NULL) { err_print("%s (%d) - %s (%d)\n", pp1->pinfo_filename, pp1->pinfo_lineno, pp2->pinfo_filename, pp2->pinfo_lineno); } fullabort(); } } /* * Convert a string of digits into a block number. * The digits are assumed to be a block number unless the * the string is terminated by 'c', in which case it is * assumed to be in units of cylinders. Accept a 'b' * to explictly specify blocks, for consistency. * * NB: uses the macro spc(), which requires that the * globals nhead/nsect/acyl be set up correctly. * * Returns -1 in the case of an error. */ static uint_t str2blks(char *str) { int blks; char *p; blks = (int)strtol(str, &p, 0); /* * Check what terminated the conversion. */ if (*p != 0) { /* * Units specifier of 'c': convert cylinders to blocks */ if (*p == 'c') { p++; blks = blks * spc(); /* * Ignore a 'b' specifier. */ } else if (*p == 'b') { p++; } /* * Anthing left over is an error */ if (*p != 0) { blks = -1; } } return (blks); } /* * Convert a string of digits into a cylinder number. * Accept a an optional 'c' specifier, for consistency. * * Returns -1 in the case of an error. */ int str2cyls(char *str) { int cyls; char *p; cyls = (int)strtol(str, &p, 0); /* * Check what terminated the conversion. */ if (*p != 0) { /* * Units specifier of 'c': accept it. */ if (*p == 'c') { p++; } /* * Anthing left over is an error */ if (*p != 0) { cyls = -1; } } return (cyls); } /* * Create a new chg_list structure, and append it onto the * end of the current chg_list under construction. By * applying changes in the order in which listed in the * data file, the changes we make are deterministic. * Return a pointer to the new structure, so that the * caller can fill in the appropriate information. */ static struct chg_list * new_chg_list(struct disk_type *disk) { struct chg_list *cp; struct chg_list *nc; nc = zalloc(sizeof (struct chg_list)); if (disk->dtype_chglist == NULL) { disk->dtype_chglist = nc; } else { for (cp = disk->dtype_chglist; cp->next; cp = cp->next) ; cp->next = nc; } nc->next = NULL; return (nc); } /* * 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) { err_print("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) { err_print("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) { err_print("%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) { err_print("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) { err_print("%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) { err_print("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) { err_print("cannot chdir() to %s - %s\n", savedir, strerror(errno)); } return (result); } static void sort_disk_list(void) { int n; struct disk_info **disks; struct disk_info *d; struct disk_info **dp; struct disk_info **dp2; /* * Count the number of disks in the list */ n = 0; for (d = disk_list; d != NULL; d = d->disk_next) { n++; } if (n == 0) { return; } /* * Allocate a simple disk list array and fill it in */ disks = (struct disk_info **) zalloc((n+1) * sizeof (struct disk_info *)); dp = disks; for (d = disk_list; d != NULL; d = d->disk_next) { *dp++ = d; } *dp = NULL; /* * Sort the disk list array */ qsort((void *) disks, n, sizeof (struct disk_info *), disk_name_compare); /* * Rebuild the linked list disk list structure */ dp = disks; disk_list = *dp; dp2 = dp + 1; do { (*dp++)->disk_next = *dp2++; } while (*dp != NULL); /* * Clean up */ (void) destroy_data((void *)disks); } /* * Compare two disk names */ static int disk_name_compare( const void *arg1, const void *arg2) { char *s1; char *s2; int n1; int n2; char *p1; char *p2; s1 = (*((struct disk_info **)arg1))->disk_name; s2 = (*((struct disk_info **)arg2))->disk_name; for (;;) { if (*s1 == 0 || *s2 == 0) break; if (isdigit(*s1) && isdigit(*s2)) { n1 = strtol(s1, &p1, 10); n2 = strtol(s2, &p2, 10); if (n1 != n2) { return (n1 - n2); } s1 = p1; s2 = p2; } else if (*s1 != *s2) { break; } else { s1++; s2++; } } return (*s1 - *s2); } static void make_controller_list(void) { int x; struct mctlr_list *ctlrp; ctlrp = controlp; for (x = nctypes; x != 0; x--) { ctlrp = zalloc(sizeof (struct mctlr_list)); ctlrp->next = controlp; ctlrp->ctlr_type = &ctlr_types[x - 1]; controlp = ctlrp; } } static void check_for_duplicate_disknames(char *arglist[]) { char *directory = "/dev/rdsk/"; char **disklist; int len; char s[MAXPATHLEN], t[MAXPATHLEN]; int diskno = 0; int i; len = strlen(directory); disklist = arglist; for (; *disklist != NULL; disklist++) { if (strncmp(directory, *disklist, len) == 0) { /* Disk is in conventional format */ canonicalize_name(s, *disklist); /* * check if the disk is already present in * disk list. */ for (i = 0; i < diskno; i++) { canonicalize_name(t, arglist[i]); if (strncmp(s, t, strlen(t)) == 0) break; } if (i != diskno) continue; } (void) strcpy(arglist[diskno], *disklist); diskno++; } arglist[diskno] = NULL; } #define DISK_PREFIX "/dev/rdsk/" /* * This Function checks if the non-conventional name is a a link to * one of the conventional whole disk name. */ static int name_represents_wholedisk(char *name) { char symname[MAXPATHLEN]; char localname[MAXPATHLEN]; char *nameptr; ssize_t symname_size; if (strlcpy(localname, name, MAXPATHLEN) >= MAXPATHLEN) return (1); /* buffer overflow, reject this name */ while ((symname_size = readlink( localname, symname, MAXPATHLEN - 1)) != -1) { symname[symname_size] = '\0'; nameptr = symname; if (strncmp(symname, DISK_PREFIX, (sizeof (DISK_PREFIX) - 1)) == 0) nameptr += (sizeof (DISK_PREFIX) - 1); if (conventional_name(nameptr)) { if (whole_disk_name(nameptr)) return (0); else return (1); } (void) strcpy(localname, symname); } return (0); } /* * 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 (c) 1991-2001 by Sun Microsystems, Inc. */ #ifndef _STARTUP_H #define _STARTUP_H #ifdef __cplusplus extern "C" { #endif /* * This file contains declarations pertaining to reading the data file. */ /* * The definitions are the token types that the data file parser recognizes. */ #define SUP_EOF -1 /* eof token */ #define SUP_STRING 0 /* string token */ #define SUP_EQL 1 /* equals token */ #define SUP_COMMA 2 /* comma token */ #define SUP_COLON 3 /* colon token */ #define SUP_EOL 4 /* newline token */ #define SUP_OR 5 /* vertical bar */ #define SUP_AND 6 /* ampersand */ #define SUP_TILDE 7 /* tilde */ /* * These definitions are flags for the legal keywords in the data file. * They are used to keep track of what parameters appear on the current * line in the file. */ #define SUP_CTLR 0x00000001 /* set ctlr */ #define SUP_DISK 0x00000002 /* set disk */ #define SUP_NCYL 0x00000004 /* set ncyl */ #define SUP_ACYL 0x00000008 /* set acyl */ #define SUP_PCYL 0x00000010 /* set pcyl */ #define SUP_NHEAD 0x00000020 /* set nhead */ #define SUP_NSECT 0x00000040 /* set nsect */ #define SUP_RPM 0x00000080 /* set rpm */ #define SUP_BPT 0x00000100 /* set bytes/track */ #define SUP_BPS 0x00000200 /* set bytes/sector */ #define SUP_DRTYPE 0x00000400 /* set drive type */ #define SUP_READ_RETRIES 0x00000800 /* set read retries */ #define SUP_WRITE_RETRIES 0x00001000 /* set write retries */ #define SUP_TRKS_ZONE 0x00002000 /* set tracks/zone */ #define SUP_ATRKS 0x00004000 /* set alt. tracks */ #define SUP_ASECT 0x00008000 /* set sectors/zone */ #define SUP_CACHE 0x00010000 /* set cache size */ #define SUP_PREFETCH 0x00020000 /* set prefetch threshold */ #define SUP_CACHE_MIN 0x00040000 /* set min. prefetch */ #define SUP_CACHE_MAX 0x00080000 /* set max. prefetch */ #define SUP_PSECT 0x00100000 /* set physical sectors */ #define SUP_PHEAD 0x00200000 /* set physical heads */ #define SUP_FMTTIME 0x00400000 /* set format time */ #define SUP_CYLSKEW 0x00800000 /* set cylinder skew */ #define SUP_TRKSKEW 0x01000000 /* set track skew */ /* * The define the minimum set of parameters necessary to declare a disk * and a partition map in the data file. Depending on the ctlr type, * more info than this may be necessary for declaring disks. */ #define SUP_MIN_DRIVE (SUP_CTLR | SUP_RPM | SUP_PCYL | \ SUP_NCYL | SUP_ACYL | SUP_NHEAD | SUP_NSECT) #define SUP_MIN_PART 0x0003 /* for maps */ /* * Prototypes for ANSI C compilers */ int do_options(int argc, char *argv[]); void sup_init(void); int open_disk(char *diskname, int flags); void do_search(char *arglist[]); int dtype_match(struct dk_label *label, struct disk_type *dtype); int parts_match(struct dk_label *label, struct partition_info *pinfo); int diskname_match(char *name, struct disk_info *disk); #ifdef __cplusplus } #endif #endif /* _STARTUP_H */