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