xref: /illumos-gate/usr/src/cmd/format/menu_analyze.c (revision 2b24ab6b3865caeede9eeb9db6b83e1d89dcd1ea)
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
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
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
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
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
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
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
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
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
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
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