menu_analyze.c revision 7563:84ec90ffc3f7
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 */
43int
44a_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 interruptable 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 */
67int
68a_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 interruptable 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 */
92int
93a_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 interruptable 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 */
115int
116a_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 interruptable 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 */
139int
140a_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 interruptable 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 */
162int
163a_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	 */
236PRINT_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 */
252int
253a_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 */
360int
361a_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 */
415int
416a_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 interruptable 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 */
461int
462a_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 interruptable with CTRL-C. Continue")) {
484		return (-1);
485	}
486
487	return (do_scan(SCAN_WRITE | SCAN_VERIFY, F_NORMAL));
488}
489