#include "global.h"
#include "analyze.h"
#include <stdlib.h>
#include <errno.h>
#include "misc.h"
#include "defect.h"
#include "label.h"
#include "param.h"
#include "checkdev.h"
int scan_entire = 1;
diskaddr_t scan_lower = 0;
diskaddr_t scan_upper = 0;
int scan_correct = 1;
int scan_stop = 0;
int scan_loop = 0;
int scan_passes = 2;
int scan_random = 0;
uint_t scan_size = 0;
int scan_auto = 1;
int scan_restore_defects = 1;
int scan_restore_label = 1;
offset_t scan_cur_block = -1;
int64_t scan_blocks_fixed = -1;
int media_error;
int disk_error;
static unsigned int scan_patterns[] = {
0xc6dec6de,
0x6db6db6d,
0x00000000,
0xffffffff,
0xaaaaaaaa,
};
#define NPATTERNS 5
static unsigned int purge_patterns[] = {
0xaaaaaaaa,
0x55555555,
0xaaaaaaaa,
0xaaaaaaaa,
};
static unsigned int alpha_pattern = 0x40404040;
static int scan_repair(diskaddr_t bn, int mode);
static int analyze_blocks(int flags, diskaddr_t blkno, uint_t blkcnt,
unsigned data, int init, int driver_flags, int *xfercntp);
static int handle_error_conditions(void);
static int verify_blocks(int flags, diskaddr_t blkno, uint_t blkcnt,
unsigned data, int driver_flags, int *xfercntp);
int
do_scan(int flags, int mode)
{
diskaddr_t start, end, curnt;
int pass, needinit, data;
uint_t size;
int status, founderr, i, j;
int error = 0;
int pattern = 0;
int xfercnt;
if (scan_correct && !EMBEDDED_SCSI && (cur_ops->op_repair != NULL) &&
(cur_list.list == NULL)) {
err_print("Current Defect List must be initialized ");
err_print("to do automatic repair.\n");
return (-1);
}
if (scan_entire) {
start = 0;
if (cur_label == L_TYPE_SOLARIS) {
if (cur_ctype->ctype_flags & CF_SCSI)
end = datasects() - 1;
else
end = physsects() - 1;
} else if (cur_label == L_TYPE_EFI) {
end = cur_parts->etoc->efi_last_lba;
}
} else {
start = scan_lower;
end = scan_upper;
}
if ((flags & (SCAN_PATTERN | SCAN_WRITE)) &&
(checkmount(start, end))) {
err_print("Cannot do analysis on a mounted partition.\n");
return (-1);
}
if ((flags & (SCAN_PATTERN | SCAN_WRITE)) &&
(checkswap(start, end))) {
err_print("Cannot do analysis on a partition \
which is currently being used for swapping.\n");
return (-1);
}
if ((flags & (SCAN_PATTERN | SCAN_WRITE)) &&
(checkdevinuse(cur_disk->disk_name, (diskaddr_t)-1,
(diskaddr_t)-1, 0, 0))) {
err_print("Cannot do analysis on a partition "
"while it in use as described above.\n");
return (-1);
}
if (flags & (SCAN_PATTERN | SCAN_WRITE)) {
if (cur_label == L_TYPE_SOLARIS) {
if (start < (diskaddr_t)totalsects() &&
end >= (diskaddr_t)datasects()) {
if (!EMBEDDED_SCSI) {
cur_list.flags |= LIST_DIRTY;
}
if (cur_disk->disk_flags & DSK_LABEL)
cur_flags |= LABEL_DIRTY;
}
}
if (start == 0) {
if (cur_disk->disk_flags & DSK_LABEL)
cur_flags |= LABEL_DIRTY;
}
}
scan_blocks_fixed = 0;
for (pass = 0; pass < scan_passes || scan_loop; pass++) {
if (flags & SCAN_PATTERN) {
if (scan_random)
data = (int)mrand48();
else
data = scan_patterns[pass % NPPATTERNS];
if (flags & SCAN_PURGE) {
flags &= ~(SCAN_PURGE_READ_PASS
| SCAN_PURGE_ALPHA_PASS);
switch (pattern % (NPPATTERNS + 1)) {
case NPPATTERNS:
pattern = 0;
if (!error) {
fmt_print(
"\nThe last %d passes were successful, running alpha pattern pass", NPPATTERNS);
flags |= SCAN_PURGE_ALPHA_PASS;
data = alpha_pattern;
} else {
data = purge_patterns[pattern];
pattern++;
};
break;
case READPATTERN:
flags |= SCAN_PURGE_READ_PASS;
default:
data = purge_patterns[pattern];
pattern++;
break;
}
}
fmt_print("\n pass %d", pass);
fmt_print(" - pattern = 0x%x", data);
} else
fmt_print("\n pass %d", pass);
fmt_print("\n");
needinit = 1;
if (log_file) {
pr_dblock(log_print, start);
log_print("\n");
}
xfercnt = 0;
for (curnt = start; curnt <= end; curnt += size) {
if ((end - curnt) < scan_size)
size = end - curnt + 1;
else
size = scan_size;
scan_cur_block = curnt;
nolog_print(" ");
pr_dblock(nolog_print, curnt);
nolog_print(" \015");
(void) fflush(stdout);
disk_error = 0;
status = analyze_blocks(flags, curnt, size,
(unsigned)data, needinit, (F_ALLERRS | F_SILENT),
&xfercnt);
needinit = 0;
if (!status)
continue;
if (handle_error_conditions()) {
scan_blocks_fixed = scan_cur_block = -1;
return (-1);
}
needinit = 1;
if (!media_error)
continue;
nolog_print(" ");
pr_dblock(nolog_print, curnt);
nolog_print(" \015");
(void) fflush(stdout);
founderr = 0;
for (j = 0; j < size * 5; j++) {
i = j % size;
disk_error = 0;
status = analyze_blocks(flags, (curnt + i), 1,
(unsigned)data, needinit, F_ALLERRS, NULL);
needinit = 0;
if (!status)
continue;
if (handle_error_conditions()) {
scan_blocks_fixed = scan_cur_block = -1;
return (-1);
}
needinit = 1;
if (!media_error)
continue;
founderr = 1;
if (scan_correct && (flags != SCAN_VALID)) {
if (scan_repair(curnt+i, mode)) {
error = -1;
}
} else
err_print("\n");
if (scan_stop)
goto out;
}
needinit = 1;
if (!founderr) {
err_print("Warning: unable to pinpoint ");
err_print("defective block.\n");
}
}
enter_critical();
if (log_file) {
pr_dblock(log_print, scan_cur_block);
log_print("\n");
}
scan_cur_block = -1;
exit_critical();
fmt_print("\n");
if (flags & SCAN_VERIFY) {
flags ^= SCAN_VERIFY_READ_PASS;
}
}
out:
fmt_print("\n");
enter_critical();
if (!EMBEDDED_SCSI && (cur_list.flags & LIST_DIRTY) &&
(scan_restore_defects)) {
cur_list.flags = 0;
write_deflist(&cur_list);
}
if ((cur_flags & LABEL_DIRTY) && (scan_restore_label)) {
cur_flags &= ~LABEL_DIRTY;
(void) write_label();
}
if (log_file && scan_cur_block >= 0) {
pr_dblock(log_print, scan_cur_block);
log_print("\n");
}
fmt_print("Total of %lld defective blocks repaired.\n",
scan_blocks_fixed);
scan_blocks_fixed = scan_cur_block = -1;
exit_critical();
return (error);
}
static int
scan_repair(diskaddr_t bn, int mode)
{
int status;
int result = 1;
char *buf;
int buf_is_good;
int i;
if (cur_ops->op_repair == NULL) {
err_print("Warning: Controller does ");
err_print("not support repairing.\n\n");
return (result);
}
buf = malloc(cur_blksz);
if (buf == NULL) {
err_print("Warning: no memory.\n\n");
return (result);
}
enter_critical();
buf_is_good = 0;
for (i = 0; i < 5; i++) {
status = (*cur_ops->op_rdwr)(DIR_READ, cur_file, bn, 1,
buf, F_SILENT, NULL);
if (status == 0) {
buf_is_good = 1;
break;
}
}
fmt_print("Repairing %s error on %llu (",
buf_is_good ? "soft" : "hard", bn);
pr_dblock(fmt_print, bn);
fmt_print(")...");
status = (*cur_ops->op_repair)(bn, mode);
if (status) {
fmt_print("failed.\n\n");
} else {
if (!buf_is_good) {
bzero(buf, cur_blksz);
fmt_print("Warning: Block %llu zero-filled.\n", bn);
} else {
fmt_print("ok.\n");
}
status = (*cur_ops->op_rdwr)(DIR_WRITE, cur_file, bn,
1, buf, (F_SILENT | F_ALLERRS), NULL);
if (status == 0) {
status = (*cur_ops->op_rdwr)(DIR_READ, cur_file, bn,
1, buf, (F_SILENT | F_ALLERRS), NULL);
}
if (status) {
fmt_print("The new block also appears defective.\n");
}
fmt_print("\n");
if (EMBEDDED_SCSI) {
if (cur_list.list != NULL) {
if (cur_list.flags & LIST_PGLIST) {
add_ldef(bn, &cur_list);
} else {
kill_deflist(&cur_list);
}
}
} else if (cur_ctype->ctype_flags & CF_WLIST) {
kill_deflist(&cur_list);
(*cur_ops->op_ex_cur)(&cur_list);
fmt_print("Current list updated\n");
} else {
add_ldef(bn, &cur_list);
write_deflist(&cur_list);
}
kill_deflist(&work_list);
scan_blocks_fixed++;
result = 0;
}
exit_critical();
free(buf);
return (result);
}
static int
analyze_blocks(int flags, diskaddr_t blkno, uint_t blkcnt, unsigned data,
int init, int driver_flags, int *xfercntp)
{
int corrupt = 0;
int status;
diskaddr_t i, nints;
unsigned *ptr = (uint_t *)pattern_buf;
media_error = 0;
if (flags & SCAN_VERIFY) {
return (verify_blocks(flags, blkno, blkcnt, data,
driver_flags, xfercntp));
}
nints = (diskaddr_t)blkcnt * cur_blksz / sizeof (int);
if ((flags & SCAN_PATTERN) && init) {
for (i = 0; i < nints; i++)
*((int *)((int *)pattern_buf + i)) = data;
}
enter_critical();
if (flags & SCAN_VALID) {
status = (*cur_ops->op_rdwr)(DIR_READ, cur_file, blkno,
blkcnt, (caddr_t)cur_buf, driver_flags, xfercntp);
if (status)
goto bad;
}
if (flags & SCAN_PATTERN) {
if (flags & SCAN_VALID)
corrupt++;
if (!(flags & SCAN_PURGE_READ_PASS)) {
status = (*cur_ops->op_rdwr)(DIR_WRITE, cur_file, blkno,
blkcnt, (caddr_t)pattern_buf, driver_flags,
xfercntp);
if (status)
goto bad;
}
if ((!(flags & SCAN_PURGE)) || (flags & SCAN_PURGE_READ_PASS)) {
status = (*cur_ops->op_rdwr)(DIR_READ, cur_file, blkno,
blkcnt, (caddr_t)pattern_buf, driver_flags,
xfercntp);
if (status)
goto bad;
}
}
if ((flags & SCAN_COMPARE) || (flags & SCAN_PURGE_READ_PASS)) {
for (i = nints, ptr = (uint_t *)pattern_buf; i; i--)
if (*ptr++ != data) {
err_print("Data miscompare error (expecting ");
err_print("0x%x, got 0x%x) at ", data,
*((int *)((int *)pattern_buf +
(nints - i))));
pr_dblock(err_print, blkno);
err_print(", offset = 0x%llx.\n",
(nints - i) * sizeof (int));
goto bad;
}
}
if (flags & SCAN_WRITE) {
status = (*cur_ops->op_rdwr)(DIR_WRITE, cur_file, blkno,
blkcnt, (caddr_t)cur_buf, driver_flags, xfercntp);
if (status)
goto bad;
}
exit_critical();
return (0);
bad:
if (corrupt) {
if ((*cur_ops->op_rdwr)(DIR_WRITE, cur_file, blkno,
blkcnt, (caddr_t)cur_buf, F_NORMAL, xfercntp))
err_print("Warning: unable to restore original data.\n");
}
exit_critical();
return (-1);
}
static int
verify_blocks(int flags, diskaddr_t blkno, uint_t blkcnt, unsigned data,
int driver_flags, int *xfercntp)
{
int status, i, nints;
unsigned *ptr = (uint_t *)pattern_buf;
nints = cur_blksz / sizeof (int);
if (!(flags & SCAN_VERIFY_READ_PASS)) {
for (data = blkno; data < blkno + blkcnt; data++) {
for (i = 0; i < nints; i++) {
*ptr++ = data;
}
}
ptr = (uint_t *)pattern_buf;
}
if (!(flags & SCAN_VERIFY_READ_PASS)) {
status = (*cur_ops->op_rdwr)(DIR_WRITE, cur_file, blkno,
blkcnt, (caddr_t)pattern_buf, driver_flags, xfercntp);
if (status)
goto bad;
} else {
status = (*cur_ops->op_rdwr)(DIR_READ, cur_file, blkno,
blkcnt, (caddr_t)pattern_buf, driver_flags, xfercntp);
if (status)
goto bad;
for (data = blkno; data < blkno + blkcnt; data++) {
for (i = 0; i < nints; i++) {
if (*ptr++ != data) {
ptr--;
err_print("Data miscompare error "
"(expecting 0x%x, got 0x%x) at ",
data, *ptr);
pr_dblock(err_print, blkno);
err_print(", offset = 0x%x.\n",
(ptr - (uint_t *)pattern_buf) *
sizeof (int));
goto bad;
}
}
}
}
return (0);
bad:
return (-1);
}
static int
handle_error_conditions(void)
{
if (errno == ENXIO) {
fmt_print("\n\nWarning:Cannot access drive, ");
fmt_print("aborting surface analysis.\n");
return (-1);
}
switch (disk_error) {
case DISK_STAT_RESERVED:
case DISK_STAT_UNAVAILABLE:
fmt_print("\n\nWarning:Drive may be reserved ");
fmt_print("or has been removed, ");
fmt_print("aborting surface analysis.\n");
return (-1);
case DISK_STAT_NOTREADY:
fmt_print("\n\nWarning: Drive not ready, ");
fmt_print("aborting surface analysis.\n");
return (-1);
case DISK_STAT_DATA_PROTECT:
fmt_print("\n\nWarning: Drive is write protected, ");
fmt_print("aborting surface analysis.\n");
return (-1);
default:
break;
}
return (0);
}