URI:
       t9fsys.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
       ---
       t9fsys.c (35143B)
       ---
            1 #include "stdinc.h"
            2 #include <bio.h>
            3 #include "dat.h"
            4 #include "fns.h"
            5 #include "9.h"
            6 
            7 struct Fsys {
            8         QLock        lock;
            9 
           10         char*        name;                /* copy here & Fs to ease error reporting */
           11         char*        dev;
           12         char*        venti;
           13 
           14         Fs*        fs;
           15         VtConn* session;
           16         int        ref;
           17 
           18         int        noauth;
           19         int        noperm;
           20         int        wstatallow;
           21 
           22         Fsys*        next;
           23 };
           24 
           25 int mempcnt;                        /* from fossil.c */
           26 
           27 int        fsGetBlockSize(Fs *fs);
           28 
           29 static struct {
           30         RWLock        lock;
           31         Fsys*        head;
           32         Fsys*        tail;
           33 
           34         char*        curfsys;
           35 } sbox;
           36 
           37 static char *_argv0;
           38 #define argv0 _argv0
           39 
           40 static char FsysAll[] = "all";
           41 
           42 static char EFsysBusy[] = "fsys: '%s' busy";
           43 static char EFsysExists[] = "fsys: '%s' already exists";
           44 static char EFsysNoCurrent[] = "fsys: no current fsys";
           45 static char EFsysNotFound[] = "fsys: '%s' not found";
           46 static char EFsysNotOpen[] = "fsys: '%s' not open";
           47 
           48 static char *
           49 ventihost(char *host)
           50 {
           51         if(host != nil)
           52                 return vtstrdup(host);
           53         host = getenv("venti");
           54         if(host == nil)
           55                 host = vtstrdup("$venti");
           56         return host;
           57 }
           58 
           59 static void
           60 prventihost(char *host)
           61 {
           62         char *vh;
           63 
           64         vh = ventihost(host);
           65         fprint(2, "%s: dialing venti at %s\n",
           66                 argv0, netmkaddr(vh, 0, "venti"));
           67         free(vh);
           68 }
           69 
           70 static VtConn *
           71 myDial(char *host)
           72 {
           73         prventihost(host);
           74         return vtdial(host);
           75 }
           76 
           77 static int
           78 myRedial(VtConn *z, char *host)
           79 {
           80         prventihost(host);
           81         return vtredial(z, host);
           82 }
           83 
           84 static Fsys*
           85 _fsysGet(char* name)
           86 {
           87         Fsys *fsys;
           88 
           89         if(name == nil || name[0] == '\0')
           90                 name = "main";
           91 
           92         rlock(&sbox.lock);
           93         for(fsys = sbox.head; fsys != nil; fsys = fsys->next){
           94                 if(strcmp(name, fsys->name) == 0){
           95                         fsys->ref++;
           96                         break;
           97                 }
           98         }
           99         runlock(&sbox.lock);
          100         if(fsys == nil)
          101                 werrstr(EFsysNotFound, name);
          102         return fsys;
          103 }
          104 
          105 static int
          106 cmdPrintConfig(int argc, char* argv[])
          107 {
          108         Fsys *fsys;
          109         char *usage = "usage: printconfig";
          110 
          111         ARGBEGIN{
          112         default:
          113                 return cliError(usage);
          114         }ARGEND
          115 
          116         if(argc)
          117                 return cliError(usage);
          118 
          119         rlock(&sbox.lock);
          120         for(fsys = sbox.head; fsys != nil; fsys = fsys->next){
          121                 consPrint("\tfsys %s config %s\n", fsys->name, fsys->dev);
          122                 if(fsys->venti && fsys->venti[0])
          123                         consPrint("\tfsys %s venti %q\n", fsys->name,
          124                                 fsys->venti);
          125         }
          126         runlock(&sbox.lock);
          127         return 1;
          128 }
          129 
          130 Fsys*
          131 fsysGet(char* name)
          132 {
          133         Fsys *fsys;
          134 
          135         if((fsys = _fsysGet(name)) == nil)
          136                 return nil;
          137 
          138         qlock(&fsys->lock);
          139         if(fsys->fs == nil){
          140                 werrstr(EFsysNotOpen, fsys->name);
          141                 qunlock(&fsys->lock);
          142                 fsysPut(fsys);
          143                 return nil;
          144         }
          145         qunlock(&fsys->lock);
          146 
          147         return fsys;
          148 }
          149 
          150 char*
          151 fsysGetName(Fsys* fsys)
          152 {
          153         return fsys->name;
          154 }
          155 
          156 Fsys*
          157 fsysIncRef(Fsys* fsys)
          158 {
          159         wlock(&sbox.lock);
          160         fsys->ref++;
          161         wunlock(&sbox.lock);
          162 
          163         return fsys;
          164 }
          165 
          166 void
          167 fsysPut(Fsys* fsys)
          168 {
          169         wlock(&sbox.lock);
          170         assert(fsys->ref > 0);
          171         fsys->ref--;
          172         wunlock(&sbox.lock);
          173 }
          174 
          175 Fs*
          176 fsysGetFs(Fsys* fsys)
          177 {
          178         assert(fsys != nil && fsys->fs != nil);
          179 
          180         return fsys->fs;
          181 }
          182 
          183 void
          184 fsysFsRlock(Fsys* fsys)
          185 {
          186         rlock(&fsys->fs->elk);
          187 }
          188 
          189 void
          190 fsysFsRUnlock(Fsys* fsys)
          191 {
          192         runlock(&fsys->fs->elk);
          193 }
          194 
          195 int
          196 fsysNoAuthCheck(Fsys* fsys)
          197 {
          198         return fsys->noauth;
          199 }
          200 
          201 int
          202 fsysNoPermCheck(Fsys* fsys)
          203 {
          204         return fsys->noperm;
          205 }
          206 
          207 int
          208 fsysWstatAllow(Fsys* fsys)
          209 {
          210         return fsys->wstatallow;
          211 }
          212 
          213 static char modechars[] = "YUGalLdHSATs";
          214 static ulong modebits[] = {
          215         ModeSticky,
          216         ModeSetUid,
          217         ModeSetGid,
          218         ModeAppend,
          219         ModeExclusive,
          220         ModeLink,
          221         ModeDir,
          222         ModeHidden,
          223         ModeSystem,
          224         ModeArchive,
          225         ModeTemporary,
          226         ModeSnapshot,
          227         0
          228 };
          229 
          230 char*
          231 fsysModeString(ulong mode, char *buf)
          232 {
          233         int i;
          234         char *p;
          235 
          236         p = buf;
          237         for(i=0; modebits[i]; i++)
          238                 if(mode & modebits[i])
          239                         *p++ = modechars[i];
          240         sprint(p, "%luo", mode&0777);
          241         return buf;
          242 }
          243 
          244 int
          245 fsysParseMode(char* s, ulong* mode)
          246 {
          247         ulong x, y;
          248         char *p;
          249 
          250         x = 0;
          251         for(; *s < '0' || *s > '9'; s++){
          252                 if(*s == 0)
          253                         return 0;
          254                 p = strchr(modechars, *s);
          255                 if(p == nil)
          256                         return 0;
          257                 x |= modebits[p-modechars];
          258         }
          259         y = strtoul(s, &p, 8);
          260         if(*p != '\0' || y > 0777)
          261                 return 0;
          262         *mode = x|y;
          263         return 1;
          264 }
          265 
          266 File*
          267 fsysGetRoot(Fsys* fsys, char* name)
          268 {
          269         File *root, *sub;
          270 
          271         assert(fsys != nil && fsys->fs != nil);
          272 
          273         root = fsGetRoot(fsys->fs);
          274         if(name == nil || strcmp(name, "") == 0)
          275                 return root;
          276 
          277         sub = fileWalk(root, name);
          278         fileDecRef(root);
          279 
          280         return sub;
          281 }
          282 
          283 static Fsys*
          284 fsysAlloc(char* name, char* dev)
          285 {
          286         Fsys *fsys;
          287 
          288         wlock(&sbox.lock);
          289         for(fsys = sbox.head; fsys != nil; fsys = fsys->next){
          290                 if(strcmp(fsys->name, name) != 0)
          291                         continue;
          292                 werrstr(EFsysExists, name);
          293                 wunlock(&sbox.lock);
          294                 return nil;
          295         }
          296 
          297         fsys = vtmallocz(sizeof(Fsys));
          298         fsys->name = vtstrdup(name);
          299         fsys->dev = vtstrdup(dev);
          300 
          301         fsys->ref = 1;
          302 
          303         if(sbox.tail != nil)
          304                 sbox.tail->next = fsys;
          305         else
          306                 sbox.head = fsys;
          307         sbox.tail = fsys;
          308         wunlock(&sbox.lock);
          309 
          310         return fsys;
          311 }
          312 
          313 static int
          314 fsysClose(Fsys* fsys, int argc, char* argv[])
          315 {
          316         char *usage = "usage: [fsys name] close";
          317 
          318         ARGBEGIN{
          319         default:
          320                 return cliError(usage);
          321         }ARGEND
          322         if(argc)
          323                 return cliError(usage);
          324 
          325         return cliError("close isn't working yet; halt %s and then kill fossil",
          326                 fsys->name);
          327 
          328         /*
          329          * Oooh. This could be hard. What if fsys->ref != 1?
          330          * Also, fsClose() either does the job or panics, can we
          331          * gracefully detect it's still busy?
          332          *
          333          * More thought and care needed here.
          334         fsClose(fsys->fs);
          335         fsys->fs = nil;
          336         vtfreeconn(fsys->session);
          337         fsys->session = nil;
          338 
          339         if(sbox.curfsys != nil && strcmp(fsys->name, sbox.curfsys) == 0){
          340                 sbox.curfsys = nil;
          341                 consPrompt(nil);
          342         }
          343 
          344         return 1;
          345          */
          346 }
          347 
          348 static int
          349 fsysVac(Fsys* fsys, int argc, char* argv[])
          350 {
          351         uchar score[VtScoreSize];
          352         char *usage = "usage: [fsys name] vac path";
          353 
          354         ARGBEGIN{
          355         default:
          356                 return cliError(usage);
          357         }ARGEND
          358         if(argc != 1)
          359                 return cliError(usage);
          360 
          361         if(!fsVac(fsys->fs, argv[0], score))
          362                 return 0;
          363 
          364         consPrint("vac:%V\n", score);
          365         return 1;
          366 }
          367 
          368 static int
          369 fsysSnap(Fsys* fsys, int argc, char* argv[])
          370 {
          371         int doarchive;
          372         char *usage = "usage: [fsys name] snap [-a] [-s /active] [-d /archive/yyyy/mmmm]";
          373         char *src, *dst;
          374 
          375         src = nil;
          376         dst = nil;
          377         doarchive = 0;
          378         ARGBEGIN{
          379         default:
          380                 return cliError(usage);
          381         case 'a':
          382                 doarchive = 1;
          383                 break;
          384         case 'd':
          385                 if((dst = ARGF()) == nil)
          386                         return cliError(usage);
          387                 break;
          388         case 's':
          389                 if((src = ARGF()) == nil)
          390                         return cliError(usage);
          391                 break;
          392         }ARGEND
          393         if(argc)
          394                 return cliError(usage);
          395 
          396         if(!fsSnapshot(fsys->fs, src, dst, doarchive))
          397                 return 0;
          398 
          399         return 1;
          400 }
          401 
          402 static int
          403 fsysSnapClean(Fsys *fsys, int argc, char* argv[])
          404 {
          405         u32int arch, snap, life;
          406         char *usage = "usage: [fsys name] snapclean [maxminutes]\n";
          407 
          408         ARGBEGIN{
          409         default:
          410                 return cliError(usage);
          411         }ARGEND
          412 
          413         if(argc > 1)
          414                 return cliError(usage);
          415         if(argc == 1)
          416                 life = atoi(argv[0]);
          417         else
          418                 snapGetTimes(fsys->fs->snap, &arch, &snap, &life);
          419 
          420         fsSnapshotCleanup(fsys->fs, life);
          421         return 1;
          422 }
          423 
          424 static int
          425 fsysSnapTime(Fsys* fsys, int argc, char* argv[])
          426 {
          427         char buf[128], *x;
          428         int hh, mm, changed;
          429         u32int arch, snap, life;
          430         char *usage = "usage: [fsys name] snaptime [-a hhmm] [-s snapminutes] [-t maxminutes]";
          431 
          432         changed = 0;
          433         snapGetTimes(fsys->fs->snap, &arch, &snap, &life);
          434         ARGBEGIN{
          435         case 'a':
          436                 changed = 1;
          437                 x = ARGF();
          438                 if(x == nil)
          439                         return cliError(usage);
          440                 if(strcmp(x, "none") == 0){
          441                         arch = ~(u32int)0;
          442                         break;
          443                 }
          444                 if(strlen(x) != 4 || strspn(x, "0123456789") != 4)
          445                         return cliError(usage);
          446                 hh = (x[0]-'0')*10 + x[1]-'0';
          447                 mm = (x[2]-'0')*10 + x[3]-'0';
          448                 if(hh >= 24 || mm >= 60)
          449                         return cliError(usage);
          450                 arch = hh*60+mm;
          451                 break;
          452         case 's':
          453                 changed = 1;
          454                 x = ARGF();
          455                 if(x == nil)
          456                         return cliError(usage);
          457                 if(strcmp(x, "none") == 0){
          458                         snap = ~(u32int)0;
          459                         break;
          460                 }
          461                 snap = atoi(x);
          462                 break;
          463         case 't':
          464                 changed = 1;
          465                 x = ARGF();
          466                 if(x == nil)
          467                         return cliError(usage);
          468                 if(strcmp(x, "none") == 0){
          469                         life = ~(u32int)0;
          470                         break;
          471                 }
          472                 life = atoi(x);
          473                 break;
          474         default:
          475                 return cliError(usage);
          476         }ARGEND
          477         if(argc > 0)
          478                 return cliError(usage);
          479 
          480         if(changed){
          481                 snapSetTimes(fsys->fs->snap, arch, snap, life);
          482                 return 1;
          483         }
          484         snapGetTimes(fsys->fs->snap, &arch, &snap, &life);
          485         if(arch != ~(u32int)0)
          486                 sprint(buf, "-a %02d%02d", arch/60, arch%60);
          487         else
          488                 sprint(buf, "-a none");
          489         if(snap != ~(u32int)0)
          490                 sprint(buf+strlen(buf), " -s %d", snap);
          491         else
          492                 sprint(buf+strlen(buf), " -s none");
          493         if(life != ~(u32int)0)
          494                 sprint(buf+strlen(buf), " -t %ud", life);
          495         else
          496                 sprint(buf+strlen(buf), " -t none");
          497         consPrint("\tsnaptime %s\n", buf);
          498         return 1;
          499 }
          500 
          501 static int
          502 fsysSync(Fsys* fsys, int argc, char* argv[])
          503 {
          504         char *usage = "usage: [fsys name] sync";
          505         int n;
          506 
          507         ARGBEGIN{
          508         default:
          509                 return cliError(usage);
          510         }ARGEND
          511         if(argc > 0)
          512                 return cliError(usage);
          513 
          514         n = cacheDirty(fsys->fs->cache);
          515         fsSync(fsys->fs);
          516         consPrint("\t%s sync: wrote %d blocks\n", fsys->name, n);
          517         return 1;
          518 }
          519 
          520 static int
          521 fsysHalt(Fsys *fsys, int argc, char* argv[])
          522 {
          523         char *usage = "usage: [fsys name] halt";
          524 
          525         ARGBEGIN{
          526         default:
          527                 return cliError(usage);
          528         }ARGEND
          529         if(argc > 0)
          530                 return cliError(usage);
          531 
          532         fsHalt(fsys->fs);
          533         return 1;
          534 }
          535 
          536 static int
          537 fsysUnhalt(Fsys *fsys, int argc, char* argv[])
          538 {
          539         char *usage = "usage: [fsys name] unhalt";
          540 
          541         ARGBEGIN{
          542         default:
          543                 return cliError(usage);
          544         }ARGEND
          545         if(argc > 0)
          546                 return cliError(usage);
          547 
          548         if(!fsys->fs->halted)
          549                 return cliError("file system %s not halted", fsys->name);
          550 
          551         fsUnhalt(fsys->fs);
          552         return 1;
          553 }
          554 
          555 static int
          556 fsysRemove(Fsys* fsys, int argc, char* argv[])
          557 {
          558         File *file;
          559         char *usage = "usage: [fsys name] remove path ...";
          560 
          561         ARGBEGIN{
          562         default:
          563                 return cliError(usage);
          564         }ARGEND
          565         if(argc == 0)
          566                 return cliError(usage);
          567 
          568         rlock(&fsys->fs->elk);
          569         while(argc > 0){
          570                 if((file = fileOpen(fsys->fs, argv[0])) == nil)
          571                         consPrint("%s: %r\n", argv[0]);
          572                 else{
          573                         if(!fileRemove(file, uidadm))
          574                                 consPrint("%s: %r\n", argv[0]);
          575                         fileDecRef(file);
          576                 }
          577                 argc--;
          578                 argv++;
          579         }
          580         runlock(&fsys->fs->elk);
          581 
          582         return 1;
          583 }
          584 
          585 static int
          586 fsysClri(Fsys* fsys, int argc, char* argv[])
          587 {
          588         char *usage = "usage: [fsys name] clri path ...";
          589 
          590         ARGBEGIN{
          591         default:
          592                 return cliError(usage);
          593         }ARGEND
          594         if(argc == 0)
          595                 return cliError(usage);
          596 
          597         rlock(&fsys->fs->elk);
          598         while(argc > 0){
          599                 if(!fileClriPath(fsys->fs, argv[0], uidadm))
          600                         consPrint("clri %s: %r\n", argv[0]);
          601                 argc--;
          602                 argv++;
          603         }
          604         runlock(&fsys->fs->elk);
          605 
          606         return 1;
          607 }
          608 
          609 /*
          610  * Inspect and edit the labels for blocks on disk.
          611  */
          612 static int
          613 fsysLabel(Fsys* fsys, int argc, char* argv[])
          614 {
          615         Fs *fs;
          616         Label l;
          617         int n, r;
          618         u32int addr;
          619         Block *b, *bb;
          620         char *usage = "usage: [fsys name] label addr [type state epoch epochClose tag]";
          621 
          622         ARGBEGIN{
          623         default:
          624                 return cliError(usage);
          625         }ARGEND
          626         if(argc != 1 && argc != 6)
          627                 return cliError(usage);
          628 
          629         r = 0;
          630         rlock(&fsys->fs->elk);
          631 
          632         fs = fsys->fs;
          633         addr = strtoul(argv[0], 0, 0);
          634         b = cacheLocal(fs->cache, PartData, addr, OReadOnly);
          635         if(b == nil)
          636                 goto Out0;
          637 
          638         l = b->l;
          639         consPrint("%slabel %#ux %ud %ud %ud %ud %#x\n",
          640                 argc==6 ? "old: " : "", addr, l.type, l.state,
          641                 l.epoch, l.epochClose, l.tag);
          642 
          643         if(argc == 6){
          644                 if(strcmp(argv[1], "-") != 0)
          645                         l.type = atoi(argv[1]);
          646                 if(strcmp(argv[2], "-") != 0)
          647                         l.state = atoi(argv[2]);
          648                 if(strcmp(argv[3], "-") != 0)
          649                         l.epoch = strtoul(argv[3], 0, 0);
          650                 if(strcmp(argv[4], "-") != 0)
          651                         l.epochClose = strtoul(argv[4], 0, 0);
          652                 if(strcmp(argv[5], "-") != 0)
          653                         l.tag = strtoul(argv[5], 0, 0);
          654 
          655                 consPrint("new: label %#ux %ud %ud %ud %ud %#x\n",
          656                         addr, l.type, l.state, l.epoch, l.epochClose, l.tag);
          657                 bb = _blockSetLabel(b, &l);
          658                 if(bb == nil)
          659                         goto Out1;
          660                 n = 0;
          661                 for(;;){
          662                         if(blockWrite(bb, Waitlock)){
          663                                 while(bb->iostate != BioClean){
          664                                         assert(bb->iostate == BioWriting);
          665                                         rsleep(&bb->ioready);
          666                                 }
          667                                 break;
          668                         }
          669                         consPrint("blockWrite: %r\n");
          670                         if(n++ >= 5){
          671                                 consPrint("giving up\n");
          672                                 break;
          673                         }
          674                         sleep(5*1000);
          675                 }
          676                 blockPut(bb);
          677         }
          678         r = 1;
          679 Out1:
          680         blockPut(b);
          681 Out0:
          682         runlock(&fs->elk);
          683 
          684         return r;
          685 }
          686 
          687 /*
          688  * Inspect and edit the blocks on disk.
          689  */
          690 static int
          691 fsysBlock(Fsys* fsys, int argc, char* argv[])
          692 {
          693         Fs *fs;
          694         char *s;
          695         Block *b;
          696         uchar *buf;
          697         u32int addr;
          698         int c, count, i, offset;
          699         char *usage = "usage: [fsys name] block addr offset [count [data]]";
          700 
          701         ARGBEGIN{
          702         default:
          703                 return cliError(usage);
          704         }ARGEND
          705         if(argc < 2 || argc > 4)
          706                 return cliError(usage);
          707 
          708         fs = fsys->fs;
          709         addr = strtoul(argv[0], 0, 0);
          710         offset = strtoul(argv[1], 0, 0);
          711         if(offset < 0 || offset >= fs->blockSize){
          712                 werrstr("bad offset");
          713                 return 0;
          714         }
          715         if(argc > 2)
          716                 count = strtoul(argv[2], 0, 0);
          717         else
          718                 count = 100000000;
          719         if(offset+count > fs->blockSize)
          720                 count = fs->blockSize - count;
          721 
          722         rlock(&fs->elk);
          723 
          724         b = cacheLocal(fs->cache, PartData, addr, argc==4 ? OReadWrite : OReadOnly);
          725         if(b == nil){
          726                 werrstr("cacheLocal %#ux: %r", addr);
          727                 runlock(&fs->elk);
          728                 return 0;
          729         }
          730 
          731         consPrint("\t%sblock %#ux %ud %ud %.*H\n",
          732                 argc==4 ? "old: " : "", addr, offset, count, count, b->data+offset);
          733 
          734         if(argc == 4){
          735                 s = argv[3];
          736                 if(strlen(s) != 2*count){
          737                         werrstr("bad data count");
          738                         goto Out;
          739                 }
          740                 buf = vtmallocz(count);
          741                 for(i = 0; i < count*2; i++){
          742                         if(s[i] >= '0' && s[i] <= '9')
          743                                 c = s[i] - '0';
          744                         else if(s[i] >= 'a' && s[i] <= 'f')
          745                                 c = s[i] - 'a' + 10;
          746                         else if(s[i] >= 'A' && s[i] <= 'F')
          747                                 c = s[i] - 'A' + 10;
          748                         else{
          749                                 werrstr("bad hex");
          750                                 vtfree(buf);
          751                                 goto Out;
          752                         }
          753                         if((i & 1) == 0)
          754                                 c <<= 4;
          755                         buf[i>>1] |= c;
          756                 }
          757                 memmove(b->data+offset, buf, count);
          758                 consPrint("\tnew: block %#ux %ud %ud %.*H\n",
          759                         addr, offset, count, count, b->data+offset);
          760                 blockDirty(b);
          761         }
          762 
          763 Out:
          764         blockPut(b);
          765         runlock(&fs->elk);
          766 
          767         return 1;
          768 }
          769 
          770 /*
          771  * Free a disk block.
          772  */
          773 static int
          774 fsysBfree(Fsys* fsys, int argc, char* argv[])
          775 {
          776         Fs *fs;
          777         Label l;
          778         char *p;
          779         Block *b;
          780         u32int addr;
          781         char *usage = "usage: [fsys name] bfree addr ...";
          782 
          783         ARGBEGIN{
          784         default:
          785                 return cliError(usage);
          786         }ARGEND
          787         if(argc == 0)
          788                 return cliError(usage);
          789 
          790         fs = fsys->fs;
          791         rlock(&fs->elk);
          792         while(argc > 0){
          793                 addr = strtoul(argv[0], &p, 0);
          794                 if(*p != '\0'){
          795                         consPrint("bad address - '%ud'\n", addr);
          796                         /* syntax error; let's stop */
          797                         runlock(&fs->elk);
          798                         return 0;
          799                 }
          800                 b = cacheLocal(fs->cache, PartData, addr, OReadOnly);
          801                 if(b == nil){
          802                         consPrint("loading %#ux: %r\n", addr);
          803                         continue;
          804                 }
          805                 l = b->l;
          806                 if(l.state == BsFree)
          807                         consPrint("%#ux is already free\n", addr);
          808                 else{
          809                         consPrint("label %#ux %ud %ud %ud %ud %#x\n",
          810                                 addr, l.type, l.state, l.epoch, l.epochClose, l.tag);
          811                         l.state = BsFree;
          812                         l.type = BtMax;
          813                         l.tag = 0;
          814                         l.epoch = 0;
          815                         l.epochClose = 0;
          816                         if(!blockSetLabel(b, &l, 0))
          817                                 consPrint("freeing %#ux: %r\n", addr);
          818                 }
          819                 blockPut(b);
          820                 argc--;
          821                 argv++;
          822         }
          823         runlock(&fs->elk);
          824 
          825         return 1;
          826 }
          827 
          828 static int
          829 fsysDf(Fsys *fsys, int argc, char* argv[])
          830 {
          831         char *usage = "usage: [fsys name] df";
          832         u32int used, tot, bsize;
          833         Fs *fs;
          834 
          835         ARGBEGIN{
          836         default:
          837                 return cliError(usage);
          838         }ARGEND
          839         if(argc != 0)
          840                 return cliError(usage);
          841 
          842         fs = fsys->fs;
          843         cacheCountUsed(fs->cache, fs->elo, &used, &tot, &bsize);
          844         consPrint("\t%s: %,llud used + %,llud free = %,llud (%.1f%% used)\n",
          845                 fsys->name, used*(vlong)bsize, (tot-used)*(vlong)bsize,
          846                 tot*(vlong)bsize, used*100.0/tot);
          847         return 1;
          848 }
          849 
          850 /*
          851  * Zero an entry or a pointer.
          852  */
          853 static int
          854 fsysClrep(Fsys* fsys, int argc, char* argv[], int ch)
          855 {
          856         Fs *fs;
          857         Entry e;
          858         Block *b;
          859         u32int addr;
          860         int i, max, offset, sz;
          861         uchar zero[VtEntrySize];
          862         char *usage = "usage: [fsys name] clr%c addr offset ...";
          863 
          864         ARGBEGIN{
          865         default:
          866                 return cliError(usage, ch);
          867         }ARGEND
          868         if(argc < 2)
          869                 return cliError(usage, ch);
          870 
          871         fs = fsys->fs;
          872         rlock(&fsys->fs->elk);
          873 
          874         addr = strtoul(argv[0], 0, 0);
          875         b = cacheLocal(fs->cache, PartData, addr, argc==4 ? OReadWrite : OReadOnly);
          876         if(b == nil){
          877                 werrstr("cacheLocal %#ux: %r", addr);
          878         Err:
          879                 runlock(&fsys->fs->elk);
          880                 return 0;
          881         }
          882 
          883         switch(ch){
          884         default:
          885                 werrstr("clrep");
          886                 goto Err;
          887         case 'e':
          888                 if(b->l.type != BtDir){
          889                         werrstr("wrong block type");
          890                         goto Err;
          891                 }
          892                 sz = VtEntrySize;
          893                 memset(&e, 0, sizeof e);
          894                 entryPack(&e, zero, 0);
          895                 break;
          896         case 'p':
          897                 if(b->l.type == BtDir || b->l.type == BtData){
          898                         werrstr("wrong block type");
          899                         goto Err;
          900                 }
          901                 sz = VtScoreSize;
          902                 memmove(zero, vtzeroscore, VtScoreSize);
          903                 break;
          904         }
          905         max = fs->blockSize/sz;
          906 
          907         for(i = 1; i < argc; i++){
          908                 offset = atoi(argv[i]);
          909                 if(offset >= max){
          910                         consPrint("\toffset %d too large (>= %d)\n", i, max);
          911                         continue;
          912                 }
          913                 consPrint("\tblock %#ux %d %d %.*H\n", addr, offset*sz, sz, sz, b->data+offset*sz);
          914                 memmove(b->data+offset*sz, zero, sz);
          915         }
          916         blockDirty(b);
          917         blockPut(b);
          918         runlock(&fsys->fs->elk);
          919 
          920         return 1;
          921 }
          922 
          923 static int
          924 fsysClre(Fsys* fsys, int argc, char* argv[])
          925 {
          926         return fsysClrep(fsys, argc, argv, 'e');
          927 }
          928 
          929 static int
          930 fsysClrp(Fsys* fsys, int argc, char* argv[])
          931 {
          932         return fsysClrep(fsys, argc, argv, 'p');
          933 }
          934 
          935 static int
          936 fsysEsearch1(File* f, char* s, u32int elo)
          937 {
          938         int n, r;
          939         DirEntry de;
          940         DirEntryEnum *dee;
          941         File *ff;
          942         Entry e, ee;
          943         char *t;
          944 
          945         dee = deeOpen(f);
          946         if(dee == nil)
          947                 return 0;
          948 
          949         n = 0;
          950         for(;;){
          951                 r = deeRead(dee, &de);
          952                 if(r < 0){
          953                         consPrint("\tdeeRead %s/%s: %r\n", s, de.elem);
          954                         break;
          955                 }
          956                 if(r == 0)
          957                         break;
          958                 if(de.mode & ModeSnapshot){
          959                         if((ff = fileWalk(f, de.elem)) == nil)
          960                                 consPrint("\tcannot walk %s/%s: %r\n", s, de.elem);
          961                         else{
          962                                 if(!fileGetSources(ff, &e, &ee))
          963                                         consPrint("\tcannot get sources for %s/%s: %r\n", s, de.elem);
          964                                 else if(e.snap != 0 && e.snap < elo){
          965                                         consPrint("\t%ud\tclri %s/%s\n", e.snap, s, de.elem);
          966                                         n++;
          967                                 }
          968                                 fileDecRef(ff);
          969                         }
          970                 }
          971                 else if(de.mode & ModeDir){
          972                         if((ff = fileWalk(f, de.elem)) == nil)
          973                                 consPrint("\tcannot walk %s/%s: %r\n", s, de.elem);
          974                         else{
          975                                 t = smprint("%s/%s", s, de.elem);
          976                                 n += fsysEsearch1(ff, t, elo);
          977                                 vtfree(t);
          978                                 fileDecRef(ff);
          979                         }
          980                 }
          981                 deCleanup(&de);
          982                 if(r < 0)
          983                         break;
          984         }
          985         deeClose(dee);
          986 
          987         return n;
          988 }
          989 
          990 static int
          991 fsysEsearch(Fs* fs, char* path, u32int elo)
          992 {
          993         int n;
          994         File *f;
          995         DirEntry de;
          996 
          997         f = fileOpen(fs, path);
          998         if(f == nil)
          999                 return 0;
         1000         if(!fileGetDir(f, &de)){
         1001                 consPrint("\tfileGetDir %s failed: %r\n", path);
         1002                 fileDecRef(f);
         1003                 return 0;
         1004         }
         1005         if((de.mode & ModeDir) == 0){
         1006                 fileDecRef(f);
         1007                 deCleanup(&de);
         1008                 return 0;
         1009         }
         1010         deCleanup(&de);
         1011         n = fsysEsearch1(f, path, elo);
         1012         fileDecRef(f);
         1013         return n;
         1014 }
         1015 
         1016 static int
         1017 fsysEpoch(Fsys* fsys, int argc, char* argv[])
         1018 {
         1019         Fs *fs;
         1020         int force, n, remove;
         1021         u32int low, old;
         1022         char *usage = "usage: [fsys name] epoch [[-ry] low]";
         1023 
         1024         force = 0;
         1025         remove = 0;
         1026         ARGBEGIN{
         1027         case 'y':
         1028                 force = 1;
         1029                 break;
         1030         case 'r':
         1031                 remove = 1;
         1032                 break;
         1033         default:
         1034                 return cliError(usage);
         1035         }ARGEND
         1036         if(argc > 1)
         1037                 return cliError(usage);
         1038         if(argc > 0)
         1039                 low = strtoul(argv[0], 0, 0);
         1040         else
         1041                 low = ~(u32int)0;
         1042 
         1043         if(low == 0)
         1044                 return cliError("low epoch cannot be zero");
         1045 
         1046         fs = fsys->fs;
         1047 
         1048         rlock(&fs->elk);
         1049         consPrint("\tlow %ud hi %ud\n", fs->elo, fs->ehi);
         1050         if(low == ~(u32int)0){
         1051                 runlock(&fs->elk);
         1052                 return 1;
         1053         }
         1054         n = fsysEsearch(fsys->fs, "/archive", low);
         1055         n += fsysEsearch(fsys->fs, "/snapshot", low);
         1056         consPrint("\t%d snapshot%s found with epoch < %ud\n", n, n==1 ? "" : "s", low);
         1057         runlock(&fs->elk);
         1058 
         1059         /*
         1060          * There's a small race here -- a new snapshot with epoch < low might
         1061          * get introduced now that we unlocked fs->elk.  Low has to
         1062          * be <= fs->ehi.  Of course, in order for this to happen low has
         1063          * to be equal to the current fs->ehi _and_ a snapshot has to
         1064          * run right now.  This is a small enough window that I don't care.
         1065          */
         1066         if(n != 0 && !force){
         1067                 consPrint("\tnot setting low epoch\n");
         1068                 return 1;
         1069         }
         1070         old = fs->elo;
         1071         if(!fsEpochLow(fs, low))
         1072                 consPrint("\tfsEpochLow: %r\n");
         1073         else{
         1074                 consPrint("\told: epoch%s %ud\n", force ? " -y" : "", old);
         1075                 consPrint("\tnew: epoch%s %ud\n", force ? " -y" : "", fs->elo);
         1076                 if(fs->elo < low)
         1077                         consPrint("\twarning: new low epoch < old low epoch\n");
         1078                 if(force && remove)
         1079                         fsSnapshotRemove(fs);
         1080         }
         1081 
         1082         return 1;
         1083 }
         1084 
         1085 static int
         1086 fsysCreate(Fsys* fsys, int argc, char* argv[])
         1087 {
         1088         int r;
         1089         ulong mode;
         1090         char *elem, *p, *path;
         1091         char *usage = "usage: [fsys name] create path uid gid perm";
         1092         DirEntry de;
         1093         File *file, *parent;
         1094 
         1095         ARGBEGIN{
         1096         default:
         1097                 return cliError(usage);
         1098         }ARGEND
         1099         if(argc != 4)
         1100                 return cliError(usage);
         1101 
         1102         if(!fsysParseMode(argv[3], &mode))
         1103                 return cliError(usage);
         1104         if(mode&ModeSnapshot)
         1105                 return cliError("create - cannot create with snapshot bit set");
         1106 
         1107         if(strcmp(argv[1], uidnoworld) == 0)
         1108                 return cliError("permission denied");
         1109 
         1110         rlock(&fsys->fs->elk);
         1111         path = vtstrdup(argv[0]);
         1112         if((p = strrchr(path, '/')) != nil){
         1113                 *p++ = '\0';
         1114                 elem = p;
         1115                 p = path;
         1116                 if(*p == '\0')
         1117                         p = "/";
         1118         }
         1119         else{
         1120                 p = "/";
         1121                 elem = path;
         1122         }
         1123 
         1124         r = 0;
         1125         if((parent = fileOpen(fsys->fs, p)) == nil)
         1126                 goto out;
         1127 
         1128         file = fileCreate(parent, elem, mode, argv[1]);
         1129         fileDecRef(parent);
         1130         if(file == nil){
         1131                 werrstr("create %s/%s: %r", p, elem);
         1132                 goto out;
         1133         }
         1134 
         1135         if(!fileGetDir(file, &de)){
         1136                 werrstr("stat failed after create: %r");
         1137                 goto out1;
         1138         }
         1139 
         1140         if(strcmp(de.gid, argv[2]) != 0){
         1141                 vtfree(de.gid);
         1142                 de.gid = vtstrdup(argv[2]);
         1143                 if(!fileSetDir(file, &de, argv[1])){
         1144                         werrstr("wstat failed after create: %r");
         1145                         goto out2;
         1146                 }
         1147         }
         1148         r = 1;
         1149 
         1150 out2:
         1151         deCleanup(&de);
         1152 out1:
         1153         fileDecRef(file);
         1154 out:
         1155         vtfree(path);
         1156         runlock(&fsys->fs->elk);
         1157 
         1158         return r;
         1159 }
         1160 
         1161 static void
         1162 fsysPrintStat(char *prefix, char *file, DirEntry *de)
         1163 {
         1164         char buf[64];
         1165 
         1166         if(prefix == nil)
         1167                 prefix = "";
         1168         consPrint("%sstat %q %q %q %q %s %llud\n", prefix,
         1169                 file, de->elem, de->uid, de->gid, fsysModeString(de->mode, buf), de->size);
         1170 }
         1171 
         1172 static int
         1173 fsysStat(Fsys* fsys, int argc, char* argv[])
         1174 {
         1175         int i;
         1176         File *f;
         1177         DirEntry de;
         1178         char *usage = "usage: [fsys name] stat files...";
         1179 
         1180         ARGBEGIN{
         1181         default:
         1182                 return cliError(usage);
         1183         }ARGEND
         1184 
         1185         if(argc == 0)
         1186                 return cliError(usage);
         1187 
         1188         rlock(&fsys->fs->elk);
         1189         for(i=0; i<argc; i++){
         1190                 if((f = fileOpen(fsys->fs, argv[i])) == nil){
         1191                         consPrint("%s: %r\n", argv[i]);
         1192                         continue;
         1193                 }
         1194                 if(!fileGetDir(f, &de)){
         1195                         consPrint("%s: %r\n", argv[i]);
         1196                         fileDecRef(f);
         1197                         continue;
         1198                 }
         1199                 fsysPrintStat("\t", argv[i], &de);
         1200                 deCleanup(&de);
         1201                 fileDecRef(f);
         1202         }
         1203         runlock(&fsys->fs->elk);
         1204         return 1;
         1205 }
         1206 
         1207 static int
         1208 fsysWstat(Fsys *fsys, int argc, char* argv[])
         1209 {
         1210         File *f;
         1211         char *p;
         1212         DirEntry de;
         1213         char *usage = "usage: [fsys name] wstat file elem uid gid mode length\n"
         1214                 "\tuse - for any field to mean don't change";
         1215 
         1216         ARGBEGIN{
         1217         default:
         1218                 return cliError(usage);
         1219         }ARGEND
         1220 
         1221         if(argc != 6)
         1222                 return cliError(usage);
         1223 
         1224         rlock(&fsys->fs->elk);
         1225         if((f = fileOpen(fsys->fs, argv[0])) == nil){
         1226                 werrstr("console wstat - walk - %r");
         1227                 runlock(&fsys->fs->elk);
         1228                 return 0;
         1229         }
         1230         if(!fileGetDir(f, &de)){
         1231                 werrstr("console wstat - stat - %r");
         1232                 fileDecRef(f);
         1233                 runlock(&fsys->fs->elk);
         1234                 return 0;
         1235         }
         1236         fsysPrintStat("\told: w", argv[0], &de);
         1237 
         1238         if(strcmp(argv[1], "-") != 0){
         1239                 if(!validFileName(argv[1])){
         1240                         werrstr("console wstat - bad elem");
         1241                         goto error;
         1242                 }
         1243                 vtfree(de.elem);
         1244                 de.elem = vtstrdup(argv[1]);
         1245         }
         1246         if(strcmp(argv[2], "-") != 0){
         1247                 if(!validUserName(argv[2])){
         1248                         werrstr("console wstat - bad uid");
         1249                         goto error;
         1250                 }
         1251                 vtfree(de.uid);
         1252                 de.uid = vtstrdup(argv[2]);
         1253         }
         1254         if(strcmp(argv[3], "-") != 0){
         1255                 if(!validUserName(argv[3])){
         1256                         werrstr("console wstat - bad gid");
         1257                         goto error;
         1258                 }
         1259                 vtfree(de.gid);
         1260                 de.gid = vtstrdup(argv[3]);
         1261         }
         1262         if(strcmp(argv[4], "-") != 0){
         1263                 if(!fsysParseMode(argv[4], &de.mode)){
         1264                         werrstr("console wstat - bad mode");
         1265                         goto error;
         1266                 }
         1267         }
         1268         if(strcmp(argv[5], "-") != 0){
         1269                 de.size = strtoull(argv[5], &p, 0);
         1270                 if(argv[5][0] == '\0' || *p != '\0' || (vlong)de.size < 0){
         1271                         werrstr("console wstat - bad length");
         1272                         goto error;
         1273                 }
         1274         }
         1275 
         1276         if(!fileSetDir(f, &de, uidadm)){
         1277                 werrstr("console wstat - %r");
         1278                 goto error;
         1279         }
         1280         deCleanup(&de);
         1281 
         1282         if(!fileGetDir(f, &de)){
         1283                 werrstr("console wstat - stat2 - %r");
         1284                 goto error;
         1285         }
         1286         fsysPrintStat("\tnew: w", argv[0], &de);
         1287         deCleanup(&de);
         1288         fileDecRef(f);
         1289         runlock(&fsys->fs->elk);
         1290 
         1291         return 1;
         1292 
         1293 error:
         1294         deCleanup(&de);        /* okay to do this twice */
         1295         fileDecRef(f);
         1296         runlock(&fsys->fs->elk);
         1297         return 0;
         1298 }
         1299 
         1300 static void
         1301 fsckClri(Fsck *fsck, char *name, MetaBlock *mb, int i, Block *b)
         1302 {
         1303         USED(name);
         1304 
         1305         if((fsck->flags&DoClri) == 0)
         1306                 return;
         1307 
         1308         mbDelete(mb, i);
         1309         mbPack(mb);
         1310         blockDirty(b);
         1311 }
         1312 
         1313 static void
         1314 fsckClose(Fsck *fsck, Block *b, u32int epoch)
         1315 {
         1316         Label l;
         1317 
         1318         if((fsck->flags&DoClose) == 0)
         1319                 return;
         1320         l = b->l;
         1321         if(l.state == BsFree || (l.state&BsClosed)){
         1322                 consPrint("%#ux is already closed\n", b->addr);
         1323                 return;
         1324         }
         1325         if(epoch){
         1326                 l.state |= BsClosed;
         1327                 l.epochClose = epoch;
         1328         }else
         1329                 l.state = BsFree;
         1330 
         1331         if(!blockSetLabel(b, &l, 0))
         1332                 consPrint("%#ux setlabel: %r\n", b->addr);
         1333 }
         1334 
         1335 static void
         1336 fsckClre(Fsck *fsck, Block *b, int offset)
         1337 {
         1338         Entry e;
         1339 
         1340         if((fsck->flags&DoClre) == 0)
         1341                 return;
         1342         if(offset<0 || offset*VtEntrySize >= fsck->bsize){
         1343                 consPrint("bad clre\n");
         1344                 return;
         1345         }
         1346         memset(&e, 0, sizeof e);
         1347         entryPack(&e, b->data, offset);
         1348         blockDirty(b);
         1349 }
         1350 
         1351 static void
         1352 fsckClrp(Fsck *fsck, Block *b, int offset)
         1353 {
         1354         if((fsck->flags&DoClrp) == 0)
         1355                 return;
         1356         if(offset<0 || offset*VtScoreSize >= fsck->bsize){
         1357                 consPrint("bad clre\n");
         1358                 return;
         1359         }
         1360         memmove(b->data+offset*VtScoreSize, vtzeroscore, VtScoreSize);
         1361         blockDirty(b);
         1362 }
         1363 
         1364 static int
         1365 fsysCheck(Fsys *fsys, int argc, char *argv[])
         1366 {
         1367         int i, halting;
         1368         char *usage = "usage: [fsys name] check [-v] [options]";
         1369         Fsck fsck;
         1370         Block *b;
         1371         Super super;
         1372 
         1373         memset(&fsck, 0, sizeof fsck);
         1374         fsck.fs = fsys->fs;
         1375         fsck.clri = fsckClri;
         1376         fsck.clre = fsckClre;
         1377         fsck.clrp = fsckClrp;
         1378         fsck.close = fsckClose;
         1379         fsck.print = consPrint;
         1380 
         1381         ARGBEGIN{
         1382         default:
         1383                 return cliError(usage);
         1384         }ARGEND
         1385 
         1386         for(i=0; i<argc; i++){
         1387                 if(strcmp(argv[i], "pblock") == 0)
         1388                         fsck.printblocks = 1;
         1389                 else if(strcmp(argv[i], "pdir") == 0)
         1390                         fsck.printdirs = 1;
         1391                 else if(strcmp(argv[i], "pfile") == 0)
         1392                         fsck.printfiles = 1;
         1393                 else if(strcmp(argv[i], "bclose") == 0)
         1394                         fsck.flags |= DoClose;
         1395                 else if(strcmp(argv[i], "clri") == 0)
         1396                         fsck.flags |= DoClri;
         1397                 else if(strcmp(argv[i], "clre") == 0)
         1398                         fsck.flags |= DoClre;
         1399                 else if(strcmp(argv[i], "clrp") == 0)
         1400                         fsck.flags |= DoClrp;
         1401                 else if(strcmp(argv[i], "fix") == 0)
         1402                         fsck.flags |= DoClose|DoClri|DoClre|DoClrp;
         1403                 else if(strcmp(argv[i], "venti") == 0)
         1404                         fsck.useventi = 1;
         1405                 else if(strcmp(argv[i], "snapshot") == 0)
         1406                         fsck.walksnapshots = 1;
         1407                 else{
         1408                         consPrint("unknown option '%s'\n", argv[i]);
         1409                         return cliError(usage);
         1410                 }
         1411         }
         1412 
         1413         halting = fsys->fs->halted==0;
         1414         if(halting)
         1415                 fsHalt(fsys->fs);
         1416         if(fsys->fs->arch){
         1417                 b = superGet(fsys->fs->cache, &super);
         1418                 if(b == nil){
         1419                         consPrint("could not load super block\n");
         1420                         goto Out;
         1421                 }
         1422                 blockPut(b);
         1423                 if(super.current != NilBlock){
         1424                         consPrint("cannot check fs while archiver is running; "
         1425                                 "wait for it to finish\n");
         1426                         goto Out;
         1427                 }
         1428         }
         1429         fsCheck(&fsck);
         1430         consPrint("fsck: %d clri, %d clre, %d clrp, %d bclose\n",
         1431                 fsck.nclri, fsck.nclre, fsck.nclrp, fsck.nclose);
         1432 Out:
         1433         if(halting)
         1434                 fsUnhalt(fsys->fs);
         1435         return 1;
         1436 }
         1437 
         1438 static int
         1439 fsysVenti(char* name, int argc, char* argv[])
         1440 {
         1441         int r;
         1442         char *host;
         1443         char *usage = "usage: [fsys name] venti [address]";
         1444         Fsys *fsys;
         1445 
         1446         ARGBEGIN{
         1447         default:
         1448                 return cliError(usage);
         1449         }ARGEND
         1450 
         1451         if(argc == 0)
         1452                 host = nil;
         1453         else if(argc == 1)
         1454                 host = argv[0];
         1455         else
         1456                 return cliError(usage);
         1457 
         1458         if((fsys = _fsysGet(name)) == nil)
         1459                 return 0;
         1460 
         1461         qlock(&fsys->lock);
         1462         if(host == nil)
         1463                 host = fsys->venti;
         1464         else{
         1465                 vtfree(fsys->venti);
         1466                 if(host[0])
         1467                         fsys->venti = vtstrdup(host);
         1468                 else{
         1469                         host = nil;
         1470                         fsys->venti = nil;
         1471                 }
         1472         }
         1473 
         1474         /* already open: do a redial */
         1475         if(fsys->fs != nil){
         1476                 if(fsys->session == nil){
         1477                         werrstr("file system was opened with -V");
         1478                         r = 0;
         1479                         goto out;
         1480                 }
         1481                 r = 1;
         1482                 if(myRedial(fsys->session, host) < 0
         1483                 || vtconnect(fsys->session) < 0)
         1484                         r = 0;
         1485                 goto out;
         1486         }
         1487 
         1488         /* not yet open: try to dial */
         1489         if(fsys->session)
         1490                 vtfreeconn(fsys->session);
         1491         r = 1;
         1492         if((fsys->session = myDial(host)) == nil
         1493         || vtconnect(fsys->session) < 0)
         1494                 r = 0;
         1495 out:
         1496         qunlock(&fsys->lock);
         1497         fsysPut(fsys);
         1498         return r;
         1499 }
         1500 
         1501 static ulong
         1502 freemem(void)
         1503 {
         1504         int nf, pgsize = 0;
         1505         uvlong size, userpgs = 0, userused = 0;
         1506         char *ln, *sl;
         1507         char *fields[2];
         1508         Biobuf *bp;
         1509 
         1510         size = 64*1024*1024;
         1511         bp = Bopen("#c/swap", OREAD);
         1512         if (bp != nil) {
         1513                 while ((ln = Brdline(bp, '\n')) != nil) {
         1514                         ln[Blinelen(bp)-1] = '\0';
         1515                         nf = tokenize(ln, fields, nelem(fields));
         1516                         if (nf != 2)
         1517                                 continue;
         1518                         if (strcmp(fields[1], "pagesize") == 0)
         1519                                 pgsize = atoi(fields[0]);
         1520                         else if (strcmp(fields[1], "user") == 0) {
         1521                                 sl = strchr(fields[0], '/');
         1522                                 if (sl == nil)
         1523                                         continue;
         1524                                 userpgs = atoll(sl+1);
         1525                                 userused = atoll(fields[0]);
         1526                         }
         1527                 }
         1528                 Bterm(bp);
         1529                 if (pgsize > 0 && userpgs > 0)
         1530                         size = (userpgs - userused) * pgsize;
         1531         }
         1532         /* cap it to keep the size within 32 bits */
         1533         if (size >= 3840UL * 1024 * 1024)
         1534                 size = 3840UL * 1024 * 1024;
         1535         return size;
         1536 }
         1537 
         1538 static int
         1539 fsysOpen(char* name, int argc, char* argv[])
         1540 {
         1541         char *p, *host;
         1542         Fsys *fsys;
         1543         int noauth, noventi, noperm, rflag, wstatallow, noatimeupd;
         1544         long ncache;
         1545         char *usage = "usage: fsys name open [-APVWr] [-c ncache]";
         1546 
         1547         ncache = 1000;
         1548         noauth = noperm = wstatallow = noventi = noatimeupd = 0;
         1549         rflag = OReadWrite;
         1550 
         1551         ARGBEGIN{
         1552         default:
         1553                 return cliError(usage);
         1554         case 'A':
         1555                 noauth = 1;
         1556                 break;
         1557         case 'P':
         1558                 noperm = 1;
         1559                 break;
         1560         case 'V':
         1561                 noventi = 1;
         1562                 break;
         1563         case 'W':
         1564                 wstatallow = 1;
         1565                 break;
         1566         case 'a':
         1567                 noatimeupd = 1;
         1568                 break;
         1569         case 'c':
         1570                 p = ARGF();
         1571                 if(p == nil)
         1572                         return cliError(usage);
         1573                 ncache = strtol(argv[0], &p, 0);
         1574                 if(ncache <= 0 || p == argv[0] || *p != '\0')
         1575                         return cliError(usage);
         1576                 break;
         1577         case 'r':
         1578                 rflag = OReadOnly;
         1579                 break;
         1580         }ARGEND
         1581         if(argc)
         1582                 return cliError(usage);
         1583 
         1584         if((fsys = _fsysGet(name)) == nil)
         1585                 return 0;
         1586 
         1587         /* automatic memory sizing? */
         1588         if(mempcnt > 0) {
         1589                 /* TODO: 8K is a hack; use the actual block size */
         1590                 ncache = (((vlong)freemem() * mempcnt) / 100) / (8*1024);
         1591                 if (ncache < 100)
         1592                         ncache = 100;
         1593         }
         1594 
         1595         qlock(&fsys->lock);
         1596         if(fsys->fs != nil){
         1597                 werrstr(EFsysBusy, fsys->name);
         1598                 qunlock(&fsys->lock);
         1599                 fsysPut(fsys);
         1600                 return 0;
         1601         }
         1602 
         1603         if(noventi){
         1604                 if(fsys->session){
         1605                         vtfreeconn(fsys->session);
         1606                         fsys->session = nil;
         1607                 }
         1608         }
         1609         else if(fsys->session == nil){
         1610                 if(fsys->venti && fsys->venti[0])
         1611                         host = fsys->venti;
         1612                 else
         1613                         host = nil;
         1614 
         1615                 if((fsys->session = myDial(host)) == nil
         1616                 || vtconnect(fsys->session) < 0 && !noventi)
         1617                         fprint(2, "warning: connecting to venti: %r\n");
         1618         }
         1619         if((fsys->fs = fsOpen(fsys->dev, fsys->session, ncache, rflag)) == nil){
         1620                 werrstr("fsOpen: %r");
         1621                 qunlock(&fsys->lock);
         1622                 fsysPut(fsys);
         1623                 return 0;
         1624         }
         1625         fsys->fs->name = fsys->name;        /* for better error messages */
         1626         fsys->noauth = noauth;
         1627         fsys->noperm = noperm;
         1628         fsys->wstatallow = wstatallow;
         1629         fsys->fs->noatimeupd = noatimeupd;
         1630         qunlock(&fsys->lock);
         1631         fsysPut(fsys);
         1632 
         1633         if(strcmp(name, "main") == 0)
         1634                 usersFileRead(nil);
         1635 
         1636         return 1;
         1637 }
         1638 
         1639 static int
         1640 fsysUnconfig(char* name, int argc, char* argv[])
         1641 {
         1642         Fsys *fsys, **fp;
         1643         char *usage = "usage: fsys name unconfig";
         1644 
         1645         ARGBEGIN{
         1646         default:
         1647                 return cliError(usage);
         1648         }ARGEND
         1649         if(argc)
         1650                 return cliError(usage);
         1651 
         1652         wlock(&sbox.lock);
         1653         fp = &sbox.head;
         1654         for(fsys = *fp; fsys != nil; fsys = fsys->next){
         1655                 if(strcmp(fsys->name, name) == 0)
         1656                         break;
         1657                 fp = &fsys->next;
         1658         }
         1659         if(fsys == nil){
         1660                 werrstr(EFsysNotFound, name);
         1661                 wunlock(&sbox.lock);
         1662                 return 0;
         1663         }
         1664         if(fsys->ref != 0 || fsys->fs != nil){
         1665                 werrstr(EFsysBusy, fsys->name);
         1666                 wunlock(&sbox.lock);
         1667                 return 0;
         1668         }
         1669         *fp = fsys->next;
         1670         wunlock(&sbox.lock);
         1671 
         1672         if(fsys->session != nil)
         1673                 vtfreeconn(fsys->session);
         1674         if(fsys->venti != nil)
         1675                 vtfree(fsys->venti);
         1676         if(fsys->dev != nil)
         1677                 vtfree(fsys->dev);
         1678         if(fsys->name != nil)
         1679                 vtfree(fsys->name);
         1680         vtfree(fsys);
         1681 
         1682         return 1;
         1683 }
         1684 
         1685 static int
         1686 fsysConfig(char* name, int argc, char* argv[])
         1687 {
         1688         Fsys *fsys;
         1689         char *part;
         1690         char *usage = "usage: fsys name config [dev]";
         1691 
         1692         ARGBEGIN{
         1693         default:
         1694                 return cliError(usage);
         1695         }ARGEND
         1696         if(argc > 1)
         1697                 return cliError(usage);
         1698 
         1699         if(argc == 0)
         1700                 part = foptname;
         1701         else
         1702                 part = argv[0];
         1703 
         1704         if((fsys = _fsysGet(part)) != nil){
         1705                 qlock(&fsys->lock);
         1706                 if(fsys->fs != nil){
         1707                         werrstr(EFsysBusy, fsys->name);
         1708                         qunlock(&fsys->lock);
         1709                         fsysPut(fsys);
         1710                         return 0;
         1711                 }
         1712                 vtfree(fsys->dev);
         1713                 fsys->dev = vtstrdup(part);
         1714                 qunlock(&fsys->lock);
         1715         }
         1716         else if((fsys = fsysAlloc(name, part)) == nil)
         1717                 return 0;
         1718 
         1719         fsysPut(fsys);
         1720         return 1;
         1721 }
         1722 
         1723 static struct {
         1724         char*        cmd;
         1725         int        (*f)(Fsys*, int, char**);
         1726         int        (*f1)(char*, int, char**);
         1727 } fsyscmd[] = {
         1728         { "close",        fsysClose, },
         1729         { "config",        nil, fsysConfig, },
         1730         { "open",        nil, fsysOpen, },
         1731         { "unconfig",        nil, fsysUnconfig, },
         1732         { "venti",        nil, fsysVenti, },
         1733 
         1734         { "bfree",        fsysBfree, },
         1735         { "block",        fsysBlock, },
         1736         { "check",        fsysCheck, },
         1737         { "clre",        fsysClre, },
         1738         { "clri",        fsysClri, },
         1739         { "clrp",        fsysClrp, },
         1740         { "create",        fsysCreate, },
         1741         { "df",                fsysDf, },
         1742         { "epoch",        fsysEpoch, },
         1743         { "halt",        fsysHalt, },
         1744         { "label",        fsysLabel, },
         1745         { "remove",        fsysRemove, },
         1746         { "snap",        fsysSnap, },
         1747         { "snaptime",        fsysSnapTime, },
         1748         { "snapclean",        fsysSnapClean, },
         1749         { "stat",        fsysStat, },
         1750         { "sync",        fsysSync, },
         1751         { "unhalt",        fsysUnhalt, },
         1752         { "wstat",        fsysWstat, },
         1753         { "vac",        fsysVac, },
         1754 
         1755         { nil,                nil, },
         1756 };
         1757 
         1758 static int
         1759 fsysXXX1(Fsys *fsys, int i, int argc, char* argv[])
         1760 {
         1761         int r;
         1762 
         1763         qlock(&fsys->lock);
         1764         if(fsys->fs == nil){
         1765                 qunlock(&fsys->lock);
         1766                 werrstr(EFsysNotOpen, fsys->name);
         1767                 return 0;
         1768         }
         1769 
         1770         if(fsys->fs->halted
         1771         && fsyscmd[i].f != fsysUnhalt && fsyscmd[i].f != fsysCheck){
         1772                 werrstr("file system %s is halted", fsys->name);
         1773                 qunlock(&fsys->lock);
         1774                 return 0;
         1775         }
         1776 
         1777         r = (*fsyscmd[i].f)(fsys, argc, argv);
         1778         qunlock(&fsys->lock);
         1779         return r;
         1780 }
         1781 
         1782 static int
         1783 fsysXXX(char* name, int argc, char* argv[])
         1784 {
         1785         int i, r;
         1786         Fsys *fsys;
         1787 
         1788         for(i = 0; fsyscmd[i].cmd != nil; i++){
         1789                 if(strcmp(fsyscmd[i].cmd, argv[0]) == 0)
         1790                         break;
         1791         }
         1792 
         1793         if(fsyscmd[i].cmd == nil){
         1794                 werrstr("unknown command - '%s'", argv[0]);
         1795                 return 0;
         1796         }
         1797 
         1798         /* some commands want the name... */
         1799         if(fsyscmd[i].f1 != nil){
         1800                 if(strcmp(name, FsysAll) == 0){
         1801                         werrstr("cannot use fsys %#q with %#q command", FsysAll, argv[0]);
         1802                         return 0;
         1803                 }
         1804                 return (*fsyscmd[i].f1)(name, argc, argv);
         1805         }
         1806 
         1807         /* ... but most commands want the Fsys */
         1808         if(strcmp(name, FsysAll) == 0){
         1809                 r = 1;
         1810                 rlock(&sbox.lock);
         1811                 for(fsys = sbox.head; fsys != nil; fsys = fsys->next){
         1812                         fsys->ref++;
         1813                         r = fsysXXX1(fsys, i, argc, argv) && r;
         1814                         fsys->ref--;
         1815                 }
         1816                 runlock(&sbox.lock);
         1817         }else{
         1818                 if((fsys = _fsysGet(name)) == nil)
         1819                         return 0;
         1820                 r = fsysXXX1(fsys, i, argc, argv);
         1821                 fsysPut(fsys);
         1822         }
         1823         return r;
         1824 }
         1825 
         1826 static int
         1827 cmdFsysXXX(int argc, char* argv[])
         1828 {
         1829         char *name;
         1830 
         1831         if((name = sbox.curfsys) == nil){
         1832                 werrstr(EFsysNoCurrent, argv[0]);
         1833                 return 0;
         1834         }
         1835 
         1836         return fsysXXX(name, argc, argv);
         1837 }
         1838 
         1839 static int
         1840 cmdFsys(int argc, char* argv[])
         1841 {
         1842         Fsys *fsys;
         1843         char *usage = "usage: fsys [name ...]";
         1844 
         1845         ARGBEGIN{
         1846         default:
         1847                 return cliError(usage);
         1848         }ARGEND
         1849 
         1850         if(argc == 0){
         1851                 rlock(&sbox.lock);
         1852                 currfsysname = sbox.head->name;
         1853                 for(fsys = sbox.head; fsys != nil; fsys = fsys->next)
         1854                         consPrint("\t%s\n", fsys->name);
         1855                 runlock(&sbox.lock);
         1856                 return 1;
         1857         }
         1858         if(argc == 1){
         1859                 fsys = nil;
         1860                 if(strcmp(argv[0], FsysAll) != 0 && (fsys = fsysGet(argv[0])) == nil)
         1861                         return 0;
         1862                 sbox.curfsys = vtstrdup(argv[0]);
         1863                 consPrompt(sbox.curfsys);
         1864                 if(fsys)
         1865                         fsysPut(fsys);
         1866                 return 1;
         1867         }
         1868 
         1869         return fsysXXX(argv[0], argc-1, argv+1);
         1870 }
         1871 
         1872 int
         1873 fsysInit(void)
         1874 {
         1875         int i;
         1876 
         1877         fmtinstall('H', encodefmt);
         1878         fmtinstall('V', scoreFmt);
         1879         fmtinstall('L', labelFmt);
         1880 
         1881         cliAddCmd("fsys", cmdFsys);
         1882         for(i = 0; fsyscmd[i].cmd != nil; i++){
         1883                 if(fsyscmd[i].f != nil)
         1884                         cliAddCmd(fsyscmd[i].cmd, cmdFsysXXX);
         1885         }
         1886         /* the venti cmd is special: the fs can be either open or closed */
         1887         cliAddCmd("venti", cmdFsysXXX);
         1888         cliAddCmd("printconfig", cmdPrintConfig);
         1889 
         1890         return 1;
         1891 }