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
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
a_read()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
a_refresh()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
a_test()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
a_write()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
a_compare()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
a_print()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 * cur_blksz / sizeof (int); i += 6) {
187 /*
188 * Print the data.
189 */
190 for (j = 0; j < 6; j++)
191 if (i + j < scan_size * cur_blksz / 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
a_setup()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
a_config()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
a_purge()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
a_verify()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