URI: 
       tpart.c - plan9port - [fork] Plan 9 from user space
  HTML git clone git://src.adamsgaard.dk/plan9port
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR 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 *hi)
           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, 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;
          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", file);
          129                         seterr(EOk, "can't open partition='%s': %r", file);
          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 %s", file);
          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 %lld)", name, dir->length);
          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->name);
          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 memory buffer.
          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 size,
          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 it's best
          226  * if we just do the work here, with only one place to get it right (hopefully).
          227  *
          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)
          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, isread ? "read" : "write");
          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%blocksize){
          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)) != blocksize){
          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)) != blocksize){
          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%blocksize){
          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%blocksize){
          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 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;
          377                 }
          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;
          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 %d: %r",
          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 %ud to partition %s size 0x%llux",
          419                         isread ? "read" : "write", offset, count, part->name, part->size);
          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+offset, buf, count, blocksize);
          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") || strstr(err, "partition has changed")){
          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_DONTNEED);
          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 enough. */
          587         }
          588         return -1;
          589 }