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