1 #include <u.h> 2 #include <libc.h> 3 #include <bio.h> 4 #include <ctype.h> 5 #include <disk.h> 6 7 static Disk* 8 mkwidth(Disk *disk) 9 { 10 char buf[40]; 11 12 snprint(buf, sizeof buf, "%lld", disk->size); 13 disk->width = strlen(buf); 14 return disk; 15 } 16 17 /* 18 * Discover the disk geometry by various sleazeful means. 19 * 20 * First, if there is a partition table in sector 0, 21 * see if all the partitions have the same end head 22 * and sector; if so, we'll assume that that's the 23 * right count. 24 * 25 * If that fails, we'll try looking at the geometry that the ATA 26 * driver supplied, if any, and translate that as a 27 * BIOS might. 28 * 29 * If that too fails, which should only happen on a SCSI 30 * disk with no currently defined partitions, we'll try 31 * various common (h, s) pairs used by BIOSes when faking 32 * the geometries. 33 */ 34 typedef struct Table Table; 35 typedef struct Tentry Tentry; 36 struct Tentry { 37 uchar active; /* active flag */ 38 uchar starth; /* starting head */ 39 uchar starts; /* starting sector */ 40 uchar startc; /* starting cylinder */ 41 uchar type; /* partition type */ 42 uchar endh; /* ending head */ 43 uchar ends; /* ending sector */ 44 uchar endc; /* ending cylinder */ 45 uchar xlba[4]; /* starting LBA from beginning of disc */ 46 uchar xsize[4]; /* size in sectors */ 47 }; 48 enum { 49 Toffset = 446, /* offset of partition table in sector */ 50 Magic0 = 0x55, 51 Magic1 = 0xAA, 52 NTentry = 4, 53 }; 54 struct Table { 55 Tentry entry[NTentry]; 56 uchar magic[2]; 57 }; 58 static int 59 partitiongeometry(Disk *disk) 60 { 61 char *rawname; 62 int i, h, rawfd, s; 63 uchar buf[512]; 64 Table *t; 65 66 if(disk->c == 0 || disk->h == 0 || disk->s == 0) 67 return -1; 68 69 t = (Table*)(buf + Toffset); 70 71 /* 72 * look for an MBR first in the /dev/sdXX/data partition, otherwise 73 * attempt to fall back on the current partition. 74 */ 75 rawname = malloc(strlen(disk->prefix) + 5); /* prefix + "data" + nul */ 76 if(rawname == nil) 77 return -1; 78 79 strcpy(rawname, disk->prefix); 80 strcat(rawname, "data"); 81 rawfd = open(rawname, OREAD); 82 free(rawname); 83 if(rawfd >= 0 84 && seek(rawfd, 0, 0) >= 0 85 && readn(rawfd, buf, 512) == 512 86 && t->magic[0] == Magic0 87 && t->magic[1] == Magic1) { 88 close(rawfd); 89 } else { 90 if(rawfd >= 0) 91 close(rawfd); 92 if(seek(disk->fd, 0, 0) < 0 93 || readn(disk->fd, buf, 512) != 512 94 || t->magic[0] != Magic0 95 || t->magic[1] != Magic1) { 96 return -1; 97 } 98 } 99 100 h = s = -1; 101 for(i=0; i<NTentry; i++) { 102 if(t->entry[i].type == 0) 103 continue; 104 105 t->entry[i].ends &= 63; 106 if(h == -1) { 107 h = t->entry[i].endh; 108 s = t->entry[i].ends; 109 } else { 110 /* 111 * Only accept the partition info if every 112 * partition is consistent. 113 */ 114 if(h != t->entry[i].endh || s != t->entry[i].ends) 115 return -1; 116 } 117 } 118 119 if(h == -1) 120 return -1; 121 122 disk->h = h+1; /* heads count from 0 */ 123 disk->s = s; /* sectors count from 1 */ 124 disk->c = disk->secs / (disk->h*disk->s); 125 disk->chssrc = Gpart; 126 return 0; 127 } 128 129 /* 130 * If there is ATA geometry, use it, perhaps massaged. 131 */ 132 static int 133 drivergeometry(Disk *disk) 134 { 135 int m; 136 137 if(disk->c == 0 || disk->h == 0 || disk->s == 0) 138 return -1; 139 140 disk->chssrc = Gdisk; 141 if(disk->c < 1024) 142 return 0; 143 144 switch(disk->h) { 145 case 15: 146 disk->h = 255; 147 disk->c /= 17; 148 return 0; 149 150 default: 151 for(m = 2; m*disk->h < 256; m *= 2) { 152 if(disk->c/m < 1024) { 153 disk->c /= m; 154 disk->h *= m; 155 return 0; 156 } 157 } 158 159 /* set to 255, 63 and be done with it */ 160 disk->h = 255; 161 disk->s = 63; 162 disk->c = disk->secs / (disk->h * disk->s); 163 return 0; 164 } 165 } 166 167 /* 168 * There's no ATA geometry and no partitions. 169 * Our guess is as good as anyone's. 170 */ 171 static struct { 172 int h; 173 int s; 174 } guess[] = { 175 64, 32, 176 64, 63, 177 128, 63, 178 255, 63, 179 }; 180 static int 181 guessgeometry(Disk *disk) 182 { 183 int i; 184 long c; 185 186 disk->chssrc = Gguess; 187 c = 1024; 188 for(i=0; i<nelem(guess); i++) 189 if(c*guess[i].h*guess[i].s >= disk->secs) { 190 disk->h = guess[i].h; 191 disk->s = guess[i].s; 192 disk->c = disk->secs / (disk->h * disk->s); 193 return 0; 194 } 195 196 /* use maximum values */ 197 disk->h = 255; 198 disk->s = 63; 199 disk->c = disk->secs / (disk->h * disk->s); 200 return 0; 201 } 202 203 static void 204 findgeometry(Disk *disk) 205 { 206 if(partitiongeometry(disk) < 0 207 && drivergeometry(disk) < 0 208 && guessgeometry(disk) < 0) { /* can't happen */ 209 print("we're completely confused about your disk; sorry\n"); 210 assert(0); 211 } 212 } 213 214 static Disk* 215 openfile(Disk *disk) 216 { 217 Dir *d; 218 219 if((d = dirfstat(disk->fd)) == nil){ 220 free(disk); 221 return nil; 222 } 223 224 disk->secsize = 512; 225 disk->size = d->length; 226 disk->secs = disk->size / disk->secsize; 227 disk->offset = 0; 228 free(d); 229 230 findgeometry(disk); 231 return mkwidth(disk); 232 } 233 234 static Disk* 235 opensd(Disk *disk) 236 { 237 Biobuf b; 238 char *p, *f[10]; 239 int nf; 240 241 Binit(&b, disk->ctlfd, OREAD); 242 while(p = Brdline(&b, '\n')) { 243 p[Blinelen(&b)-1] = '\0'; 244 nf = tokenize(p, f, nelem(f)); 245 if(nf >= 3 && strcmp(f[0], "geometry") == 0) { 246 disk->secsize = strtoll(f[2], 0, 0); 247 if(nf >= 6) { 248 disk->c = strtol(f[3], 0, 0); 249 disk->h = strtol(f[4], 0, 0); 250 disk->s = strtol(f[5], 0, 0); 251 } 252 } 253 if(nf >= 4 && strcmp(f[0], "part") == 0 && strcmp(f[1], disk->part) == 0) { 254 disk->offset = strtoll(f[2], 0, 0); 255 disk->secs = strtoll(f[3], 0, 0) - disk->offset; 256 } 257 } 258 259 260 disk->size = disk->secs * disk->secsize; 261 if(disk->size <= 0) { 262 strcpy(disk->part, ""); 263 disk->type = Tfile; 264 return openfile(disk); 265 } 266 267 findgeometry(disk); 268 return mkwidth(disk); 269 } 270 271 Disk* 272 opendisk(char *disk, int rdonly, int noctl) 273 { 274 char *p, *q; 275 Disk *d; 276 277 d = mallocz(sizeof(*d), 1); 278 if(d == nil) 279 return nil; 280 281 d->fd = d->wfd = d->ctlfd = -1; 282 d->rdonly = rdonly; 283 284 d->fd = open(disk, OREAD); 285 if(d->fd < 0) { 286 werrstr("cannot open disk file"); 287 free(d); 288 return nil; 289 } 290 291 if(rdonly == 0) { 292 d->wfd = open(disk, OWRITE); 293 if(d->wfd < 0) 294 d->rdonly = 1; 295 } 296 297 if(noctl) 298 return openfile(d); 299 300 p = malloc(strlen(disk) + 4); /* 4: slop for "ctl\0" */ 301 if(p == nil) { 302 close(d->wfd); 303 close(d->fd); 304 free(d); 305 return nil; 306 } 307 strcpy(p, disk); 308 309 /* check for floppy(3) disk */ 310 if(strlen(p) >= 7) { 311 q = p+strlen(p)-7; 312 if(q[0] == 'f' && q[1] == 'd' && isdigit(q[2]) && strcmp(q+3, "disk") == 0) { 313 strcpy(q+3, "ctl"); 314 if((d->ctlfd = open(p, ORDWR)) >= 0) { 315 *q = '\0'; 316 d->prefix = p; 317 d->type = Tfloppy; 318 return openfile(d); 319 } 320 } 321 } 322 323 /* attempt to find sd(3) disk or partition */ 324 if(q = strrchr(p, '/')) 325 q++; 326 else 327 q = p; 328 329 strcpy(q, "ctl"); 330 if((d->ctlfd = open(p, ORDWR)) >= 0) { 331 *q = '\0'; 332 d->prefix = p; 333 d->type = Tsd; 334 d->part = strdup(disk+(q-p)); 335 if(d->part == nil){ 336 close(d->ctlfd); 337 close(d->wfd); 338 close(d->fd); 339 free(p); 340 free(d); 341 return nil; 342 } 343 return opensd(d); 344 } 345 346 *q = '\0'; 347 d->prefix = p; 348 /* assume we just have a normal file */ 349 d->type = Tfile; 350 return openfile(d); 351 } 352