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