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