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 sprint(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 t = (Table*)(buf + Toffset); 67 68 /* 69 * look for an MBR first in the /dev/sdXX/data partition, otherwise 70 * attempt to fall back on the current partition. 71 */ 72 rawname = malloc(strlen(disk->prefix) + 5); /* prefix + "data" + nul */ 73 if(rawname == nil) 74 return -1; 75 76 strcpy(rawname, disk->prefix); 77 strcat(rawname, "data"); 78 rawfd = open(rawname, OREAD); 79 free(rawname); 80 if(rawfd >= 0 81 && seek(rawfd, 0, 0) >= 0 82 && readn(rawfd, buf, 512) == 512 83 && t->magic[0] == Magic0 84 && t->magic[1] == Magic1) { 85 close(rawfd); 86 } else { 87 if(rawfd >= 0) 88 close(rawfd); 89 if(seek(disk->fd, 0, 0) < 0 90 || readn(disk->fd, buf, 512) != 512 91 || t->magic[0] != Magic0 92 || t->magic[1] != Magic1) { 93 return -1; 94 } 95 } 96 97 h = s = -1; 98 for(i=0; i<NTentry; i++) { 99 if(t->entry[i].type == 0) 100 continue; 101 102 t->entry[i].ends &= 63; 103 if(h == -1) { 104 h = t->entry[i].endh; 105 s = t->entry[i].ends; 106 } else { 107 /* 108 * Only accept the partition info if every 109 * partition is consistent. 110 */ 111 if(h != t->entry[i].endh || s != t->entry[i].ends) 112 return -1; 113 } 114 } 115 116 if(h == -1) 117 return -1; 118 119 disk->h = h+1; /* heads count from 0 */ 120 disk->s = s; /* sectors count from 1 */ 121 disk->c = disk->secs / (disk->h*disk->s); 122 disk->chssrc = Gpart; 123 return 0; 124 } 125 126 /* 127 * If there is ATA geometry, use it, perhaps massaged. 128 */ 129 static int 130 drivergeometry(Disk *disk) 131 { 132 int m; 133 134 if(disk->c == 0 || disk->h == 0 || disk->s == 0) 135 return -1; 136 137 disk->chssrc = Gdisk; 138 if(disk->c < 1024) 139 return 0; 140 141 switch(disk->h) { 142 case 15: 143 disk->h = 255; 144 disk->c /= 17; 145 return 0; 146 147 default: 148 for(m = 2; m*disk->h < 256; m *= 2) { 149 if(disk->c/m < 1024) { 150 disk->c /= m; 151 disk->h *= m; 152 return 0; 153 } 154 } 155 156 /* set to 255, 63 and be done with it */ 157 disk->h = 255; 158 disk->s = 63; 159 disk->c = disk->secs / (disk->h * disk->s); 160 return 0; 161 } 162 return -1; /* not reached */ 163 } 164 165 /* 166 * There's no ATA geometry and no partitions. 167 * Our guess is as good as anyone's. 168 */ 169 static struct { 170 int h; 171 int s; 172 } guess[] = { 173 64, 32, 174 64, 63, 175 128, 63, 176 255, 63, 177 }; 178 static int 179 guessgeometry(Disk *disk) 180 { 181 int i; 182 long c; 183 184 disk->chssrc = Gguess; 185 c = 1024; 186 for(i=0; i<nelem(guess); i++) 187 if(c*guess[i].h*guess[i].s >= disk->secs) { 188 disk->h = guess[i].h; 189 disk->s = guess[i].s; 190 disk->c = disk->secs / (disk->h * disk->s); 191 return 0; 192 } 193 194 /* use maximum values */ 195 disk->h = 255; 196 disk->s = 63; 197 disk->c = disk->secs / (disk->h * disk->s); 198 return 0; 199 } 200 201 static void 202 findgeometry(Disk *disk) 203 { 204 if(partitiongeometry(disk) < 0 205 && drivergeometry(disk) < 0 206 && guessgeometry(disk) < 0) { /* can't happen */ 207 print("we're completely confused about your disk; sorry\n"); 208 assert(0); 209 } 210 } 211 212 static Disk* 213 openfile(Disk *disk) 214 { 215 Dir *d; 216 217 if((d = dirfstat(disk->fd)) == nil){ 218 free(disk); 219 return nil; 220 } 221 222 disk->secsize = 512; 223 disk->size = d->length; 224 disk->secs = disk->size / disk->secsize; 225 disk->offset = 0; 226 free(d); 227 228 findgeometry(disk); 229 return mkwidth(disk); 230 } 231 232 static Disk* 233 opensd(Disk *disk) 234 { 235 Biobuf b; 236 char *p, *f[10]; 237 int nf; 238 239 Binit(&b, disk->ctlfd, OREAD); 240 while(p = Brdline(&b, '\n')) { 241 p[Blinelen(&b)-1] = '\0'; 242 nf = tokenize(p, f, nelem(f)); 243 if(nf >= 3 && strcmp(f[0], "geometry") == 0) { 244 disk->secsize = strtoll(f[2], 0, 0); 245 if(nf >= 6) { 246 disk->c = strtol(f[3], 0, 0); 247 disk->h = strtol(f[4], 0, 0); 248 disk->s = strtol(f[5], 0, 0); 249 } 250 } 251 if(nf >= 4 && strcmp(f[0], "part") == 0 && strcmp(f[1], disk->part) == 0) { 252 disk->offset = strtoll(f[2], 0, 0); 253 disk->secs = strtoll(f[3], 0, 0) - disk->offset; 254 } 255 } 256 257 258 disk->size = disk->secs * disk->secsize; 259 if(disk->size <= 0) { 260 strcpy(disk->part, ""); 261 disk->type = Tfile; 262 return openfile(disk); 263 } 264 265 findgeometry(disk); 266 return mkwidth(disk); 267 } 268 269 Disk* 270 opendisk(char *disk, int rdonly, int noctl) 271 { 272 char *p, *q; 273 Disk *d; 274 275 d = malloc(sizeof(*d)); 276 if(d == nil) 277 return nil; 278 279 d->fd = d->wfd = d->ctlfd = -1; 280 d->rdonly = rdonly; 281 282 d->fd = open(disk, OREAD); 283 if(d->fd < 0) { 284 werrstr("cannot open disk file"); 285 free(d); 286 return nil; 287 } 288 289 if(rdonly == 0) { 290 d->wfd = open(disk, OWRITE); 291 if(d->wfd < 0) 292 d->rdonly = 1; 293 } 294 295 if(noctl) 296 return openfile(d); 297 298 p = malloc(strlen(disk) + 4); /* 4: slop for "ctl\0" */ 299 if(p == nil) { 300 close(d->wfd); 301 close(d->fd); 302 free(d); 303 return nil; 304 } 305 strcpy(p, disk); 306 307 /* check for floppy(3) disk */ 308 if(strlen(p) >= 7) { 309 q = p+strlen(p)-7; 310 if(q[0] == 'f' && q[1] == 'd' && isdigit(q[2]) && strcmp(q+3, "disk") == 0) { 311 strcpy(q+3, "ctl"); 312 if((d->ctlfd = open(p, ORDWR)) >= 0) { 313 *q = '\0'; 314 d->prefix = p; 315 d->type = Tfloppy; 316 return openfile(d); 317 } 318 } 319 } 320 321 /* attempt to find sd(3) disk or partition */ 322 if(q = strrchr(p, '/')) 323 q++; 324 else 325 q = p; 326 327 strcpy(q, "ctl"); 328 if((d->ctlfd = open(p, ORDWR)) >= 0) { 329 *q = '\0'; 330 d->prefix = p; 331 d->type = Tsd; 332 d->part = strdup(disk+(q-p)); 333 if(d->part == nil){ 334 close(d->ctlfd); 335 close(d->wfd); 336 close(d->fd); 337 free(p); 338 free(d); 339 return nil; 340 } 341 return opensd(d); 342 } 343 344 *q = '\0'; 345 d->prefix = p; 346 /* assume we just have a normal file */ 347 d->type = Tfile; 348 return openfile(d); 349 } 350 351