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