Blob


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"
19 u32int maxblocksize;
20 int readonly;
22 int findsubpart(Part *part, char *name);
24 static int
25 strtoullsuf(char *p, char **pp, int rad, u64int *u)
26 {
27 u64int v;
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 }
60 static int
61 parsepart(char *name, char **file, char **subpart, u64int *lo, u64int *hi)
62 {
63 char *p;
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 }
97 #undef min
98 #define min(a, b) ((a) < (b) ? (a) : (b))
99 Part*
100 initpart(char *name, int mode)
102 Part *part;
103 Dir *dir;
104 char *file, *subname;
105 u64int lo, hi;
107 if(parsepart(name, &file, &subname, &lo, &hi) < 0){
108 werrstr("cannot parse name %s", name);
109 return nil;
111 trace(TraceDisk, "initpart %s file %s lo 0x%llx hi 0x%llx", name, file, lo, hi);
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;
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", file);
129 seterr(EOk, "can't open partition='%s': %r", file);
130 fprint(2, "%r\n");
131 free(file);
132 return nil;
134 fprint(2, "warning: %s opened for reading only\n", name);
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;
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 %s", file);
150 free(file);
151 return nil;
154 if(dir->length < hi || dir->length < lo){
155 freepart(part);
156 seterr(EOk, "partition '%s': bounds out of range (max %lld)", name, dir->length);
157 free(dir);
158 free(file);
159 return nil;
161 if(hi == 0)
162 hi = dir->length;
163 part->size = hi - part->offset;
164 #ifdef CANBLOCKSIZE
166 struct statfs sfs;
167 if(fstatfs(part->fd, &sfs) >= 0 && sfs.f_bsize > 512)
168 part->fsblocksize = sfs.f_bsize;
170 #endif
172 part->fsblocksize = min(part->fsblocksize, MaxIo);
174 if(subname && findsubpart(part, subname) < 0){
175 werrstr("cannot find subpartition %s", subname);
176 freepart(part);
177 return nil;
179 free(dir);
180 return part;
183 int
184 flushpart(Part *part)
186 USED(part);
187 #ifdef __linux__ /* grrr! */
188 if(fsync(part->fd) < 0){
189 logerr(EAdmin, "flushpart %s: %r", part->name);
190 return -1;
192 posix_fadvise(part->fd, 0, 0, POSIX_FADV_DONTNEED);
193 #endif
194 return 0;
197 void
198 freepart(Part *part)
200 if(part == nil)
201 return;
202 if(part->fd >= 0)
203 close(part->fd);
204 free(part->name);
205 free(part);
208 void
209 partblocksize(Part *part, u32int blocksize)
211 if(part->blocksize)
212 sysfatal("resetting partition=%s's block size", part->name);
213 part->blocksize = blocksize;
214 if(blocksize > maxblocksize)
215 maxblocksize = blocksize;
218 /*
219 * Read/write some amount of data between a block device or file and a memory buffer.
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 size,
223 * making this a lot more complicated than it otherwise would be.
225 * Most of our callers will make things easy on us, but for some callers it's best
226 * if we just do the work here, with only one place to get it right (hopefully).
228 * If everything is aligned properly, prwb will try to do big transfers in the main
229 * body of the loop: up to MaxIo bytes at a time. If everything isn't aligned properly,
230 * we work one block at a time.
231 */
232 int
233 prwb(char *name, int fd, int isread, u64int offset, void *vbuf, u32int count, u32int blocksize)
235 char *op;
236 u8int *buf, *freetmp, *dst;
237 u32int icount, opsize;
238 int r, count1;
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;
262 return icount;
263 #else
264 u32int c, delta;
265 u8int *tmp;
267 icount = count;
268 buf = vbuf;
269 tmp = nil;
270 freetmp = nil;
271 opsize = blocksize;
273 if(count == 0){
274 logerr(EStrange, "pwrb %s called to %s 0 bytes", name, isread ? "read" : "write");
275 return 0;
278 assert(blocksize > 0);
280 /* allocate blocksize-aligned temp buffer if needed */
281 if((ulong)offset%blocksize || (ulong)buf%blocksize || count%blocksize){
282 if((freetmp = malloc(blocksize*2)) == nil)
283 return -1;
284 tmp = freetmp;
285 tmp += blocksize - (ulong)tmp%blocksize;
288 /* handle beginning fringe */
289 if((delta = (ulong)offset%blocksize) != 0){
290 assert(tmp != nil);
291 if((r=pread(fd, tmp, blocksize, offset-delta)) != blocksize){
292 dst = tmp;
293 offset = offset-delta;
294 op = "read";
295 count1 = blocksize;
296 goto Error;
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)) != blocksize){
305 dst = tmp;
306 offset = offset-delta;
307 op = "read";
308 count1 = blocksize;
309 goto Error;
312 assert(c > 0);
313 offset += c;
314 buf += c;
315 count -= c;
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;
331 if(isread){
332 if((r=pread(fd, dst, opsize, offset))<=0 || r%blocksize){
333 op = "read";
334 count1 = opsize;
335 goto Error;
337 if(dst == tmp){
338 assert(r == blocksize);
339 memmove(buf, tmp, blocksize);
341 }else{
342 if(dst == tmp){
343 assert(opsize == blocksize);
344 memmove(dst, buf, blocksize);
346 if((r=pwrite(fd, dst, opsize, offset))<=0 || r%blocksize){
347 count1 = opsize;
348 op = "write";
349 goto Error;
351 if(dst == tmp)
352 assert(r == blocksize);
354 assert(r > 0);
355 offset += r;
356 buf += r;
357 count -= r;
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 get less than
366 * a block as long as it's enough to satisfy the read - maybe this is
367 * a normal file. (We never write to normal files, or else things would
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, blocksize);
373 dst = tmp;
374 op = "read";
375 count1 = blocksize;
376 goto Error;
378 if(isread)
379 memmove(buf, tmp, count);
380 else{
381 memmove(tmp, buf, count);
382 if(pwrite(fd, tmp, blocksize, offset) != blocksize){
383 dst = tmp;
384 count1 = blocksize;
385 op = "write";
386 goto Error;
390 if(freetmp)
391 free(freetmp);
392 return icount;
393 #endif
395 Error:
396 seterr(EAdmin, "%s %s offset 0x%llux count %ud buf %p returned %d: %r",
397 op, name, offset, count1, dst, r);
398 if(freetmp)
399 free(freetmp);
400 return -1;
403 #ifndef PLAN9PORT
404 static int sdreset(Part*);
405 static int reopen(Part*);
406 static int threadspawnl(int[3], char*, char*, ...);
407 #endif
409 int
410 rwpart(Part *part, int isread, u64int offset, u8int *buf, u32int count)
412 int n, try;
413 u32int blocksize;
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 %ud to partition %s size 0x%llux",
419 isread ? "read" : "write", offset, count, part->name, part->size);
420 return -1;
423 blocksize = part->fsblocksize;
424 if(blocksize == 0)
425 blocksize = part->blocksize;
426 if(blocksize == 0)
427 blocksize = 4096;
429 for(try=0;; try++){
430 n = prwb(part->filename, part->fd, isread, part->offset+offset, buf, count, blocksize);
431 if(n >= 0 || try > 10)
432 break;
434 #ifndef PLAN9PORT
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") || strstr(err, "partition has changed")){
443 reopen(part);
444 continue;
447 #endif
448 break;
450 #ifdef __linux__ /* sigh */
451 posix_fadvise(part->fd, part->offset+offset, n, POSIX_FADV_DONTNEED);
452 #endif
453 return n;
455 int
456 readpart(Part *part, u64int offset, u8int *buf, u32int count)
458 return rwpart(part, 1, offset, buf, count);
461 int
462 writepart(Part *part, u64int offset, u8int *buf, u32int count)
464 return rwpart(part, 0, offset, buf, count);
467 ZBlock*
468 readfile(char *name)
470 Part *p;
471 ZBlock *b;
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;
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;
488 freepart(p);
489 return b;
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.
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)
503 uchar buf[512];
504 char *line[40], *f[4];
505 int i, n;
506 vlong start, end;
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;
516 /* Plan 9 partition table is just text strings */
517 if(strncmp((char*)buf, "part ", 5) != 0)
518 return -1;
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;
535 return -1;
538 return -1;
541 #define GSHORT(p) (((p)[1]<<8)|(p)[0])
542 #define GLONG(p) ((GSHORT(p+2)<<16)|GSHORT(p))
544 typedef struct Dospart Dospart;
545 struct Dospart
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 };
558 int
559 findsubpart(Part *part, char *name)
561 int i;
562 uchar buf[512];
563 u64int size;
564 Dospart *dp;
566 /* See if this is a Plan 9 partition. */
567 if(tryplan9part(part, name) >= 0)
568 return 0;
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;
586 /* Not implementing extended partitions - enough is enough. */
588 return -1;