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