tpart.c - plan9port - [fork] Plan 9 from user space | |
git clone git://src.adamsgaard.dk/plan9port | |
Log | |
Files | |
Refs | |
README | |
LICENSE | |
--- | |
tpart.c (12618B) | |
--- | |
1 #ifdef PLAN9PORT /* SORRY! */ | |
2 # include <u.h> | |
3 # include <sys/types.h> | |
4 # ifdef __linux__ /* REALLY SORRY! */ | |
5 # define CANBLOCKSIZE 1 | |
6 # include <sys/vfs.h> | |
7 # elif defined(__FreeBSD__) | |
8 # define CANBLOCKSIZE 1 | |
9 # include <sys/param.h> | |
10 # include <sys/stat.h> | |
11 # include <sys/mount.h> | |
12 # endif | |
13 #endif | |
14 #include "stdinc.h" | |
15 #include <ctype.h> | |
16 #include "dat.h" | |
17 #include "fns.h" | |
18 | |
19 u32int maxblocksize; | |
20 int readonly; | |
21 | |
22 int findsubpart(Part *part, char *name); | |
23 | |
24 static int | |
25 strtoullsuf(char *p, char **pp, int rad, u64int *u) | |
26 { | |
27 u64int v; | |
28 | |
29 if(!isdigit((uchar)*p)) | |
30 return -1; | |
31 v = strtoull(p, &p, rad); | |
32 switch(*p){ | |
33 case 'k': | |
34 case 'K': | |
35 v *= 1024; | |
36 p++; | |
37 break; | |
38 case 'm': | |
39 case 'M': | |
40 v *= 1024*1024; | |
41 p++; | |
42 break; | |
43 case 'g': | |
44 case 'G': | |
45 v *= 1024*1024*1024; | |
46 p++; | |
47 break; | |
48 case 't': | |
49 case 'T': | |
50 v *= 1024*1024; | |
51 v *= 1024*1024; | |
52 p++; | |
53 break; | |
54 } | |
55 *pp = p; | |
56 *u = v; | |
57 return 0; | |
58 } | |
59 | |
60 static int | |
61 parsepart(char *name, char **file, char **subpart, u64int *lo, u64int *h… | |
62 { | |
63 char *p; | |
64 | |
65 *file = estrdup(name); | |
66 *lo = 0; | |
67 *hi = 0; | |
68 *subpart = nil; | |
69 if((p = strrchr(*file, ':')) == nil) | |
70 return 0; | |
71 *p++ = 0; | |
72 if(isalpha(*p)){ | |
73 *subpart = p; | |
74 return 0; | |
75 } | |
76 if(*p == '-') | |
77 *lo = 0; | |
78 else{ | |
79 if(strtoullsuf(p, &p, 0, lo) < 0){ | |
80 free(*file); | |
81 return -1; | |
82 } | |
83 } | |
84 if(*p == '-') | |
85 p++; | |
86 if(*p == 0){ | |
87 *hi = 0; | |
88 return 0; | |
89 } | |
90 if(strtoullsuf(p, &p, 0, hi) < 0 || *p != 0){ | |
91 free(*file); | |
92 return -1; | |
93 } | |
94 return 0; | |
95 } | |
96 | |
97 #undef min | |
98 #define min(a, b) ((a) < (b) ? (a) : (b)) | |
99 Part* | |
100 initpart(char *name, int mode) | |
101 { | |
102 Part *part; | |
103 Dir *dir; | |
104 char *file, *subname; | |
105 u64int lo, hi; | |
106 | |
107 if(parsepart(name, &file, &subname, &lo, &hi) < 0){ | |
108 werrstr("cannot parse name %s", name); | |
109 return nil; | |
110 } | |
111 trace(TraceDisk, "initpart %s file %s lo 0x%llx hi 0x%llx", name… | |
112 part = MKZ(Part); | |
113 part->name = estrdup(name); | |
114 part->filename = estrdup(file); | |
115 if(readonly){ | |
116 mode &= ~(OREAD|OWRITE|ORDWR); | |
117 mode |= OREAD; | |
118 } | |
119 #ifdef __linux__ /* sorry, but linus made O_DIRECT unusable! */ | |
120 mode &= ~ODIRECT; | |
121 #endif | |
122 part->fd = open(file, mode); | |
123 if(part->fd < 0){ | |
124 if((mode&(OREAD|OWRITE|ORDWR)) == ORDWR) | |
125 part->fd = open(file, (mode&~ORDWR)|OREAD); | |
126 if(part->fd < 0){ | |
127 freepart(part); | |
128 fprint(2, "can't open partition='%s': %r\n", fil… | |
129 seterr(EOk, "can't open partition='%s': %r", fil… | |
130 fprint(2, "%r\n"); | |
131 free(file); | |
132 return nil; | |
133 } | |
134 fprint(2, "warning: %s opened for reading only\n", name); | |
135 } | |
136 part->offset = lo; | |
137 dir = dirfstat(part->fd); | |
138 if(dir == nil){ | |
139 freepart(part); | |
140 seterr(EOk, "can't stat partition='%s': %r", file); | |
141 free(file); | |
142 return nil; | |
143 } | |
144 if(dir->length == 0){ | |
145 free(dir); | |
146 dir = dirstat(file); | |
147 if(dir == nil || dir->length == 0) { | |
148 freepart(part); | |
149 seterr(EOk, "can't determine size of partition %… | |
150 free(file); | |
151 return nil; | |
152 } | |
153 } | |
154 if(dir->length < hi || dir->length < lo){ | |
155 freepart(part); | |
156 seterr(EOk, "partition '%s': bounds out of range (max %l… | |
157 free(dir); | |
158 free(file); | |
159 return nil; | |
160 } | |
161 if(hi == 0) | |
162 hi = dir->length; | |
163 part->size = hi - part->offset; | |
164 #ifdef CANBLOCKSIZE | |
165 { | |
166 struct statfs sfs; | |
167 if(fstatfs(part->fd, &sfs) >= 0 && sfs.f_bsize > 512) | |
168 part->fsblocksize = sfs.f_bsize; | |
169 } | |
170 #endif | |
171 | |
172 part->fsblocksize = min(part->fsblocksize, MaxIo); | |
173 | |
174 if(subname && findsubpart(part, subname) < 0){ | |
175 werrstr("cannot find subpartition %s", subname); | |
176 freepart(part); | |
177 return nil; | |
178 } | |
179 free(dir); | |
180 return part; | |
181 } | |
182 | |
183 int | |
184 flushpart(Part *part) | |
185 { | |
186 USED(part); | |
187 #ifdef __linux__ /* grrr! */ | |
188 if(fsync(part->fd) < 0){ | |
189 logerr(EAdmin, "flushpart %s: %r", part->name); | |
190 return -1; | |
191 } | |
192 posix_fadvise(part->fd, 0, 0, POSIX_FADV_DONTNEED); | |
193 #endif | |
194 return 0; | |
195 } | |
196 | |
197 void | |
198 freepart(Part *part) | |
199 { | |
200 if(part == nil) | |
201 return; | |
202 if(part->fd >= 0) | |
203 close(part->fd); | |
204 free(part->name); | |
205 free(part); | |
206 } | |
207 | |
208 void | |
209 partblocksize(Part *part, u32int blocksize) | |
210 { | |
211 if(part->blocksize) | |
212 sysfatal("resetting partition=%s's block size", part->na… | |
213 part->blocksize = blocksize; | |
214 if(blocksize > maxblocksize) | |
215 maxblocksize = blocksize; | |
216 } | |
217 | |
218 /* | |
219 * Read/write some amount of data between a block device or file and a m… | |
220 * | |
221 * Most Unix systems require that when accessing a block device directly, | |
222 * the buffer, offset, and count are all multiples of the device block s… | |
223 * making this a lot more complicated than it otherwise would be. | |
224 * | |
225 * Most of our callers will make things easy on us, but for some callers… | |
226 * if we just do the work here, with only one place to get it right (hop… | |
227 * | |
228 * If everything is aligned properly, prwb will try to do big transfers … | |
229 * body of the loop: up to MaxIo bytes at a time. If everything isn't a… | |
230 * we work one block at a time. | |
231 */ | |
232 int | |
233 prwb(char *name, int fd, int isread, u64int offset, void *vbuf, u32int c… | |
234 { | |
235 char *op; | |
236 u8int *buf, *freetmp, *dst; | |
237 u32int icount, opsize; | |
238 int r, count1; | |
239 | |
240 | |
241 #ifndef PLAN9PORT | |
242 USED(blocksize); | |
243 icount = count; | |
244 buf = vbuf; | |
245 op = isread ? "read" : "write"; | |
246 dst = buf; | |
247 freetmp = nil; | |
248 while(count > 0){ | |
249 opsize = min(count, 131072 /* blocksize */); | |
250 if(isread) | |
251 r = pread(fd, dst, opsize, offset); | |
252 else | |
253 r = pwrite(fd, dst, opsize, offset); | |
254 if(r <= 0) | |
255 goto Error; | |
256 offset += r; | |
257 count -= r; | |
258 dst += r; | |
259 if(r != opsize) | |
260 goto Error; | |
261 } | |
262 return icount; | |
263 #else | |
264 u32int c, delta; | |
265 u8int *tmp; | |
266 | |
267 icount = count; | |
268 buf = vbuf; | |
269 tmp = nil; | |
270 freetmp = nil; | |
271 opsize = blocksize; | |
272 | |
273 if(count == 0){ | |
274 logerr(EStrange, "pwrb %s called to %s 0 bytes", name, i… | |
275 return 0; | |
276 } | |
277 | |
278 assert(blocksize > 0); | |
279 | |
280 /* allocate blocksize-aligned temp buffer if needed */ | |
281 if((ulong)offset%blocksize || (ulong)buf%blocksize || count%bloc… | |
282 if((freetmp = malloc(blocksize*2)) == nil) | |
283 return -1; | |
284 tmp = freetmp; | |
285 tmp += blocksize - (ulong)tmp%blocksize; | |
286 } | |
287 | |
288 /* handle beginning fringe */ | |
289 if((delta = (ulong)offset%blocksize) != 0){ | |
290 assert(tmp != nil); | |
291 if((r=pread(fd, tmp, blocksize, offset-delta)) != blocks… | |
292 dst = tmp; | |
293 offset = offset-delta; | |
294 op = "read"; | |
295 count1 = blocksize; | |
296 goto Error; | |
297 } | |
298 c = min(count, blocksize-delta); | |
299 assert(c > 0 && c < blocksize); | |
300 if(isread) | |
301 memmove(buf, tmp+delta, c); | |
302 else{ | |
303 memmove(tmp+delta, buf, c); | |
304 if((r=pwrite(fd, tmp, blocksize, offset-delta)) … | |
305 dst = tmp; | |
306 offset = offset-delta; | |
307 op = "read"; | |
308 count1 = blocksize; | |
309 goto Error; | |
310 } | |
311 } | |
312 assert(c > 0); | |
313 offset += c; | |
314 buf += c; | |
315 count -= c; | |
316 } | |
317 | |
318 /* handle full blocks */ | |
319 while(count >= blocksize){ | |
320 assert((ulong)offset%blocksize == 0); | |
321 if((ulong)buf%blocksize){ | |
322 assert(tmp != nil); | |
323 dst = tmp; | |
324 opsize = blocksize; | |
325 }else{ | |
326 dst = buf; | |
327 opsize = count - count%blocksize; | |
328 if(opsize > MaxIo) | |
329 opsize = MaxIo; | |
330 } | |
331 if(isread){ | |
332 if((r=pread(fd, dst, opsize, offset))<=0 || r%bl… | |
333 op = "read"; | |
334 count1 = opsize; | |
335 goto Error; | |
336 } | |
337 if(dst == tmp){ | |
338 assert(r == blocksize); | |
339 memmove(buf, tmp, blocksize); | |
340 } | |
341 }else{ | |
342 if(dst == tmp){ | |
343 assert(opsize == blocksize); | |
344 memmove(dst, buf, blocksize); | |
345 } | |
346 if((r=pwrite(fd, dst, opsize, offset))<=0 || r%b… | |
347 count1 = opsize; | |
348 op = "write"; | |
349 goto Error; | |
350 } | |
351 if(dst == tmp) | |
352 assert(r == blocksize); | |
353 } | |
354 assert(r > 0); | |
355 offset += r; | |
356 buf += r; | |
357 count -= r; | |
358 } | |
359 | |
360 /* handle ending fringe */ | |
361 if(count > 0){ | |
362 assert((ulong)offset%blocksize == 0); | |
363 assert(tmp != nil); | |
364 /* | |
365 * Complicated condition: if we're reading it's okay to … | |
366 * a block as long as it's enough to satisfy the read - … | |
367 * a normal file. (We never write to normal files, or e… | |
368 * be even more complicated.) | |
369 */ | |
370 r = pread(fd, tmp, blocksize, offset); | |
371 if((isread && r < count) || (!isread && r != blocksize)){ | |
372 print("FAILED isread=%d r=%d count=%d blocksize=%d\n", isread, r, count,… | |
373 dst = tmp; | |
374 op = "read"; | |
375 count1 = blocksize; | |
376 goto Error; | |
377 } | |
378 if(isread) | |
379 memmove(buf, tmp, count); | |
380 else{ | |
381 memmove(tmp, buf, count); | |
382 if(pwrite(fd, tmp, blocksize, offset) != blocksi… | |
383 dst = tmp; | |
384 count1 = blocksize; | |
385 op = "write"; | |
386 goto Error; | |
387 } | |
388 } | |
389 } | |
390 if(freetmp) | |
391 free(freetmp); | |
392 return icount; | |
393 #endif | |
394 | |
395 Error: | |
396 seterr(EAdmin, "%s %s offset 0x%llux count %ud buf %p returned %… | |
397 op, name, offset, count1, dst, r); | |
398 if(freetmp) | |
399 free(freetmp); | |
400 return -1; | |
401 } | |
402 | |
403 #ifndef PLAN9PORT | |
404 static int sdreset(Part*); | |
405 static int reopen(Part*); | |
406 static int threadspawnl(int[3], char*, char*, ...); | |
407 #endif | |
408 | |
409 int | |
410 rwpart(Part *part, int isread, u64int offset, u8int *buf, u32int count) | |
411 { | |
412 int n, try; | |
413 u32int blocksize; | |
414 | |
415 trace(TraceDisk, "%s %s %ud at 0x%llx", | |
416 isread ? "read" : "write", part->name, count, offset); | |
417 if(offset >= part->size || offset+count > part->size){ | |
418 seterr(EStrange, "out of bounds %s offset 0x%llux count … | |
419 isread ? "read" : "write", offset, count, part->… | |
420 return -1; | |
421 } | |
422 | |
423 blocksize = part->fsblocksize; | |
424 if(blocksize == 0) | |
425 blocksize = part->blocksize; | |
426 if(blocksize == 0) | |
427 blocksize = 4096; | |
428 | |
429 for(try=0;; try++){ | |
430 n = prwb(part->filename, part->fd, isread, part->offset+… | |
431 if(n >= 0 || try > 10) | |
432 break; | |
433 | |
434 #ifndef PLAN9PORT | |
435 { | |
436 char err[ERRMAX]; | |
437 /* | |
438 * This happens with the sdmv disks frustratingly often. | |
439 * Try to fix things up and continue. | |
440 */ | |
441 rerrstr(err, sizeof err); | |
442 if(strstr(err, "i/o timeout") || strstr(err, "i/o error"… | |
443 reopen(part); | |
444 continue; | |
445 } | |
446 } | |
447 #endif | |
448 break; | |
449 } | |
450 #ifdef __linux__ /* sigh */ | |
451 posix_fadvise(part->fd, part->offset+offset, n, POSIX_FADV_DONTN… | |
452 #endif | |
453 return n; | |
454 } | |
455 int | |
456 readpart(Part *part, u64int offset, u8int *buf, u32int count) | |
457 { | |
458 return rwpart(part, 1, offset, buf, count); | |
459 } | |
460 | |
461 int | |
462 writepart(Part *part, u64int offset, u8int *buf, u32int count) | |
463 { | |
464 return rwpart(part, 0, offset, buf, count); | |
465 } | |
466 | |
467 ZBlock* | |
468 readfile(char *name) | |
469 { | |
470 Part *p; | |
471 ZBlock *b; | |
472 | |
473 p = initpart(name, OREAD); | |
474 if(p == nil) | |
475 return nil; | |
476 b = alloczblock(p->size, 0, p->blocksize); | |
477 if(b == nil){ | |
478 seterr(EOk, "can't alloc %s: %r", name); | |
479 freepart(p); | |
480 return nil; | |
481 } | |
482 if(readpart(p, 0, b->data, p->size) < 0){ | |
483 seterr(EOk, "can't read %s: %r", name); | |
484 freepart(p); | |
485 freezblock(b); | |
486 return nil; | |
487 } | |
488 freepart(p); | |
489 return b; | |
490 } | |
491 | |
492 /* | |
493 * Search for the Plan 9 partition with the given name. | |
494 * This lets you write things like /dev/ad4:arenas | |
495 * if you move a disk from a Plan 9 system to a FreeBSD system. | |
496 * | |
497 * God I hope I never write this code again. | |
498 */ | |
499 #define MAGIC "plan9 partitions" | |
500 static int | |
501 tryplan9part(Part *part, char *name) | |
502 { | |
503 uchar buf[512]; | |
504 char *line[40], *f[4]; | |
505 int i, n; | |
506 vlong start, end; | |
507 | |
508 /* | |
509 * Partition table in second sector. | |
510 * Could also look on 2nd last sector and last sector, | |
511 * but those disks died out long before venti came along. | |
512 */ | |
513 if(readpart(part, 512, buf, 512) != 512) | |
514 return -1; | |
515 | |
516 /* Plan 9 partition table is just text strings */ | |
517 if(strncmp((char*)buf, "part ", 5) != 0) | |
518 return -1; | |
519 | |
520 buf[511] = 0; | |
521 n = getfields((char*)buf, line, 40, 1, "\n"); | |
522 for(i=0; i<n; i++){ | |
523 if(getfields(line[i], f, 4, 1, " ") != 4) | |
524 break; | |
525 if(strcmp(f[0], "part") != 0) | |
526 break; | |
527 if(strcmp(f[1], name) == 0){ | |
528 start = 512*strtoll(f[2], 0, 0); | |
529 end = 512*strtoll(f[3], 0, 0); | |
530 if(start < end && end <= part->size){ | |
531 part->offset += start; | |
532 part->size = end - start; | |
533 return 0; | |
534 } | |
535 return -1; | |
536 } | |
537 } | |
538 return -1; | |
539 } | |
540 | |
541 #define GSHORT(p) (((p)[1]<<8)|(p)[0]) | |
542 #define GLONG(p) ((GSHORT(p+2)<<16)|GSHORT(p)) | |
543 | |
544 typedef struct Dospart Dospart; | |
545 struct Dospart | |
546 { | |
547 uchar flag; /* active flag */ | |
548 uchar shead; /* starting head */ | |
549 uchar scs[2]; /* starting cylinder/sector */ | |
550 uchar type; /* partition type */ | |
551 uchar ehead; /* ending head */ | |
552 uchar ecs[2]; /* ending cylinder/sector */ | |
553 uchar offset[4]; /* starting sector */ | |
554 uchar size[4]; /* length in sectors */ | |
555 }; | |
556 | |
557 | |
558 int | |
559 findsubpart(Part *part, char *name) | |
560 { | |
561 int i; | |
562 uchar buf[512]; | |
563 u64int size; | |
564 Dospart *dp; | |
565 | |
566 /* See if this is a Plan 9 partition. */ | |
567 if(tryplan9part(part, name) >= 0) | |
568 return 0; | |
569 | |
570 /* Otherwise try for an MBR and then narrow to Plan 9 partition.… | |
571 if(readpart(part, 0, buf, 512) != 512) | |
572 return -1; | |
573 if(buf[0x1FE] != 0x55 || buf[0x1FF] != 0xAA) | |
574 return -1; | |
575 dp = (Dospart*)(buf+0x1BE); | |
576 size = part->size; | |
577 for(i=0; i<4; i++){ | |
578 if(dp[i].type == '9'){ | |
579 part->offset = 512LL*GLONG(dp[i].offset); | |
580 part->size = 512LL*GLONG(dp[i].size); | |
581 if(tryplan9part(part, name) >= 0) | |
582 return 0; | |
583 part->offset = 0; | |
584 part->size = size; | |
585 } | |
586 /* Not implementing extended partitions - enough is enou… | |
587 } | |
588 return -1; | |
589 } |