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
a_read(void)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
a_refresh(void)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
a_test(void)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
a_write(void)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
a_compare(void)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
a_print(void)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
a_setup(void)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
a_config(void)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
a_purge(void)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
a_verify(void)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