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 * diskscan: 28 * performs a verification pass over a device specified on command line; 29 * display progress on stdout, and print bad sector numbers to stderr 30 */ 31 #include <stdio.h> 32 #include <stdlib.h> 33 #include <fcntl.h> 34 #include <unistd.h> 35 #include <stropts.h> 36 #include <memory.h> 37 #include <ctype.h> 38 #include <malloc.h> 39 #include <signal.h> 40 #include <sys/types.h> 41 #include <sys/param.h> 42 #include <sys/stat.h> 43 #include <sys/vtoc.h> 44 #include <sys/dkio.h> 45 46 static void verexit(); /* signal handler and exit routine */ 47 static void report(); /* tell user how we're getting on */ 48 static void scandisk(char *device, int devfd, int writeflag); 49 static void report(char *what, diskaddr_t sector); 50 static void verexit(int code); 51 52 #define TRUE 1 53 #define FALSE 0 54 #define VER_WRITE 1 55 #define VER_READ 2 56 57 static char *progname; 58 static struct dk_geom dkg; /* physical device boot info */ 59 static char replybuf[64]; /* used for user replies to questions */ 60 static diskaddr_t unix_base; /* first sector of UNIX System partition */ 61 static diskaddr_t unix_size; /* # sectors in UNIX System partition */ 62 static long numbadrd = 0; /* number of bad sectors on read */ 63 static long numbadwr = 0; /* number of bad sectors on write */ 64 static char eol = '\n'; /* end-of-line char (if -n, we set to '\n') */ 65 static int print_warn = 1; /* should the warning message be printed? */ 66 static int do_scan = VER_READ; 67 68 int 69 main(int argc, char *argv[]) { 70 extern int optind; 71 int devfd; /* device file descriptor */ 72 struct stat statbuf; 73 struct part_info part_info; 74 struct extpart_info extpartinfo; 75 int c; 76 int errflag = 0; 77 char *device; 78 progname = argv[0]; 79 80 /* Don't buffer stdout - we don't want to see bursts */ 81 82 setbuf(stdout, NULL); 83 84 while ((c = getopt(argc, argv, "Wny")) != -1) 85 { 86 switch (c) { 87 case 'W': 88 do_scan = VER_WRITE; 89 break; 90 91 case 'n': 92 eol = '\r'; 93 break; 94 95 case 'y': 96 print_warn = 0; 97 break; 98 99 default: 100 ++errflag; 101 break; 102 } 103 } 104 105 if ((argc - optind) < 1) 106 errflag++; 107 108 if (errflag) { 109 (void) fprintf(stderr, 110 "\nUsage: %s [-W] [-n] [-y] <phys_device_name> \n", 111 progname); 112 exit(1); 113 } 114 115 device = argv[optind]; 116 117 if (stat(device, &statbuf)) { 118 (void) fprintf(stderr, 119 "%s: invalid device %s, stat failed\n", progname, device); 120 perror(""); 121 exit(4); 122 } 123 if ((statbuf.st_mode & S_IFMT) != S_IFCHR) { 124 (void) fprintf(stderr, 125 "%s: device %s is not character special\n", 126 progname, device); 127 exit(5); 128 } 129 if ((devfd = open(device, O_RDWR)) == -1) { 130 (void) fprintf(stderr, 131 "%s: open of %s failed\n", progname, device); 132 perror(""); 133 exit(8); 134 } 135 136 if ((ioctl(devfd, DKIOCGGEOM, &dkg)) == -1) { 137 (void) fprintf(stderr, 138 "%s: unable to get disk geometry.\n", progname); 139 perror(""); 140 exit(9); 141 } 142 143 if ((ioctl(devfd, DKIOCEXTPARTINFO, &extpartinfo)) == 0) { 144 unix_base = extpartinfo.p_start; 145 unix_size = extpartinfo.p_length; 146 } else { 147 if ((ioctl(devfd, DKIOCPARTINFO, &part_info)) == 0) { 148 unix_base = (ulong_t)part_info.p_start; 149 unix_size = (uint_t)part_info.p_length; 150 } else { 151 (void) fprintf(stderr, "%s: unable to get partition " 152 "info.\n", progname); 153 perror(""); 154 exit(9); 155 } 156 } 157 158 scandisk(device, devfd, do_scan); 159 return (0); 160 } 161 162 /* 163 * scandisk: 164 * attempt to read every sector of the drive; 165 * display bad sectors found on stderr 166 */ 167 168 static void 169 scandisk(char *device, int devfd, int writeflag) 170 { 171 int trksiz = NBPSCTR * dkg.dkg_nsect; 172 char *verbuf; 173 diskaddr_t cursec; 174 int cylsiz = dkg.dkg_nsect * dkg.dkg_nhead; 175 int i; 176 char *rptr; 177 diskaddr_t tmpend = 0; 178 diskaddr_t tmpsec = 0; 179 180 /* #define LIBMALLOC */ 181 182 #ifdef LIBMALLOC 183 184 extern int mallopt(); 185 186 /* This adds 5k to the binary, but it's a lot prettier */ 187 188 189 /* make track buffer sector aligned */ 190 if (mallopt(M_GRAIN, 0x200)) { 191 perror("mallopt"); 192 exit(1); 193 } 194 if ((verbuf = malloc(NBPSCTR * dkg.dkg_nsect)) == (char *)NULL) { 195 perror("malloc"); 196 exit(1); 197 } 198 199 #else 200 201 if ((verbuf = malloc(0x200 + NBPSCTR * dkg.dkg_nsect)) 202 == (char *)NULL) { 203 perror("malloc"); 204 exit(1); 205 } 206 verbuf = (char *)(((unsigned long)verbuf + 0x00000200) & 0xfffffe00); 207 208 #endif 209 210 /* write pattern in track buffer */ 211 212 for (i = 0; i < trksiz; i++) 213 verbuf[i] = (char)0xe5; 214 215 /* Turn off retry, and set trap to turn them on again */ 216 217 (void) signal(SIGINT, verexit); 218 (void) signal(SIGQUIT, verexit); 219 220 if (writeflag == VER_READ) 221 goto do_readonly; 222 223 /* 224 * display warning only if -n arg not passed 225 * (otherwise the UI system will take care of it) 226 */ 227 228 if (print_warn == 1) { 229 (void) printf( 230 "\nCAUTION: ABOUT TO DO DESTRUCTIVE WRITE ON %s\n", device); 231 (void) printf(" THIS WILL DESTROY ANY DATA YOU HAVE ON\n"); 232 (void) printf(" THAT PARTITION OR SLICE.\n"); 233 (void) printf("Do you want to continue (y/n)? "); 234 235 rptr = fgets(replybuf, 64*sizeof (char), stdin); 236 if (!rptr || !((replybuf[0] == 'Y') || (replybuf[0] == 'y'))) 237 exit(10); 238 } 239 240 for (cursec = 0; cursec < unix_size; cursec += dkg.dkg_nsect) { 241 if (llseek(devfd, cursec * NBPSCTR, 0) == -1) { 242 (void) fprintf(stderr, 243 "Error seeking sector %llu Cylinder %llu\n", 244 cursec, cursec / cylsiz); 245 verexit(1); 246 } 247 248 /* 249 * verify sector at a time only when 250 * the whole track write fails; 251 * (if we write a sector at a time, it takes forever) 252 */ 253 254 report("Writing", cursec); 255 256 if (write(devfd, verbuf, trksiz) != trksiz) { 257 tmpend = cursec + dkg.dkg_nsect; 258 for (tmpsec = cursec; tmpsec < tmpend; tmpsec++) { 259 /* 260 * try writing to it once; if this fails, 261 * then announce the sector bad on stderr 262 */ 263 264 if (llseek(devfd, tmpsec * NBPSCTR, 0) == -1) { 265 (void) fprintf(stderr, "Error seeking " 266 "sector %llu Cylinder %llu\n", 267 tmpsec, cursec / cylsiz); 268 verexit(1); 269 } 270 271 report("Writing", tmpsec); 272 273 if (write(devfd, verbuf, NBPSCTR) != NBPSCTR) { 274 (void) fprintf(stderr, 275 "%llu\n", tmpsec + unix_base); 276 numbadwr++; 277 } 278 } 279 } 280 } 281 282 (void) putchar(eol); 283 do_readonly: 284 285 for (cursec = 0; cursec < unix_size; cursec += dkg.dkg_nsect) { 286 if (llseek(devfd, cursec * NBPSCTR, 0) == -1) { 287 (void) fprintf(stderr, 288 "Error seeking sector %llu Cylinder %llu\n", 289 cursec, cursec / cylsiz); 290 verexit(1); 291 } 292 293 /* 294 * read a sector at a time only when 295 * the whole track write fails; 296 * (if we do a sector at a time read, it takes forever) 297 */ 298 299 report("Reading", cursec); 300 if (read(devfd, verbuf, trksiz) != trksiz) { 301 tmpend = cursec + dkg.dkg_nsect; 302 for (tmpsec = cursec; tmpsec < tmpend; tmpsec++) { 303 if (llseek(devfd, tmpsec * NBPSCTR, 0) == -1) { 304 (void) fprintf(stderr, "Error seeking" 305 " sector %llu Cylinder %llu\n", 306 tmpsec, cursec / cylsiz); 307 verexit(1); 308 } 309 report("Reading", tmpsec); 310 if (read(devfd, verbuf, NBPSCTR) != NBPSCTR) { 311 (void) fprintf(stderr, "%llu\n", 312 tmpsec + unix_base); 313 numbadrd++; 314 } 315 } 316 } 317 } 318 (void) printf("%c%c======== Diskscan complete ========%c", eol, 319 eol, eol); 320 321 if ((numbadrd > 0) || (numbadwr > 0)) { 322 (void) printf("%cFound %ld bad sector(s) on read," 323 " %ld bad sector(s) on write%c", 324 eol, numbadrd, numbadwr, eol); 325 } 326 } 327 328 static void 329 verexit(int code) 330 { 331 (void) printf("\n"); 332 exit(code); 333 } 334 335 336 /* 337 * report where we are... 338 */ 339 340 static void 341 report(char *what, diskaddr_t sector) 342 { 343 (void) printf("%s sector %-19llu of %-19llu%c", what, sector, 344 unix_size, eol); 345 } 346