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