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 * 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 = 0; 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 struct dk_minfo mediainfo; 180 uint_t sector_size; 181 182 if ((ioctl(devfd, DKIOCGMEDIAINFO, &mediainfo)) == 0) { 183 sector_size = mediainfo.dki_lbsize; 184 } else { 185 sector_size = NBPSCTR; 186 } 187 trksiz = sector_size * dkg.dkg_nsect; 188 189 /* #define LIBMALLOC */ 190 191 #ifdef LIBMALLOC 192 193 extern int mallopt(); 194 195 /* This adds 5k to the binary, but it's a lot prettier */ 196 197 198 /* make track buffer sector aligned */ 199 if (mallopt(M_GRAIN, sector_size)) { 200 perror("mallopt"); 201 exit(1); 202 } 203 if ((verbuf = malloc(sector_size * dkg.dkg_nsect)) == 204 (char *)NULL) { 205 perror("malloc"); 206 exit(1); 207 } 208 209 #else 210 211 if ((verbuf = malloc(sector_size + sector_size * dkg.dkg_nsect)) 212 == (char *)NULL) { 213 perror("malloc"); 214 exit(1); 215 } 216 verbuf = (char *)((((unsigned long)verbuf + sector_size)) & 217 (-sector_size)); 218 219 #endif 220 221 /* write pattern in track buffer */ 222 223 for (i = 0; i < trksiz; i++) 224 verbuf[i] = (char)0xe5; 225 226 /* Turn off retry, and set trap to turn them on again */ 227 228 (void) signal(SIGINT, verexit); 229 (void) signal(SIGQUIT, verexit); 230 231 if (writeflag == VER_READ) 232 goto do_readonly; 233 234 /* 235 * display warning only if -n arg not passed 236 * (otherwise the UI system will take care of it) 237 */ 238 239 if (print_warn == 1) { 240 (void) printf( 241 "\nCAUTION: ABOUT TO DO DESTRUCTIVE WRITE ON %s\n", device); 242 (void) printf(" THIS WILL DESTROY ANY DATA YOU HAVE ON\n"); 243 (void) printf(" THAT PARTITION OR SLICE.\n"); 244 (void) printf("Do you want to continue (y/n)? "); 245 246 rptr = fgets(replybuf, 64*sizeof (char), stdin); 247 if (!rptr || !((replybuf[0] == 'Y') || (replybuf[0] == 'y'))) 248 exit(10); 249 } 250 251 for (cursec = 0; cursec < unix_size; cursec += dkg.dkg_nsect) { 252 if (llseek(devfd, cursec * sector_size, 0) == -1) { 253 (void) fprintf(stderr, 254 "Error seeking sector %llu Cylinder %llu\n", 255 cursec, cursec / cylsiz); 256 verexit(1); 257 } 258 259 /* 260 * verify sector at a time only when 261 * the whole track write fails; 262 * (if we write a sector at a time, it takes forever) 263 */ 264 265 report("Writing", cursec); 266 267 if (write(devfd, verbuf, trksiz) != trksiz) { 268 tmpend = cursec + dkg.dkg_nsect; 269 for (tmpsec = cursec; tmpsec < tmpend; tmpsec++) { 270 /* 271 * try writing to it once; if this fails, 272 * then announce the sector bad on stderr 273 */ 274 275 if (llseek(devfd, tmpsec * sector_size, 276 0) == -1) { 277 (void) fprintf(stderr, "Error seeking " 278 "sector %llu Cylinder %llu\n", 279 tmpsec, cursec / cylsiz); 280 verexit(1); 281 } 282 283 report("Writing", tmpsec); 284 285 if (write(devfd, verbuf, sector_size) 286 != sector_size) { 287 (void) fprintf(stderr, 288 "%llu\n", tmpsec + unix_base); 289 numbadwr++; 290 } 291 } 292 } 293 } 294 295 (void) putchar(eol); 296 do_readonly: 297 298 for (cursec = 0; cursec < unix_size; cursec += dkg.dkg_nsect) { 299 if (llseek(devfd, cursec * sector_size, 0) == -1) { 300 (void) fprintf(stderr, 301 "Error seeking sector %llu Cylinder %llu\n", 302 cursec, cursec / cylsiz); 303 verexit(1); 304 } 305 306 /* 307 * read a sector at a time only when 308 * the whole track write fails; 309 * (if we do a sector at a time read, it takes forever) 310 */ 311 312 report("Reading", cursec); 313 if (read(devfd, verbuf, trksiz) != trksiz) { 314 tmpend = cursec + dkg.dkg_nsect; 315 for (tmpsec = cursec; tmpsec < tmpend; tmpsec++) { 316 if (llseek(devfd, tmpsec * sector_size, 317 0) == -1) { 318 (void) fprintf(stderr, "Error seeking" 319 " sector %llu Cylinder %llu\n", 320 tmpsec, cursec / cylsiz); 321 verexit(1); 322 } 323 report("Reading", tmpsec); 324 if (read(devfd, verbuf, sector_size) != 325 sector_size) { 326 (void) fprintf(stderr, "%llu\n", 327 tmpsec + unix_base); 328 numbadrd++; 329 } 330 } 331 } 332 } 333 (void) printf("%c%c======== Diskscan complete ========%c", eol, 334 eol, eol); 335 336 if ((numbadrd > 0) || (numbadwr > 0)) { 337 (void) printf("%cFound %ld bad sector(s) on read," 338 " %ld bad sector(s) on write%c", 339 eol, numbadrd, numbadwr, eol); 340 } 341 } 342 343 static void 344 verexit(int code) 345 { 346 (void) printf("\n"); 347 exit(code); 348 } 349 350 351 /* 352 * report where we are... 353 */ 354 355 static void 356 report(char *what, diskaddr_t sector) 357 { 358 (void) printf("%s sector %-19llu of %-19llu%c", what, sector, 359 unix_size, eol); 360 } 361