xref: /illumos-gate/usr/src/cmd/format/menu_defect.c (revision 3db86aab)
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 2005 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 to implement the defect menu commands.
31  */
32 #include "global.h"
33 #include <unistd.h>
34 #include <string.h>
35 #include "misc.h"
36 #include "menu_defect.h"
37 #include "param.h"
38 #include "ctlr_scsi.h"
39 
40 /*
41  * This is the working defect list.  All the commands here operate on
42  * the working list, except for 'commit'.  This way the user can
43  * change his mind at any time without having mangled the current defect
44  * list.
45  */
46 struct	defect_list work_list;
47 
48 #ifdef __STDC__
49 
50 /* Function prototypes for ANSI C Compilers */
51 static int	commit_list(void);
52 
53 #else	/* __STDC__ */
54 
55 /* Function prototypes for non-ANSI C Compilers */
56 static int	commit_list();
57 
58 #endif	/* __STDC__ */
59 
60 /*
61  * This routine implements the 'restore' command.  It sets the working
62  * list equal to the current list.
63  */
64 int
65 d_restore()
66 {
67 	int	i;
68 
69 	assert(!EMBEDDED_SCSI);
70 
71 	/*
72 	 * If the working list has not been modified, there's nothing to do.
73 	 */
74 	if (!(work_list.flags & LIST_DIRTY)) {
75 		err_print("working list was not modified.\n");
76 		return (0);
77 	}
78 	/*
79 	 * Make sure the user is serious.
80 	 */
81 	if (check("Ready to update working list, continue"))
82 		return (-1);
83 	/*
84 	 * Lock out interrupts so the lists can't get mangled.
85 	 */
86 	enter_critical();
87 	/*
88 	 * Kill off the old working list.
89 	 */
90 	kill_deflist(&work_list);
91 	/*
92 	 * If the current isn't null, set the working list to be a
93 	 * copy of it.
94 	 */
95 	if (cur_list.list != NULL) {
96 		work_list.header = cur_list.header;
97 		work_list.list = (struct defect_entry *)zalloc(
98 		    LISTSIZE(work_list.header.count) * SECSIZE);
99 		for (i = 0; i < work_list.header.count; i++)
100 			*(work_list.list + i) = *(cur_list.list + i);
101 	}
102 	/*
103 	 * Initialize the flags since they are now in sync.
104 	 */
105 	work_list.flags = 0;
106 	if (work_list.list == NULL)
107 		fmt_print("working list set to null.\n\n");
108 	else
109 		fmt_print("working list updated, total of %d defects.\n\n",
110 		    work_list.header.count);
111 	exit_critical();
112 	return (0);
113 }
114 
115 /*
116  * This routine implements the 'original' command.  It extracts the
117  * manufacturer's defect list from the current disk.
118  */
119 int
120 d_original()
121 {
122 	int	status;
123 
124 
125 	/*
126 	 * If the controller does not support the extraction, we're out
127 	 * of luck.
128 	 */
129 	if (cur_ops->op_ex_man == NULL) {
130 		err_print("Controller does not support extracting ");
131 		err_print("manufacturer's defect list.\n");
132 		return (-1);
133 	}
134 	/*
135 	 * Make sure the user is serious.  Note, for SCSI disks
136 	 * since this is instantaneous, we will just do it and
137 	 * not ask for confirmation.
138 	 */
139 	if (!(cur_ctype->ctype_flags & CF_CONFIRM) &&
140 	    check(
141 "Ready to update working list. This cannot be interrupted\n\
142 and may take a long while. Continue"))
143 		return (-1);
144 	/*
145 	 * Lock out interrupts so we don't get half the list.
146 	 */
147 	enter_critical();
148 	/*
149 	 * Kill off the working list.
150 	 */
151 	kill_deflist(&work_list);
152 	fmt_print("Extracting manufacturer's defect list...");
153 	/*
154 	 * Do the extraction.
155 	 */
156 	status = (*cur_ops->op_ex_man)(&work_list);
157 	if (status)
158 		fmt_print("Extraction failed.\n\n");
159 	else {
160 		fmt_print("Extraction complete.\n");
161 		fmt_print("Working list updated, total of %d defects.\n\n",
162 		    work_list.header.count);
163 	}
164 	/*
165 	 * Mark the working list dirty since we modified it.
166 	 */
167 	work_list.flags |= LIST_DIRTY;
168 	exit_critical();
169 	/*
170 	 * Return status.
171 	 */
172 	return (status);
173 }
174 
175 /*
176  * This routine implements the 'extract' command.  It extracts the
177  * entire defect list from the current disk.
178  */
179 int
180 d_extract()
181 {
182 	int	status;
183 
184 	/*
185 	 * If the controller does not support the extraction, we are out
186 	 * of luck.
187 	 */
188 	if (cur_ops->op_ex_cur == NULL) {
189 		err_print("Controller does not support extracting ");
190 		err_print("current defect list.\n");
191 		return (-1);
192 	}
193 
194 	/*
195 	 * If disk is unformatted, you really shouldn't do this.
196 	 * However, ask user to be sure.
197 	 */
198 	if (! (cur_flags & DISK_FORMATTED) &&
199 	    (check(
200 "Cannot extract defect list from an unformatted disk. Continue")))
201 		return (-1);
202 
203 	/*
204 	 * If this takes a long time, let's ask the user if he
205 	 * doesn't mind waiting.  Note, for SCSI disks
206 	 * this operation is instantaneous so we won't ask for
207 	 * for confirmation.
208 	 */
209 	if (! (cur_ctype->ctype_flags & CF_CONFIRM) &&
210 	    check(
211 "Ready to extract working list. This cannot be interrupted\n\
212 and may take a long while. Continue"))
213 		return (-1);
214 	/*
215 	 * Lock out interrupts so we don't get half the list and
216 	 * Kill off the working list.
217 	 */
218 	enter_critical();
219 	kill_deflist(&work_list);
220 	fmt_print("Extracting defect list...");
221 
222 	/*
223 	 * Do the extraction.
224 	 */
225 	status = (*cur_ops->op_ex_cur)(&work_list);
226 	if (status) {
227 		if (!EMBEDDED_SCSI) {
228 			if (cur_flags & DISK_FORMATTED)
229 				read_list(&work_list);
230 
231 			if (work_list.list != NULL) {
232 				status = 0;
233 				fmt_print("Extraction complete.\n");
234 				fmt_print(
235 "Working list updated, total of %d defects.\n\n",
236 				work_list.header.count);
237 			} else {
238 				fmt_print("Extraction failed.\n\n");
239 			}
240 		} else {
241 			fmt_print("Extraction failed.\n\n");
242 		}
243 	} else {
244 		fmt_print("Extraction complete.\n");
245 		fmt_print("Working list updated, total of %d defects.\n\n",
246 		    work_list.header.count);
247 	}
248 	/*
249 	 * Mark the working list dirty since we modified it.
250 	 */
251 	work_list.flags |= LIST_DIRTY;
252 	exit_critical();
253 	/*
254 	 * Return status.
255 	 */
256 	return (status);
257 }
258 
259 /*
260  * This routine implements the 'add' command.  It allows the user to
261  * enter the working defect list manually.  It loops infinitely until
262  * the user breaks out with a ctrl-C.
263  */
264 int
265 d_add()
266 {
267 	int			type, bn, deflt, index;
268 	u_ioparam_t		ioparam;
269 	struct defect_entry	def;
270 
271 	assert(!EMBEDDED_SCSI);
272 
273 	/*
274 	 * Ask the user which mode of input he'd like to use.
275 	 */
276 	fmt_print("        0. bytes-from-index\n");
277 	fmt_print("        1. logical block\n");
278 	deflt = 0;
279 	ioparam.io_bounds.lower = 0;
280 	ioparam.io_bounds.upper = 1;
281 	type = input(FIO_INT, "Select input format (enter its number)", ':',
282 		&ioparam, &deflt, DATA_INPUT);
283 	fmt_print("\nEnter Control-C to terminate.\n");
284 loop:
285 	if (type) {
286 		/*
287 		 * Mode selected is logical block.  Input the defective block
288 		 * and fill in the defect entry with the info.
289 		 */
290 		def.bfi = def.nbits = UNKNOWN;
291 		ioparam.io_bounds.lower = 0;
292 		if (cur_disk->label_type == L_TYPE_SOLARIS) {
293 		    ioparam.io_bounds.upper = physsects() - 1;
294 		} else {
295 		    ioparam.io_bounds.upper = cur_parts->etoc->efi_last_lba;
296 		}
297 		bn = input(FIO_BN, "Enter defective block number", ':',
298 		    &ioparam, (int *)NULL, DATA_INPUT);
299 		def.cyl = bn2c(bn);
300 		def.head = bn2h(bn);
301 		def.sect = bn2s(bn);
302 	} else {
303 		/*
304 		 * Mode selected is bytes-from-index.  Input the information
305 		 * about the defect and fill in the defect entry.
306 		 */
307 		def.sect = UNKNOWN;
308 		ioparam.io_bounds.lower = 0;
309 		ioparam.io_bounds.upper = pcyl - 1;
310 		def.cyl = input(FIO_INT,
311 		    "Enter defect's cylinder number", ':',
312 		    &ioparam, (int *)NULL, DATA_INPUT);
313 		ioparam.io_bounds.upper = nhead - 1;
314 		def.head = input(FIO_INT, "Enter defect's head number",
315 		    ':', &ioparam, (int *)NULL, DATA_INPUT);
316 		ioparam.io_bounds.upper = cur_dtype->dtype_bpt - 1;
317 		def.bfi = input(FIO_INT, "Enter defect's bytes-from-index",
318 		    ':', &ioparam, (int *)NULL, DATA_INPUT);
319 		ioparam.io_bounds.lower = -1;
320 		ioparam.io_bounds.upper = (cur_dtype->dtype_bpt - def.bfi) * 8;
321 		if (ioparam.io_bounds.upper >= 32 * 1024)
322 			ioparam.io_bounds.upper = 32 * 1024 - 1;
323 		/*
324 		 * Note: a length of -1 means the length is not known.  We
325 		 * make this the default value.
326 		 */
327 		deflt = -1;
328 		def.nbits = input(FIO_INT, "Enter defect's length (in bits)",
329 		    ':', &ioparam, &deflt, DATA_INPUT);
330 	}
331 	/*
332 	 * Calculate where in the defect list this defect belongs
333 	 * and print it out.
334 	 */
335 	index = sort_defect(&def, &work_list);
336 	fmt_print(DEF_PRINTHEADER);
337 	pr_defect(&def, index);
338 
339 	/*
340 	 * Lock out interrupts so lists don't get mangled.
341 	 * Also, mark the working list dirty since we are modifying it.
342 	 */
343 	enter_critical();
344 	work_list.flags |= LIST_DIRTY;
345 	/*
346 	 * If the list is null, create it with zero length.  This is
347 	 * necessary because the routines to add a defect to the list
348 	 * assume the list is initialized.
349 	 */
350 	if (work_list.list == NULL) {
351 		work_list.header.magicno = (uint_t)DEFECT_MAGIC;
352 		work_list.header.count = 0;
353 		work_list.list = (struct defect_entry *)zalloc(
354 		    LISTSIZE(0) * SECSIZE);
355 	}
356 	/*
357 	 * Add the defect to the working list.
358 	 */
359 	add_def(&def, &work_list, index);
360 	fmt_print("defect number %d added.\n\n", index + 1);
361 	exit_critical();
362 	/*
363 	 * Loop back for the next defect.
364 	 */
365 	goto loop;
366 	/*NOTREACHED*/
367 #ifdef	lint
368 	return (0);
369 #endif
370 }
371 
372 /*
373  * This routine implements the 'delete' command.  It allows the user
374  * to manually delete a defect from the working list.
375  */
376 int
377 d_delete()
378 {
379 	int		i, count, num;
380 	u_ioparam_t	ioparam;
381 
382 	assert(!EMBEDDED_SCSI);
383 
384 	/*
385 	 * If the working list is null or zero length, there's nothing
386 	 * to delete.
387 	 */
388 	count = work_list.header.count;
389 	if (work_list.list == NULL || count == 0) {
390 		err_print("No defects to delete.\n");
391 		return (-1);
392 	}
393 	/*
394 	 * Ask the user which defect should be deleted. Bounds are off by
395 	 * one because user sees a one-relative list.
396 	 */
397 	ioparam.io_bounds.lower = 1;
398 	ioparam.io_bounds.upper = count;
399 	num = input(FIO_INT, "Specify defect to be deleted (enter its number)",
400 	    ':', &ioparam, (int *)NULL, DATA_INPUT);
401 	/*
402 	 *
403 	 * The user thinks it's one relative but it's not really.
404 	 */
405 	--num;
406 	/*
407 	 * Print the defect selected and ask the user for confirmation.
408 	 */
409 	fmt_print(DEF_PRINTHEADER);
410 	pr_defect(work_list.list + num, num);
411 	/*
412 	 * Lock out interrupts so the lists don't get mangled.
413 	 */
414 	enter_critical();
415 	/*
416 	 * Move down all the defects beyond the one deleted so the defect
417 	 * list is still fully populated.
418 	 */
419 	for (i = num; i < count - 1; i++)
420 		*(work_list.list + i) = *(work_list.list + i + 1);
421 	/*
422 	 * If the size of the list in sectors has changed, reallocate
423 	 * the list to shrink it appropriately.
424 	 */
425 	if (LISTSIZE(count - 1) < LISTSIZE(count))
426 		work_list.list = (struct defect_entry *)rezalloc(
427 		    (void *)work_list.list, LISTSIZE(count - 1) * SECSIZE);
428 	/*
429 	 * Decrement the defect count.
430 	 */
431 	work_list.header.count--;
432 	/*
433 	 * Recalculate the list's checksum.
434 	 */
435 	(void) checkdefsum(&work_list, CK_MAKESUM);
436 	/*
437 	 * Mark the working list dirty since we modified it.
438 	 */
439 	work_list.flags |= LIST_DIRTY;
440 	fmt_print("defect number %d deleted.\n\n", ++num);
441 	exit_critical();
442 	return (0);
443 }
444 
445 /*
446  * This routine implements the 'print' command.  It prints the working
447  * defect list out in human-readable format.
448  */
449 int
450 d_print()
451 {
452 	int	i, nomore = 0;
453 	int	c, one_line = 0;
454 	int	tty_lines = get_tty_lines();
455 
456 	/*
457 	 * If the working list is null, there's nothing to print.
458 	 */
459 	if (work_list.list == NULL) {
460 		if (EMBEDDED_SCSI)
461 			err_print(
462 "No list defined,extract primary or grown or both defects list first.\n");
463 		else
464 			err_print("No working list defined.\n");
465 		return (-1);
466 	}
467 	/*
468 	 * If we're running from a file, don't use the paging scheme.
469 	 * If we are running interactive, turn off echoing.
470 	 */
471 	if (option_f || (!isatty(0)) || (!isatty(1)))
472 		nomore++;
473 	else {
474 		enter_critical();
475 		echo_off();
476 		charmode_on();
477 		exit_critical();
478 	}
479 	/* Print out the banner. */
480 	if (work_list.header.count != 0)
481 		fmt_print(DEF_PRINTHEADER);
482 
483 	/*
484 	 * Loop through the each defect in the working list.
485 	 */
486 	for (i = 0; i < work_list.header.count; i++) {
487 		/*
488 		 * If we are paging and hit the end of a page, wait for
489 		 * the user to hit either space-bar, "q", or return
490 		 * before going on.
491 		 */
492 		if (one_line ||
493 			(!nomore && ((i + 1) % (tty_lines - 1) == 0))) {
494 			/*
495 			 * Get the next character.
496 			 */
497 			fmt_print("- hit space for more - ");
498 			c = getchar();
499 			fmt_print("\015");
500 			one_line = 0;
501 			/* Handle display one line command (return key) */
502 			if (c == '\012') {
503 				one_line++;
504 			}
505 			/* Handle Quit command */
506 			if (c == 'q') {
507 				fmt_print("                       \015");
508 				goto PRINT_EXIT;
509 			}
510 			/* Handle ^D */
511 			if (c == '\004')
512 				fullabort();
513 		}
514 		/*
515 		 * Print the defect.
516 		 */
517 		pr_defect(work_list.list + i, i);
518 	}
519 	fmt_print("total of %d defects.\n\n", i);
520 	/*
521 	 * If we were doing paging, turn echoing back on.
522 	 */
523 PRINT_EXIT:
524 	if (!nomore) {
525 		enter_critical();
526 		charmode_off();
527 		echo_on();
528 		exit_critical();
529 	}
530 	return (0);
531 }
532 
533 /*
534  * This routine implements the 'dump' command.  It writes the working
535  * defect list to a file.
536  */
537 int
538 d_dump()
539 {
540 	int	i, status = 0;
541 	char	*str;
542 	FILE	*fptr;
543 	struct	defect_entry *dptr;
544 
545 	/*
546 	 * If the working list is null, there's nothing to do.
547 	 */
548 	if (work_list.list == NULL) {
549 		if (EMBEDDED_SCSI)
550 			err_print(
551 "No list defined,extract primary or grown or both defects list first.\n");
552 		else
553 			err_print("No working list defined.\n");
554 		return (-1);
555 	}
556 	/*
557 	 * Ask the user for the name of the defect file.  Note that the
558 	 * input will be in malloc'd space since we are inputting
559 	 * type OSTR.
560 	 */
561 	str = (char *)(uintptr_t)input(FIO_OSTR, "Enter name of defect file",
562 	    ':', (u_ioparam_t *)NULL, (int *)NULL, DATA_INPUT);
563 	/*
564 	 * Lock out interrupts so the file doesn't get half written.
565 	 */
566 	enter_critical();
567 	/*
568 	 * Open the file for writing.
569 	 */
570 	if ((fptr = fopen(str, "w+")) == NULL) {
571 		err_print("unable to open defect file.\n");
572 		status = -1;
573 		goto out;
574 	}
575 	/*
576 	 * Print a header containing the magic number, count, and checksum.
577 	 */
578 	(void) fprintf(fptr, "0x%08x%8d  0x%08x\n",
579 		work_list.header.magicno,
580 		work_list.header.count, work_list.header.cksum);
581 	/*
582 	 * Loop through each defect in the working list.  Write the
583 	 * defect info to the defect file.
584 	 */
585 	for (i = 0; i < work_list.header.count; i++) {
586 		dptr = work_list.list + i;
587 		(void) fprintf(fptr, "%4d%8d%7d%8d%8d%8d\n",
588 			i+1, dptr->cyl, dptr->head,
589 			dptr->bfi, dptr->nbits, dptr->sect);
590 	}
591 	fmt_print("defect file updated, total of %d defects.\n", i);
592 	/*
593 	 * Close the defect file.
594 	 */
595 	(void) fclose(fptr);
596 out:
597 	/*
598 	 * Destroy the string used for the file name.
599 	 */
600 	destroy_data(str);
601 	exit_critical();
602 	fmt_print("\n");
603 	return (status);
604 }
605 
606 /*
607  * This routine implements the 'load' command.  It reads the working
608  * list in from a file.
609  */
610 int
611 d_load()
612 {
613 	int	i, items, status = 0, count, cksum;
614 	uint_t	magicno;
615 	char	*str;
616 	TOKEN	filename;
617 	FILE	*fptr;
618 	struct	defect_entry *dptr;
619 
620 	assert(!EMBEDDED_SCSI);
621 
622 	/*
623 	 * Ask the user for the name of the defect file.  Note that the
624 	 * input will be malloc'd space since we inputted type OSTR.
625 	 */
626 	str = (char *)(uintptr_t)input(FIO_OSTR, "Enter name of defect file",
627 	    ':', (u_ioparam_t *)NULL, (int *)NULL, DATA_INPUT);
628 	/*
629 	 * Copy the file name into local space then destroy the string
630 	 * it came in.  This is simply a precaution against later having
631 	 * to remember to destroy this space.
632 	 */
633 	enter_critical();
634 	(void) strcpy(filename, str);
635 	destroy_data(str);
636 	exit_critical();
637 	/*
638 	 * See if the defect file is accessable.  If not, we can't load
639 	 * from it.  We do this here just so we can get out before asking
640 	 * the user for confirmation.
641 	 */
642 	status = access(filename, 4);
643 	if (status) {
644 		err_print("defect file not accessable.\n");
645 		return (-1);
646 	}
647 	/*
648 	 * Make sure the user is serious.
649 	 */
650 	if (check("ready to update working list, continue"))
651 		return (-1);
652 	/*
653 	 * Lock out interrupts so the list doesn't get half loaded.
654 	 */
655 	enter_critical();
656 	/*
657 	 * Open the defect file.
658 	 */
659 	if ((fptr = fopen(filename, "r")) == NULL) {
660 		err_print("unable to open defect file.\n");
661 		exit_critical();
662 		return (-1);
663 	}
664 	/*
665 	 * Scan in the header.
666 	 */
667 	items = fscanf(fptr, "0x%x%d  0x%x\n", &magicno,
668 		&count, (uint_t *)&cksum);
669 	/*
670 	 * If the header is wrong, this isn't a good defect file.
671 	 */
672 	if (items != 3 || count < 0 ||
673 	    (magicno != (uint_t)DEFECT_MAGIC &&
674 				magicno != (uint_t)NO_CHECKSUM)) {
675 		err_print("Defect file is corrupted.\n");
676 		status = -1;
677 		goto out;
678 	}
679 	/*
680 	 * Kill off any old defects in the working list.
681 	 */
682 	kill_deflist(&work_list);
683 	/*
684 	 * Load the working list header with the header info.
685 	 */
686 	if (magicno == NO_CHECKSUM)
687 		work_list.header.magicno = (uint_t)DEFECT_MAGIC;
688 	else
689 		work_list.header.magicno = magicno;
690 	work_list.header.count = count;
691 	work_list.header.cksum = cksum;
692 	/*
693 	 * Allocate space for the new list.
694 	 */
695 	work_list.list = (struct defect_entry *)zalloc(LISTSIZE(count) *
696 	    SECSIZE);
697 	/*
698 	 * Mark the working list dirty since we are modifying it.
699 	 */
700 	work_list.flags |= LIST_DIRTY;
701 	/*
702 	 * Loop through each defect in the defect file.
703 	 */
704 	for (i = 0; i < count; i++) {
705 		dptr = work_list.list + i;
706 		/*
707 		 * Scan the info into the defect entry.
708 		 */
709 		items = fscanf(fptr, "%*d%hd%hd%d%hd%hd\n", &dptr->cyl,
710 		    &dptr->head, &dptr->bfi, &dptr->nbits, &dptr->sect);
711 		/*
712 		 * If it didn't scan right, give up.
713 		 */
714 		if (items != 5)
715 			goto bad;
716 	}
717 	/*
718 	 * Check to be sure the checksum from the defect file was correct
719 	 * unless there wasn't supposed to be a checksum.
720 	 * If there was supposed to be a valid checksum and there isn't
721 	 * then give up.
722 	 */
723 	if (magicno != NO_CHECKSUM && checkdefsum(&work_list, CK_CHECKSUM))
724 		goto bad;
725 	fmt_print("working list updated, total of %d defects.\n", i);
726 	goto out;
727 
728 bad:
729 	/*
730 	 * Some kind of error occurred.  Kill off the working list and
731 	 * mark the status bad.
732 	 */
733 	err_print("Defect file is corrupted, working list set to NULL.\n");
734 	kill_deflist(&work_list);
735 	status = -1;
736 out:
737 	/*
738 	 * Close the defect file.
739 	 */
740 	(void) fclose(fptr);
741 	exit_critical();
742 	fmt_print("\n");
743 	return (status);
744 }
745 
746 /*
747  * This routine implements the 'commit' command.  It causes the current
748  * defect list to be set equal to the working defect list.  It is the only
749  * way that changes made to the working list can actually take effect in
750  * the next format.
751  */
752 int
753 d_commit()
754 {
755 	/*
756 	 * If the working list wasn't modified, no commit is necessary.
757 	 */
758 	if (work_list.list != NULL && !(work_list.flags & LIST_DIRTY)) {
759 		err_print("working list was not modified.\n");
760 		return (0);
761 	}
762 
763 	/*
764 	 * Make sure the user is serious.
765 	 */
766 	if (check("Ready to update Current Defect List, continue"))
767 		return (-1);
768 	return (do_commit());
769 }
770 
771 int
772 do_commit()
773 {
774 	int	status;
775 
776 	if ((status = commit_list()) == 0) {
777 		/*
778 		 * Remind the user to format the drive, since changing
779 		 * the list does nothing unless a format is performed.
780 		 */
781 		fmt_print(\
782 "Disk must be reformatted for changes to take effect.\n\n");
783 	}
784 	return (status);
785 }
786 
787 
788 static int
789 commit_list()
790 {
791 	int	i;
792 
793 	/*
794 	 * Lock out interrupts so the list doesn't get half copied.
795 	 */
796 	enter_critical();
797 	/*
798 	 * Kill off any current defect list.
799 	 */
800 	kill_deflist(&cur_list);
801 	/*
802 	 * If the working list is null, initialize it to zero length.
803 	 * This is so the user can do a commit on a null list and get
804 	 * a zero length list.  Otherwise there would be no way to get
805 	 * a zero length list conveniently.
806 	 */
807 	if (work_list.list == NULL) {
808 		work_list.header.magicno = (uint_t)DEFECT_MAGIC;
809 		work_list.header.count = 0;
810 		work_list.list = (struct defect_entry *)zalloc(
811 		    LISTSIZE(0) * SECSIZE);
812 	}
813 	/*
814 	 * Copy the working list into the current list.
815 	 */
816 	cur_list.header = work_list.header;
817 	cur_list.list = (struct defect_entry *)zalloc(
818 	    LISTSIZE(cur_list.header.count) * SECSIZE);
819 	for (i = 0; i < cur_list.header.count; i++)
820 		*(cur_list.list + i) = *(work_list.list + i);
821 	/*
822 	 * Mark the working list clean, since it is now the same as the
823 	 * current list.  Note we do not mark the current list dirty,
824 	 * even though it has been changed.  This is because it does
825 	 * not reflect the state of disk, so we don't want it written
826 	 * out until a format has been done.  The format will mark it
827 	 * dirty and write it out.
828 	 */
829 	work_list.flags &= ~(LIST_DIRTY|LIST_RELOAD);
830 	cur_list.flags = work_list.flags;
831 	if (EMBEDDED_SCSI)
832 		fmt_print("Defect List has a total of %d defects.\n",
833 		    cur_list.header.count);
834 	else
835 		fmt_print("Current Defect List updated, total of %d defects.\n",
836 		    cur_list.header.count);
837 	exit_critical();
838 	return (0);
839 }
840 
841 
842 /*
843  * This routine implements the 'create' command.  It creates the
844  * manufacturer's defect on the current disk from the defect list
845  */
846 int
847 d_create()
848 {
849 	int	status;
850 
851 	assert(!EMBEDDED_SCSI);
852 
853 	/*
854 	 * If the controller does not support the extraction, we're out
855 	 * of luck.
856 	 */
857 	if (cur_ops->op_create == NULL) {
858 		err_print("Controller does not support creating ");
859 		err_print("manufacturer's defect list.\n");
860 		return (-1);
861 	}
862 	/*
863 	 * Make sure the user is serious.  Note, for SCSI disks
864 	 * since this is instantaneous, we will just do it and
865 	 * not ask for confirmation.
866 	 */
867 	if (! (cur_ctype->ctype_flags & CF_SCSI) &&
868 	    check(
869 "Ready to create the manufacturers defect information on the disk.\n\
870 This cannot be interrupted and may take a long while.\n\
871 IT WILL DESTROY ALL OF THE DATA ON THE DISK! Continue"))
872 		return (-1);
873 	/*
874 	 * Lock out interrupts so we don't get half the list.
875 	 */
876 	enter_critical();
877 	fmt_print("Creating manufacturer's defect list...");
878 	/*
879 	 * Do the Creation
880 	 */
881 	status = (*cur_ops->op_create)(&work_list);
882 	if (status) {
883 		fmt_print("Creation failed.\n\n");
884 	} else {
885 		fmt_print("Creation complete.\n");
886 	}
887 	exit_critical();
888 	/*
889 	 * Return status.
890 	 */
891 	return (status);
892 }
893 
894 
895 /*
896  * Extract primary defect list - SCSI only
897  */
898 int
899 d_primary()
900 {
901 	int	status;
902 
903 	assert(EMBEDDED_SCSI);
904 
905 	/*
906 	 * Lock out interrupts so we don't get half the list and
907 	 * Kill off the working list.
908 	 */
909 	enter_critical();
910 	kill_deflist(&work_list);
911 	fmt_print("Extracting primary defect list...");
912 
913 	/*
914 	 * Do the extraction.
915 	 */
916 	status = scsi_ex_man(&work_list);
917 	if (status) {
918 		fmt_print("Extraction failed.\n\n");
919 	} else {
920 		fmt_print("Extraction complete.\n");
921 		/*
922 		 * Mark the working list dirty since we modified it.
923 		 * Automatically commit it, for SCSI only.
924 		 */
925 		work_list.flags |= LIST_DIRTY;
926 		status = commit_list();
927 		fmt_print("\n");
928 	}
929 	exit_critical();
930 
931 	/*
932 	 * Return status.
933 	 */
934 	return (status);
935 }
936 
937 
938 /*
939  * Extract grown defects list - SCSI only
940  */
941 int
942 d_grown()
943 {
944 	int	status;
945 
946 	assert(EMBEDDED_SCSI);
947 
948 	/*
949 	 * Lock out interrupts so we don't get half the list and
950 	 * Kill off the working list.
951 	 */
952 	enter_critical();
953 	kill_deflist(&work_list);
954 	fmt_print("Extracting grown defects list...");
955 
956 	/*
957 	 * Do the extraction.
958 	 */
959 	status = scsi_ex_grown(&work_list);
960 	if (status) {
961 		fmt_print("Extraction failed.\n\n");
962 	} else {
963 		fmt_print("Extraction complete.\n");
964 		/*
965 		 * Mark the working list dirty since we modified it.
966 		 * Automatically commit it, for SCSI only.
967 		 */
968 		work_list.flags |= LIST_DIRTY;
969 		status = commit_list();
970 		fmt_print("\n");
971 	}
972 	exit_critical();
973 
974 	/*
975 	 * Return status.
976 	 */
977 	return (status);
978 }
979 
980 
981 /*
982  * Extract both primary and grown defects list - SCSI only
983  */
984 int
985 d_both()
986 {
987 	int	status;
988 
989 	assert(EMBEDDED_SCSI);
990 
991 	/*
992 	 * Lock out interrupts so we don't get half the list and
993 	 * Kill off the working list.
994 	 */
995 	enter_critical();
996 	kill_deflist(&work_list);
997 	fmt_print("Extracting both primary and grown defects lists...");
998 
999 	/*
1000 	 * Do the extraction.
1001 	 */
1002 	status = scsi_ex_cur(&work_list);
1003 	if (status) {
1004 		fmt_print("Extraction failed.\n\n");
1005 	} else {
1006 		fmt_print("Extraction complete.\n");
1007 		/*
1008 		 * Mark the working list dirty since we modified it.
1009 		 * Automatically commit it, for SCSI only.
1010 		 */
1011 		work_list.flags |= LIST_DIRTY;
1012 		status = commit_list();
1013 		fmt_print("\n");
1014 	}
1015 	exit_critical();
1016 
1017 	/*
1018 	 * Return status.
1019 	 */
1020 	return (status);
1021 }
1022