1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 1991-2002 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 /* 30 * This file contains functions implementing the analyze menu commands. 31 */ 32 #include <string.h> 33 #include "global.h" 34 #include "analyze.h" 35 #include "misc.h" 36 #include "menu_analyze.h" 37 #include "param.h" 38 39 40 41 /* 42 * This routine implements the 'read' command. It performs surface 43 * analysis by reading the disk. It is ok to run this command on 44 * mounted file systems. 45 */ 46 int 47 a_read() 48 { 49 /* 50 * The current disk must be formatted before disk analysis. 51 */ 52 if (!(cur_flags & DISK_FORMATTED)) { 53 err_print("Current Disk is unformatted.\n"); 54 return (-1); 55 } 56 57 if (check( 58 "Ready to analyze (won't harm SunOS). This takes a long time, \n" 59 "but is interruptable with CTRL-C. Continue")) 60 return (-1); 61 return (do_scan(SCAN_VALID, F_NORMAL)); 62 } 63 64 /* 65 * This routine implements the 'refresh' command. It performs surface 66 * analysis by reading the disk then writing the same data back to the 67 * disk. It is ok to run this command on file systems, but not while 68 * they are mounted. 69 */ 70 int 71 a_refresh() 72 { 73 /* 74 * The current disk must be formatted before disk analysis. 75 */ 76 if (!(cur_flags & DISK_FORMATTED)) { 77 err_print("Current Disk is unformatted.\n"); 78 return (-1); 79 } 80 81 if (check( 82 "Ready to analyze (won't harm data). This takes a long time, \n" 83 "but is interruptable with CTRL-C. Continue")) 84 return (-1); 85 return (do_scan(SCAN_VALID | SCAN_WRITE, F_NORMAL)); 86 } 87 88 /* 89 * This routine implements the 'test' command. It performs surface 90 * analysis by reading the disk, writing then reading a pattern on the disk, 91 * then writing the original data back to the disk. 92 * It is ok to run this command on file systems, but not while they are 93 * mounted. 94 */ 95 int 96 a_test() 97 { 98 /* 99 * The current disk must be formatted before disk analysis. 100 */ 101 if (!(cur_flags & DISK_FORMATTED)) { 102 err_print("Current Disk is unformatted.\n"); 103 return (-1); 104 } 105 106 if (check( 107 "Ready to analyze (won't harm data). This takes a long time, \n" 108 "but is interruptable with CTRL-C. Continue")) 109 return (-1); 110 return (do_scan(SCAN_VALID | SCAN_PATTERN | SCAN_WRITE, F_NORMAL)); 111 } 112 113 /* 114 * This routine implements the 'write' command. It performs surface 115 * analysis by writing a pattern to the disk then reading it back. 116 * It is not ok to run this command on any data you want to keep. 117 */ 118 int 119 a_write() 120 { 121 /* 122 * The current disk must be formatted before disk analysis. 123 */ 124 if (!(cur_flags & DISK_FORMATTED)) { 125 err_print("Current Disk is unformatted.\n"); 126 return (-1); 127 } 128 129 if (check( 130 "Ready to analyze (will corrupt data). This takes a long time, \n" 131 "but is interruptable with CTRL-C. Continue")) 132 return (-1); 133 return (do_scan(SCAN_PATTERN, F_NORMAL)); 134 } 135 136 /* 137 * This routine implements the 'compare' command. It performs surface 138 * analysis by writing a pattern to the disk, reading it back, then 139 * checking the data to be sure it's the same. 140 * It is not ok to run this command on any data you want to keep. 141 */ 142 int 143 a_compare() 144 { 145 /* 146 * The current disk must be formatted before disk analysis. 147 */ 148 if (!(cur_flags & DISK_FORMATTED)) { 149 err_print("Current Disk is unformatted.\n"); 150 return (-1); 151 } 152 153 if (check( 154 "Ready to analyze (will corrupt data). This takes a long time, \n" 155 "but is interruptable with CTRL-C. Continue")) 156 return (-1); 157 return (do_scan(SCAN_PATTERN | SCAN_COMPARE, F_NORMAL)); 158 } 159 160 /* 161 * This routine implements the 'print' command. It displays the data 162 * buffer in hexadecimal. It is only useful for checking the disk for 163 * a specific set of data (by reading it then printing it). 164 */ 165 int 166 a_print() 167 { 168 int i, j, lines, nomore = 0; 169 int c, one_line = 0; 170 int tty_lines = get_tty_lines(); 171 172 /* 173 * If we are running out of command file, don't page the output. 174 * Otherwise we are running with a user. Turn off echoing of 175 * input characters so we can page the output. 176 */ 177 if (option_f || (!isatty(0)) || (!isatty(1))) 178 nomore++; 179 else { 180 enter_critical(); 181 echo_off(); 182 charmode_on(); 183 exit_critical(); 184 } 185 /* 186 * Loop through the data buffer. 187 */ 188 lines = 0; 189 for (i = 0; i < scan_size * SECSIZE / sizeof (int); i += 6) { 190 /* 191 * Print the data. 192 */ 193 for (j = 0; j < 6; j++) 194 if (i + j < scan_size * SECSIZE / sizeof (int)) 195 fmt_print("0x%08x ", 196 *((int *)((int *)cur_buf + i + j))); 197 fmt_print("\n"); 198 lines++; 199 200 /* 201 * If we are paging and hit the end of a page, wait for 202 * the user to hit either space-bar, "q", return, 203 * or ctrl-C before going on. 204 */ 205 if (one_line || 206 (!nomore && (lines % (tty_lines - 1) == 0))) { 207 /* 208 * Print until first screenfull 209 */ 210 if (lines < (tty_lines -1)) 211 continue; 212 /* 213 * Get the next character. 214 */ 215 (void) printf("- hit space for more - "); 216 c = getchar(); 217 (void) printf("\015"); 218 one_line = 0; 219 /* 220 * Handle display one line command (return key) 221 */ 222 if (c == '\012') { 223 one_line++; 224 } 225 /* Handle Quit command */ 226 if (c == 'q') { 227 (void) printf( 228 " \015"); 229 goto PRINT_EXIT; 230 } 231 /* handle ^D */ 232 if (c == '\004') 233 fullabort(); 234 } 235 } 236 /* 237 * If we were doing paging, turn echoing back on. 238 */ 239 PRINT_EXIT: 240 if (!nomore) { 241 enter_critical(); 242 charmode_off(); 243 echo_on(); 244 exit_critical(); 245 } 246 return (0); 247 } 248 249 /* 250 * This routine implements the 'setup' command. It allows the user 251 * to program the variables that drive surface analysis. The approach 252 * is to prompt the user for the value of each variable, with the current 253 * value as the default. 254 */ 255 int 256 a_setup() 257 { 258 int deflt; 259 uint64_t size; 260 u_ioparam_t ioparam; 261 262 /* 263 * Because of the polarity of the yes/no structure (yes is 0), 264 * we have to invert the values for all yes/no questions. 265 */ 266 deflt = !scan_entire; 267 ioparam.io_charlist = confirm_list; 268 scan_entire = !input(FIO_MSTR, "Analyze entire disk", '?', 269 &ioparam, &deflt, DATA_INPUT); 270 /* 271 * If we are not scanning the whole disk, input the bounds of the scan. 272 */ 273 if (!scan_entire) { 274 ioparam.io_bounds.lower = 0; 275 if ((cur_ctype->ctype_flags & CF_SCSI) && 276 (cur_disk->label_type == L_TYPE_SOLARIS)) { 277 ioparam.io_bounds.upper = datasects() - 1; 278 } else if (cur_disk->label_type == L_TYPE_SOLARIS) { 279 ioparam.io_bounds.upper = physsects() - 1; 280 } else if (cur_disk->label_type == L_TYPE_EFI) { 281 ioparam.io_bounds.upper = cur_parts->etoc->efi_last_lba; 282 } 283 284 scan_lower = (diskaddr_t)input(FIO_BN, 285 "Enter starting block number", ':', 286 &ioparam, (int *)&scan_lower, DATA_INPUT); 287 ioparam.io_bounds.lower = scan_lower; 288 if (scan_upper < scan_lower) 289 scan_upper = scan_lower; 290 scan_upper = (diskaddr_t)input(FIO_BN, 291 "Enter ending block number", ':', 292 &ioparam, (int *)&scan_upper, DATA_INPUT); 293 } 294 deflt = !scan_loop; 295 ioparam.io_charlist = confirm_list; 296 scan_loop = !input(FIO_MSTR, "Loop continuously", '?', 297 &ioparam, &deflt, DATA_INPUT); 298 /* 299 * If we are not looping continuously, input the number of passes. 300 */ 301 if (!scan_loop) { 302 ioparam.io_bounds.lower = 1; 303 ioparam.io_bounds.upper = 100; 304 scan_passes = input(FIO_INT, "Enter number of passes", ':', 305 &ioparam, &scan_passes, DATA_INPUT); 306 } 307 deflt = !scan_correct; 308 ioparam.io_charlist = confirm_list; 309 scan_correct = !input(FIO_MSTR, "Repair defective blocks", '?', 310 &ioparam, &deflt, DATA_INPUT); 311 deflt = !scan_stop; 312 ioparam.io_charlist = confirm_list; 313 scan_stop = !input(FIO_MSTR, "Stop after first error", '?', 314 &ioparam, &deflt, DATA_INPUT); 315 deflt = !scan_random; 316 ioparam.io_charlist = confirm_list; 317 scan_random = !input(FIO_MSTR, "Use random bit patterns", '?', 318 &ioparam, &deflt, DATA_INPUT); 319 ioparam.io_bounds.lower = 1; 320 /* 321 * The number of blocks per transfer is limited by the buffer 322 * size, or the scan boundaries, whichever is smaller. 323 */ 324 if ((scan_entire) && (cur_disk->label_type == L_TYPE_SOLARIS)) { 325 size = physsects() - 1; 326 } else if ((scan_entire) && (cur_disk->label_type == L_TYPE_EFI)) { 327 size = cur_parts->etoc->efi_last_lba; 328 } else { 329 size = scan_upper - scan_lower + 1; 330 } 331 ioparam.io_bounds.upper = min(size, BUF_SECTS); 332 if (scan_size > ioparam.io_bounds.upper) 333 scan_size = ioparam.io_bounds.upper; 334 scan_size = input(FIO_BN, "Enter number of blocks per transfer", ':', 335 &ioparam, &scan_size, DATA_INPUT); 336 deflt = !scan_auto; 337 ioparam.io_charlist = confirm_list; 338 scan_auto = !input(FIO_MSTR, "Verify media after formatting", '?', 339 &ioparam, &deflt, DATA_INPUT); 340 341 deflt = !option_msg; 342 ioparam.io_charlist = confirm_list; 343 option_msg = !input(FIO_MSTR, "Enable extended messages", '?', 344 &ioparam, &deflt, DATA_INPUT); 345 deflt = !scan_restore_defects; 346 ioparam.io_charlist = confirm_list; 347 scan_restore_defects = !input(FIO_MSTR, "Restore defect list", '?', 348 &ioparam, &deflt, DATA_INPUT); 349 deflt = !scan_restore_label; 350 ioparam.io_charlist = confirm_list; 351 scan_restore_label = !input(FIO_MSTR, "Restore disk label", '?', 352 &ioparam, &deflt, DATA_INPUT); 353 fmt_print("\n"); 354 return (0); 355 } 356 357 /* 358 * This routine implements the 'config' command. It simply prints out 359 * the values of all the variables controlling surface analysis. It 360 * is meant to complement the 'setup' command by allowing the user to 361 * check the current setup. 362 */ 363 int 364 a_config() 365 { 366 367 fmt_print(" Analyze entire disk? "); 368 fmt_print(scan_entire ? "yes\n" : "no\n"); 369 370 if (!scan_entire) { 371 fmt_print(" Starting block number: %llu (", scan_lower); 372 pr_dblock(fmt_print, scan_lower); 373 fmt_print(")\n Ending block number: %llu (", scan_upper); 374 pr_dblock(fmt_print, scan_upper); 375 fmt_print(")\n"); 376 } 377 fmt_print(" Loop continuously? "); 378 fmt_print(scan_loop ? "yes\n" : "no\n"); 379 380 if (!scan_loop) { 381 fmt_print(" Number of passes: %d\n", scan_passes); 382 } 383 384 fmt_print(" Repair defective blocks? "); 385 fmt_print(scan_correct ? "yes\n" : "no\n"); 386 387 fmt_print(" Stop after first error? "); 388 fmt_print(scan_stop ? "yes\n" : "no\n"); 389 390 fmt_print(" Use random bit patterns? "); 391 fmt_print(scan_random ? "yes\n" : "no\n"); 392 393 fmt_print(" Number of blocks per transfer: %d (", scan_size); 394 pr_dblock(fmt_print, (daddr_t)scan_size); 395 fmt_print(")\n"); 396 397 fmt_print(" Verify media after formatting? "); 398 fmt_print(scan_auto ? "yes\n" : "no\n"); 399 400 fmt_print(" Enable extended messages? "); 401 fmt_print(option_msg ? "yes\n" : "no\n"); 402 403 fmt_print(" Restore defect list? "); 404 fmt_print(scan_restore_defects ? "yes\n" : "no\n"); 405 406 fmt_print(" Restore disk label? "); 407 fmt_print(scan_restore_label ? "yes\n" : "no\n"); 408 409 fmt_print("\n"); 410 return (0); 411 } 412 413 /* 414 * This routine implements the 'purge' command. It purges the disk 415 * by writing three patterns to the disk then reading the last one back. 416 * It is not ok to run this command on any data you want to keep. 417 */ 418 int 419 a_purge() 420 { 421 int status = 0; 422 423 /* 424 * The current disk must be formatted before disk analysis. 425 */ 426 if (!(cur_flags & DISK_FORMATTED)) { 427 err_print("Current Disk is unformatted.\n"); 428 return (-1); 429 } 430 if (scan_random) { 431 fmt_print("The purge command does not write random data\n"); 432 scan_random = 0; 433 } 434 435 if (!scan_loop && (scan_passes <= NPPATTERNS)) { 436 if (scan_passes < NPPATTERNS) { 437 fmt_print("The purge command runs for a minimum of "); 438 fmt_print("%d passes plus a last pass if the\n", 439 NPPATTERNS); 440 fmt_print("first %d passes were successful.\n", 441 NPPATTERNS); 442 } 443 scan_passes = NPPATTERNS + 1; 444 } 445 446 if (check( 447 "Ready to purge (will corrupt data). This takes a long time, \n" 448 "but is interruptable with CTRL-C. Continue")) 449 return (-1); 450 451 status = do_scan(SCAN_PATTERN | SCAN_PURGE, F_NORMAL); 452 453 return (status); 454 } 455 456 /* 457 * This routine implements the 'verify' command. It writes the disk 458 * by writing unique data for each block; after the write pass, it 459 * reads the data and verifies for correctness. Note that the entire 460 * disk (or the range of disk) is fully written first and then read. 461 * This should eliminate any caching effect on the drives. 462 * It is not ok to run this command on any data you want to keep. 463 */ 464 int 465 a_verify() 466 { 467 /* 468 * The current disk must be formatted before disk analysis. 469 */ 470 if (!(cur_flags & DISK_FORMATTED)) { 471 err_print("Current Disk is unformatted.\n"); 472 return (-1); 473 } 474 if (scan_random) { 475 fmt_print("The verify command does not write random data\n"); 476 scan_random = 0; 477 } 478 if (scan_passes < 2 && !scan_loop) { 479 scan_passes = 2; 480 fmt_print("The verify command runs minimum of 2 passes, one" 481 " for writing and \nanother for reading and verfying." 482 " Resetting the number of passes to 2.\n"); 483 } 484 485 if (check("Ready to verify (will corrupt data). This takes a long time," 486 "\nbut is interruptable with CTRL-C. Continue")) { 487 return (-1); 488 } 489 490 return (do_scan(SCAN_WRITE | SCAN_VERIFY, F_NORMAL)); 491 } 492