1implement Disks; 2 3# adapted from /sys/src/libdisk on Plan 9: subject to Lucent Public License 1.02 4 5include "sys.m"; 6 sys: Sys; 7 8include "bufio.m"; 9 bufio: Bufio; 10 Iobuf: import bufio; 11 12include "disks.m"; 13 14scsiverbose := 0; 15 16Codefile: con "/lib/scsicodes"; 17 18Code: adt { 19 v: int; # (asc<<8) | ascq 20 s: string; 21}; 22codes: array of Code; 23 24init() 25{ 26 sys = load Sys Sys->PATH; 27 bufio = load Bufio Bufio->PATH; 28} 29 30# 31# Discover the disk geometry by various sleazeful means. 32# 33# First, if there is a partition table in sector 0, 34# see if all the partitions have the same end head 35# and sector; if so, we'll assume that that's the 36# right count. 37# 38# If that fails, we'll try looking at the geometry that the ATA 39# driver supplied, if any, and translate that as a 40# BIOS might. 41# 42# If that too fails, which should only happen on a SCSI 43# disk with no currently defined partitions, we'll try 44# various common (h, s) pairs used by BIOSes when faking 45# the geometries. 46# 47 48# table entry: 49 Oactive, # active flag 50 Ostarth, # starting head 51 Ostarts, # starting sector 52 Ostartc, # starting cylinder 53 Otype, # partition type 54 Oendh, # ending head 55 Oends, # ending sector 56 Oendc: con iota; # ending cylinder 57 Oxlba: con Oendc+1; # starting LBA from start of disc or partition [4] 58 Oxsize: con Oxlba+4; # size in sectors[4] 59 60# Table: entry[NTentry][TentrySize] magic[2] 61Omagic: con NTentry*TentrySize; 62 63partitiongeometry(d: ref Disk): int 64{ 65 buf := array[512] of byte; 66 t := 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 rawfd := sys->open(d.prefix+"data", Sys->OREAD); 73 if(rawfd != nil 74 && sys->seek(rawfd, big 0, 0) == big 0 75 && sys->readn(rawfd, buf, 512) == 512 76 && int t[Omagic] == Magic0 77 && int t[Omagic+1] == Magic1) { 78 rawfd = nil; 79 }else{ 80 rawfd = nil; 81 if(sys->seek(d.fd, big 0, 0) < big 0 82 || sys->readn(d.fd, buf, 512) != 512 83 || int t[Omagic] != Magic0 84 || int t[Omagic+1] != Magic1) 85 return -1; 86 } 87 88 h := s := -1; 89 for(i:=0; i<NTentry*TentrySize; i += TentrySize) { 90 if(t[i+Otype] == byte 0) 91 continue; 92 93 t[i+Oends] &= byte 63; 94 if(h == -1) { 95 h = int t[i+Oendh]; 96 s = int t[i+Oends]; 97 } else { 98 # 99 # Only accept the partition info if every 100 # partition is consistent. 101 # 102 if(h != int t[i+Oendh] || s != int t[i+Oends]) 103 return -1; 104 } 105 } 106 107 if(h == -1) 108 return -1; 109 110 d.h = h+1; # heads count from 0 111 d.s = s; # sectors count from 1 112 d.c = int (d.secs / big (d.h*d.s)); 113 d.chssrc = "part"; 114 return 0; 115} 116 117# 118# If there is ATA geometry, use it, perhaps massaged 119# 120drivergeometry(d: ref Disk): int 121{ 122 if(d.c == 0 || d.h == 0 || d.s == 0) 123 return -1; 124 125 d.chssrc = "disk"; 126 if(d.c < 1024) 127 return 0; 128 129 case d.h { 130 15 => 131 d.h = 255; 132 d.c /= 17; 133 134 * => 135 for(m := 2; m*d.h < 256; m *= 2) { 136 if(d.c/m < 1024) { 137 d.c /= m; 138 d.h *= m; 139 return 0; 140 } 141 } 142 143 # set to 255, 63 and be done with it 144 d.h = 255; 145 d.s = 63; 146 d.c = int (d.secs / big(d.h * d.s)); 147 } 148 return 0; 149} 150 151# 152# There's no ATA geometry and no partitions. 153# Our guess is as good as anyone's. 154# 155Guess: adt { 156 h: int; 157 s: int; 158}; 159guess: array of Guess = array[] of { 160 (64, 32), 161 (64, 63), 162 (128, 63), 163 (255, 63), 164}; 165 166guessgeometry(d: ref Disk) 167{ 168 d.chssrc = "guess"; 169 c := 1024; 170 for(i:=0; i<len guess; i++) 171 if(big(c*guess[i].h*guess[i].s) >= d.secs) { 172 d.h = guess[i].h; 173 d.s = guess[i].s; 174 d.c = int(d.secs / big(d.h * d.s)); 175 return; 176 } 177 178 # use maximum values 179 d.h = 255; 180 d.s = 63; 181 d.c = int(d.secs / big(d.h * d.s)); 182} 183 184findgeometry(disk: ref Disk) 185{ 186 disk.h = disk.s = disk.c = 0; 187 if(partitiongeometry(disk) < 0 && 188 drivergeometry(disk) < 0) 189 guessgeometry(disk); 190} 191 192openfile(d: ref Disk): ref Disk 193{ 194 (ok, db) := sys->fstat(d.fd); 195 if(ok < 0) 196 return nil; 197 198 d.secsize = 512; 199 d.size = db.length; 200 d.secs = d.size / big d.secsize; 201 d.offset = big 0; 202 203 findgeometry(d); 204 return mkwidth(d); 205} 206 207opensd(d: ref Disk): ref Disk 208{ 209 b := bufio->fopen(d.ctlfd, Bufio->OREAD); 210 while((p := b.gets('\n')) != nil){ 211 p = p[0:len p - 1]; 212 (nf, f) := sys->tokenize(p, " \t"); # might need str->unquote 213 if(nf >= 3 && hd f == "geometry") { 214 d.secsize = int hd tl tl f; 215 if(nf >= 6) { 216 d.c = int hd tl tl tl f; 217 d.h = int hd tl tl tl tl f; 218 d.s = int hd tl tl tl tl tl f; 219 } 220 } 221 if(nf >= 4 && hd f == "part" && hd tl f == d.part) { 222 d.offset = big hd tl tl f; 223 d.secs = big hd tl tl tl f - d.offset; 224 } 225 } 226 227 228 d.size = d.secs * big d.secsize; 229 if(d.size <= big 0) { 230 d.part = ""; 231 d.dtype = "file"; 232 return openfile(d); 233 } 234 235 findgeometry(d); 236 return mkwidth(d); 237} 238 239Disk.open(name: string, mode: int, noctl: int): ref Disk 240{ 241 d := ref Disk; 242 d.rdonly = mode == Sys->OREAD; 243 d.fd = sys->open(name, Sys->OREAD); 244 if(d.fd == nil) 245 return nil; 246 247 if(mode != Sys->OREAD){ 248 d.wfd = sys->open(name, Sys->OWRITE); 249 if(d.wfd == nil) 250 d.rdonly = 1; 251 } 252 253 if(noctl) 254 return openfile(d); 255 256 # check for floppy(3) disk 257 if(len name >= 7) { 258 q := name[len name-7:]; 259 if(q[0] == 'f' && q[1] == 'd' && isdigit(q[2]) && q[3:] == "disk") { 260 if((d.ctlfd = sys->open(name[0:len name-4]+"ctl", Sys->ORDWR)) != nil) { 261 d.prefix = name[0:len name-4]; # fdN (unlike Plan 9) 262 d.dtype = "floppy"; 263 return openfile(d); 264 } 265 } 266 } 267 268 # attempt to find sd(3) disk or partition 269 d.prefix = name; 270 for(i := len name; --i >= 0;) 271 if(name[i] == '/'){ 272 d.prefix = name[0:i+1]; 273 break; 274 } 275 276 if((d.ctlfd = sys->open(d.prefix+"ctl", Sys->ORDWR)) != nil) { 277 d.dtype = "sd"; 278 d.part = name[len d.prefix:]; 279 return opensd(d); 280 } 281 282 # assume we have just a normal file 283 d.dtype = "file"; 284 return openfile(d); 285} 286 287mkwidth(d: ref Disk): ref Disk 288{ 289 d.width = len sys->sprint("%bd", d.size); 290 return d; 291} 292 293isdigit(c: int): int 294{ 295 return c >= '0' && c <= '9'; 296} 297 298putchs(d: ref Disk, p: array of byte, lba: big) 299{ 300 s := int (lba % big d.s); 301 h := int (lba / big d.s % big d.h); 302 c := int (lba / (big d.s * big d.h)); 303 304 if(c >= 1024) { 305 c = 1023; 306 h = d.h - 1; 307 s = d.s - 1; 308 } 309 310 p[0] = byte h; 311 p[1] = byte (((s+1) & 16r3F) | ((c>>2) & 16rC0)); 312 p[2] = byte c; 313} 314 315PCpart.bytes(p: self PCpart, d: ref Disk): array of byte 316{ 317 a := array[TentrySize] of byte; 318 a[Oactive] = byte p.active; 319 a[Otype] = byte p.ptype; 320 putchs(d, a[Ostarth:], p.base+p.offset); 321 putchs(d, a[Oendh:], p.base+p.offset+p.size-big 1); 322 putle32(a[Oxlba:], p.offset); 323 putle32(a[Oxsize:], p.size); 324 return a; 325} 326 327PCpart.extract(a: array of byte, nil: ref Disk): PCpart 328{ 329 p: PCpart; 330 p.active = int a[Oactive]; 331 p.ptype = int a[Otype]; 332 p.base = big 0; 333 p.offset = getle32(a[Oxlba:]); 334 p.size = getle32(a[Oxsize:]); 335 return p; 336} 337 338getle32(p: array of byte): big 339{ 340 return (big p[3]<<24) | (big p[2]<<16) | (big p[1] << 8) | big p[0]; 341} 342 343putle32(p: array of byte, i: big) 344{ 345 p[0] = byte i; 346 p[1] = byte (i>>8); 347 p[2] = byte (i>>16); 348 p[3] = byte (i>>24); 349} 350 351Disk.readn(d: self ref Disk, buf: array of byte, nb: int): int 352{ 353 return sys->readn(d.fd, buf, nb); 354} 355 356chstext(p: array of byte): string 357{ 358 h := int p[0]; 359 c := int p[2]; 360 c |= (int p[1]&16rC0)<<2; 361 s := (int p[1] & 16r3F); 362 return sys->sprint("%d/%d/%d", c, h, s); 363} 364