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