xref: /plan9/sys/src/libdisk/disk.c (revision 8673b4da77cad4432af52cdd8eb1236ad54ef11b)
1 #include <u.h>
2 #include <libc.h>
3 #include <bio.h>
4 #include <ctype.h>
5 #include <disk.h>
6 
7 static Disk*
mkwidth(Disk * 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
partitiongeometry(Disk * disk)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
drivergeometry(Disk * disk)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
guessgeometry(Disk * disk)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
findgeometry(Disk * disk)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*
openfile(Disk * 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*
opensd(Disk * 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*
opendisk(char * disk,int rdonly,int noctl)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