]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/disk/sacfs/sacfs.c
Add Erik Quanstrom's smart tool for ATA SMART.
[plan9front.git] / sys / src / cmd / disk / sacfs / sacfs.c
1 #include <u.h>
2 #include <libc.h>
3 #include <auth.h>
4 #include <fcall.h>
5 #include "sac.h"
6 #include "sacfs.h"
7
8 /*
9  * Rather than reading /adm/users, which is a lot of work for
10  * a toy program, we assume all groups have the form
11  *      NNN:user:user:
12  * meaning that each user is the leader of his own group.
13  */
14
15 enum
16 {
17         OPERM   = 0x3,          /* mask of all permission types in open mode */
18         Nram    = 512,
19         OffsetSize = 4,         /* size in bytes of an offset */
20         CacheSize = 20,
21 };
22
23 typedef struct Fid Fid;
24 typedef struct Path Path;
25 typedef struct Sac Sac;
26 typedef struct Cache Cache;
27
28 struct Fid
29 {
30         short busy;
31         short open;
32         int fid;
33         char *user;
34         Qid qid;
35         Sac *sac;
36         Fid     *next;
37 };
38
39 struct Sac
40 {
41         SacDir;
42         Path *path;
43 };
44
45 struct Path
46 {
47         int ref;
48         Path *up;
49         long blocks;
50         int entry;
51         int nentry;
52 };
53
54 struct Cache
55 {
56         long block;
57         ulong age;
58         uchar *data;
59 };
60
61 enum
62 {
63         Pexec =         1,
64         Pwrite =        2,
65         Pread =         4,
66         Pother =        1,
67         Pgroup =        8,
68         Powner =        64,
69 };
70
71 Fid     *fids;
72 uchar *data;
73 int     mfd[2];
74 char    user[NAMELEN];
75 char    mdata[MAXMSG+MAXFDATA];
76 Fcall   rhdr;
77 Fcall   thdr;
78 int blocksize;
79 Sac root;
80 Cache cache[CacheSize];
81 ulong cacheage;
82
83 Fid *   newfid(int);
84 void    sacstat(SacDir*, char*);
85 void    error(char*);
86 void    io(void);
87 void    *erealloc(void*, ulong);
88 void    *emalloc(ulong);
89 void    usage(void);
90 int     perm(Fid*, Sac*, int);
91 ulong   getl(void *p);
92 void    init(char*);
93 Sac     *saccpy(Sac *s);
94 Sac *saclookup(Sac *s, char *name);
95 int sacdirread(Sac *s, char *p, long off, long cnt);
96 void loadblock(void *buf, uchar *offset, int blocksize);
97 void sacfree(Sac*);
98
99 char    *rflush(Fid*), *rnop(Fid*), *rsession(Fid*),
100         *rattach(Fid*), *rclone(Fid*), *rwalk(Fid*),
101         *rclwalk(Fid*), *ropen(Fid*), *rcreate(Fid*),
102         *rread(Fid*), *rwrite(Fid*), *rclunk(Fid*),
103         *rremove(Fid*), *rstat(Fid*), *rwstat(Fid*);
104
105 char    *(*fcalls[])(Fid*) = {
106         [Tflush]        rflush,
107         [Tsession]      rsession,
108         [Tnop]          rnop,
109         [Tattach]       rattach,
110         [Tclone]        rclone,
111         [Twalk]         rwalk,
112         [Tclwalk]       rclwalk,
113         [Topen]         ropen,
114         [Tcreate]       rcreate,
115         [Tread]         rread,
116         [Twrite]        rwrite,
117         [Tclunk]        rclunk,
118         [Tremove]       rremove,
119         [Tstat]         rstat,
120         [Twstat]        rwstat,
121 };
122
123 char    Eperm[] =       "permission denied";
124 char    Enotdir[] =     "not a directory";
125 char    Enoauth[] =     "no authentication in ramfs";
126 char    Enotexist[] =   "file does not exist";
127 char    Einuse[] =      "file in use";
128 char    Eexist[] =      "file exists";
129 char    Enotowner[] =   "not owner";
130 char    Eisopen[] =     "file already open for I/O";
131 char    Excl[] =        "exclusive use file already open";
132 char    Ename[] =       "illegal name";
133 char    Erdonly[] =     "read only file system";
134
135 int debug;
136
137 void
138 notifyf(void *a, char *s)
139 {
140         USED(a);
141         if(strncmp(s, "interrupt", 9) == 0)
142                 noted(NCONT);
143         noted(NDFLT);
144 }
145
146 void
147 main(int argc, char *argv[])
148 {
149         char *defmnt;
150         int p[2];
151         char buf[12];
152         int fd;
153         int stdio = 0;
154
155         defmnt = "/n/c:";
156         ARGBEGIN{
157         case 'd':
158                 debug = 1;
159                 break;
160         case 'i':
161                 defmnt = 0;
162                 stdio = 1;
163                 mfd[0] = 0;
164                 mfd[1] = 1;
165                 break;
166         case 's':
167                 defmnt = 0;
168                 break;
169         case 'm':
170                 defmnt = ARGF();
171                 break;
172         default:
173                 usage();
174         }ARGEND
175
176         if(argc != 1)
177                 usage();
178
179         init(argv[0]);
180         
181         if(pipe(p) < 0)
182                 error("pipe failed");
183         if(!stdio){
184                 mfd[0] = p[0];
185                 mfd[1] = p[0];
186                 if(defmnt == 0){
187                         fd = create("#s/sacfs", OWRITE, 0666);
188                         if(fd < 0)
189                                 error("create of /srv/sacfs failed");
190                         sprint(buf, "%d", p[1]);
191                         if(write(fd, buf, strlen(buf)) < 0)
192                                 error("writing /srv/sacfs");
193                 }
194         }
195
196         if(debug)
197                 fmtinstall('F', fcallconv);
198         switch(rfork(RFFDG|RFPROC|RFNAMEG|RFNOTEG)){
199         case -1:
200                 error("fork");
201         case 0:
202                 close(p[1]);
203                 io();
204                 break;
205         default:
206                 close(p[0]);    /* don't deadlock if child fails */
207                 if(defmnt && mount(p[1], defmnt, MREPL|MCREATE, "") < 0)
208                         error("mount failed");
209         }
210         exits(0);
211 }
212
213 char*
214 rnop(Fid *f)
215 {
216         USED(f);
217         return 0;
218 }
219
220 char*
221 rsession(Fid *unused)
222 {
223         Fid *f;
224
225         USED(unused);
226
227         for(f = fids; f; f = f->next)
228                 if(f->busy)
229                         rclunk(f);
230         memset(thdr.authid, 0, sizeof(thdr.authid));
231         memset(thdr.authdom, 0, sizeof(thdr.authdom));
232         memset(thdr.chal, 0, sizeof(thdr.chal));
233         return 0;
234 }
235
236 char*
237 rflush(Fid *f)
238 {
239         USED(f);
240         return 0;
241 }
242
243 char*
244 rattach(Fid *f)
245 {
246         /* no authentication! */
247         f->busy = 1;
248         f->qid = (Qid){getl(root.qid), 0};
249         f->sac = saccpy(&root);
250         thdr.qid = f->qid;
251         if(rhdr.uname[0])
252                 f->user = strdup(rhdr.uname);
253         else
254                 f->user = "none";
255         return 0;
256 }
257
258 char*
259 rclone(Fid *f)
260 {
261         Fid *nf;
262
263         if(f->open)
264                 return Eisopen;
265         if(f->busy == 0)
266                 return Enotexist;
267         nf = newfid(rhdr.newfid);
268         nf->busy = 1;
269         nf->open = 0;
270         nf->qid = f->qid;
271         nf->sac = saccpy(f->sac);
272         nf->user = strdup(f->user);
273         return 0;
274 }
275
276 char*
277 rwalk(Fid *f)
278 {
279         char *name;
280         Sac *sac;
281
282         if((f->qid.path & CHDIR) == 0)
283                 return Enotdir;
284         if(f->busy == 0)
285                 return Enotexist;
286         name = rhdr.name;
287         sac = f->sac;
288         if(strcmp(name, ".") == 0){
289                 thdr.qid = f->qid;
290                 return 0;
291         }
292         if(!perm(f, sac, Pexec))
293                 return Eperm;
294         sac = saclookup(sac, name);
295         if(sac == nil)
296                 return Enotexist;
297         f->sac = sac;
298         f->qid = (Qid){getl(sac->qid), 0};
299         thdr.qid = f->qid;
300         return 0;
301 }
302
303 char *
304 rclwalk(Fid *f)
305 {
306         Fid *nf;
307         char *err;
308
309         nf = newfid(rhdr.newfid);
310         nf->busy = 1;
311         nf->sac = saccpy(f->sac);
312         nf->user = strdup(f->user);
313         if(err = rwalk(nf))
314                 rclunk(nf);
315         return err;
316 }
317
318 char *
319 ropen(Fid *f)
320 {
321         int mode, trunc;
322
323         if(f->open)
324                 return Eisopen;
325         if(f->busy == 0)
326                 return Enotexist;
327         mode = rhdr.mode;
328         if(f->qid.path & CHDIR){
329                 if(mode != OREAD)
330                         return Eperm;
331                 thdr.qid = f->qid;
332                 return 0;
333         }
334         if(mode & ORCLOSE)
335                 return Erdonly;
336         trunc = mode & OTRUNC;
337         mode &= OPERM;
338         if(mode==OWRITE || mode==ORDWR || trunc)
339                 return Erdonly;
340         if(mode==OREAD)
341                 if(!perm(f, f->sac, Pread))
342                         return Eperm;
343         if(mode==OEXEC)
344                 if(!perm(f, f->sac, Pexec))
345                         return Eperm;
346         thdr.qid = f->qid;
347         f->open = 1;
348         return 0;
349 }
350
351 char *
352 rcreate(Fid *f)
353 {
354         if(f->open)
355                 return Eisopen;
356         if(f->busy == 0)
357                 return Enotexist;
358         return Erdonly;
359 }
360
361 char*
362 rread(Fid *f)
363 {
364         Sac *sac;
365         char *buf, *buf2;
366         long off;
367         int n, cnt, i, j;
368         uchar *blocks;
369         long length;
370
371         if(f->busy == 0)
372                 return Enotexist;
373         sac = f->sac;
374         thdr.count = 0;
375         off = rhdr.offset;
376         buf = thdr.data;
377         cnt = rhdr.count;
378         if(f->qid.path & CHDIR){
379                 cnt = (rhdr.count/DIRLEN)*DIRLEN;
380                 if(off%DIRLEN)
381                         return "i/o error";
382                 thdr.count = sacdirread(sac, buf, off, cnt);
383                 return 0;
384         }
385         length = getl(sac->length);
386         if(off >= length) {
387                 rhdr.count = 0;
388                 return 0;
389         }
390         if(cnt > length-off)
391                 cnt = length-off;
392         thdr.count = cnt;
393         if(cnt == 0)
394                 return 0;
395         blocks = data + getl(sac->blocks);
396         buf2 = malloc(blocksize);
397         if(buf2 == nil)
398                 sysfatal("malloc failed");
399         while(cnt > 0) {
400                 i = off/blocksize;
401                 n = blocksize;
402                 if(n > length-i*blocksize)
403                         n = length-i*blocksize;
404                 loadblock(buf2, blocks+i*OffsetSize, n);
405                 j = off-i*blocksize;
406                 n = blocksize-j;
407                 if(n > cnt)
408                         n = cnt;
409                 memmove(buf, buf2+j, n);
410                 cnt -= n;
411                 off += n;
412                 buf += n;
413         }
414         free(buf2);
415         return 0;
416 }
417
418 char*
419 rwrite(Fid *f)
420 {
421         if(f->busy == 0)
422                 return Enotexist;
423         return Erdonly;
424 }
425
426 char *
427 rclunk(Fid *f)
428 {
429         f->busy = 0;
430         f->open = 0;
431         free(f->user);
432         sacfree(f->sac);
433         return 0;
434 }
435
436 char *
437 rremove(Fid *f)
438 {
439         f->busy = 0;
440         f->open = 0;
441         free(f->user);
442         sacfree(f->sac);
443         return Erdonly;
444 }
445
446 char *
447 rstat(Fid *f)
448 {
449         if(f->busy == 0)
450                 return Enotexist;
451         sacstat(f->sac, thdr.stat);
452         return 0;
453 }
454
455 char *
456 rwstat(Fid *f)
457 {
458         if(f->busy == 0)
459                 return Enotexist;
460         return Erdonly;
461 }
462
463 Sac*
464 saccpy(Sac *s)
465 {
466         Sac *ss;
467         
468         ss = emalloc(sizeof(Sac));
469         *ss = *s;
470         if(ss->path)
471                 ss->path->ref++;
472         return ss;
473 }
474
475 Path *
476 pathalloc(Path *p, long blocks, int entry, int nentry)
477 {
478         Path *pp = emalloc(sizeof(Path));
479
480         pp->ref = 1;
481         pp->blocks = blocks;
482         pp->entry = entry;
483         pp->nentry = nentry;
484         pp->up = p;
485         return pp;
486 }
487
488 void
489 pathfree(Path *p)
490 {
491         if(p == nil)
492                 return;
493         p->ref--;
494         if(p->ref > 0)
495                 return;
496
497         pathfree(p->up);
498         free(p);
499 }
500
501
502 void
503 sacfree(Sac *s)
504 {
505         pathfree(s->path);
506         free(s);
507 }
508
509 void
510 sacstat(SacDir *s, char *buf)
511 {
512         Dir dir;
513
514         memmove(dir.name, s->name, NAMELEN);
515         dir.qid = (Qid){getl(s->qid), 0};
516         dir.mode = getl(s->mode);
517         dir.length = getl(s->length);
518         if(dir.mode &CHDIR)
519                 dir.length *= DIRLEN;
520         memmove(dir.uid, s->uid, NAMELEN);
521         memmove(dir.gid, s->gid, NAMELEN);
522         dir.atime = getl(s->atime);
523         dir.mtime = getl(s->mtime);
524         convD2M(&dir, buf);
525 }
526
527 void
528 loadblock(void *buf, uchar *offset, int blocksize)
529 {
530         long block, n;
531         ulong age;
532         int i, j;
533
534         block = getl(offset);
535         if(block < 0) {
536                 block = -block;
537                 cacheage++;
538                 // age has wraped
539                 if(cacheage == 0) {
540                         for(i=0; i<CacheSize; i++)
541                                 cache[i].age = 0;
542                 }
543                 j = 0;
544                 age = cache[0].age;
545                 for(i=0; i<CacheSize; i++) {
546                         if(cache[i].age < age) {
547                                 age = cache[i].age;
548                                 j = i;
549                         }
550                         if(cache[i].block != block)
551                                 continue;
552                         memmove(buf, cache[i].data, blocksize);
553                         cache[i].age = cacheage;
554                         return;
555                 }
556
557                 n = getl(offset+OffsetSize);
558                 if(n < 0)
559                         n = -n;
560                 n -= block;
561                 if(unsac(buf, data+block, blocksize, n)<0)
562                         sysfatal("unsac failed!");
563                 memmove(cache[j].data, buf, blocksize);
564                 cache[j].age = cacheage;
565                 cache[j].block = block;
566         } else {
567                 memmove(buf, data+block, blocksize);
568         }
569 }
570
571 Sac*
572 sacparent(Sac *s)
573 {
574         uchar *blocks;
575         SacDir *buf;
576         int per, i, n;
577         Path *p;
578
579         p = s->path;
580         if(p == nil || p->up == nil) {
581                 pathfree(p);
582                 *s = root;
583                 return s;
584         }
585         p = p->up;
586
587         blocks = data + p->blocks;
588         per = blocksize/sizeof(SacDir);
589         i = p->entry/per;
590         n = per;
591         if(n > p->nentry-i*per)
592                 n = p->nentry-i*per;
593         buf = emalloc(per*sizeof(SacDir));
594         loadblock(buf, blocks + i*OffsetSize, n*sizeof(SacDir));
595         s->SacDir = buf[p->entry-i*per];
596         free(buf);
597         p->ref++;
598         pathfree(s->path);
599         s->path = p;
600         return s;
601 }
602
603 int
604 sacdirread(Sac *s, char *p, long off, long cnt)
605 {
606         uchar *blocks;
607         SacDir *buf;
608         int iblock, per, i, j, ndir, n;
609
610         blocks = data + getl(s->blocks);
611         per = blocksize/sizeof(SacDir);
612         ndir = getl(s->length);
613         off /= DIRLEN;
614         cnt /= DIRLEN;
615         if(off >= ndir)
616                 return 0;
617         if(cnt > ndir-off)
618                 cnt = ndir-off;
619         iblock = -1;
620         buf = emalloc(per*sizeof(SacDir));
621         for(i=off; i<off+cnt; i++) {
622                 j = i/per;
623                 if(j != iblock) {
624                         n = per;
625                         if(n > ndir-j*per)
626                                 n = ndir-j*per;
627                         loadblock(buf, blocks + j*OffsetSize, n*sizeof(SacDir));
628                         iblock = j;
629                 }
630                 sacstat(buf+i-j*per, p);
631                 p += DIRLEN;
632         }
633         free(buf);
634         return cnt*DIRLEN;
635 }
636
637 Sac*
638 saclookup(Sac *s, char *name)
639 {
640         int ndir;
641         int top, bot, i, j, k, n, per;
642         uchar *blocks;
643         SacDir *buf;
644         int iblock;
645         SacDir *sd;
646         
647         if(strcmp(name, "..") == 0)
648                 return sacparent(s);
649         blocks = data + getl(s->blocks);
650         per = blocksize/sizeof(SacDir);
651         ndir = getl(s->length);
652         buf = malloc(per*sizeof(SacDir));
653         if(buf == nil)
654                 sysfatal("malloc failed");
655         iblock = -1;
656
657         if(1) {
658                 // linear search
659                 for(i=0; i<ndir; i++) {
660                         j = i/per;
661                         if(j != iblock) {
662                                 n = per;
663                                 if(n > ndir-j*per)
664                                         n = ndir-j*per;
665                                 loadblock(buf, blocks + j*OffsetSize, n*sizeof(SacDir));
666                                 iblock = j;
667                         }
668                         sd = buf+i-j*per;
669                         k = strcmp(name, sd->name);
670                         if(k == 0) {
671                                 s->path = pathalloc(s->path, getl(s->blocks), i, ndir);
672                                 s->SacDir = *sd;
673                                 free(buf);
674                                 return s;
675                         }
676                 }
677                 free(buf);
678                 return 0;
679         }
680
681         // binary search
682         top = ndir;
683         bot = 0;
684         while(bot != top){
685                 i = (bot+top)>>1;
686                 j = i/per;
687                 if(j != iblock) {
688                         n = per;
689                         if(n > ndir-j*per)
690                                 n = ndir-j*per;
691                         loadblock(buf, blocks + j*OffsetSize, n*sizeof(SacDir));
692                         iblock = j;
693                 }
694                 j *= per;
695                 sd = buf+i-j;
696                 k = strcmp(name, sd->name);
697                 if(k == 0) {
698                         s->path = pathalloc(s->path, getl(s->blocks), i, ndir);
699                         s->SacDir = *sd;
700                         free(buf);
701                 }
702                 if(k < 0) {
703                         top = i;
704                         sd = buf;
705                         if(strcmp(name, sd->name) < 0)
706                                 top = j;
707                 } else {
708                         bot = i+1;
709                         if(ndir-j < per)
710                                 i = ndir-j;
711                         else
712                                 i = per;
713                         sd = buf+i-1;
714                         if(strcmp(name, sd->name) > 0)
715                                 bot = j+i;
716                 }
717         }
718         return 0;
719 }
720
721 Fid *
722 newfid(int fid)
723 {
724         Fid *f, *ff;
725
726         ff = 0;
727         for(f = fids; f; f = f->next)
728                 if(f->fid == fid)
729                         return f;
730                 else if(!ff && !f->busy)
731                         ff = f;
732         if(ff){
733                 ff->fid = fid;
734                 return ff;
735         }
736         f = emalloc(sizeof *f);
737         memset(f, 0 , sizeof(Fid));
738         f->fid = fid;
739         f->next = fids;
740         fids = f;
741         return f;
742 }
743
744 void
745 io(void)
746 {
747         char *err;
748         int n;
749
750         for(;;){
751                 /*
752                  * reading from a pipe or a network device
753                  * will give an error after a few eof reads
754                  * however, we cannot tell the difference
755                  * between a zero-length read and an interrupt
756                  * on the processes writing to us,
757                  * so we wait for the error
758                  */
759                 n = read(mfd[0], mdata, sizeof mdata);
760                 if(n == 0)
761                         continue;
762                 if(n < 0)
763                         error("mount read");
764                 if(convM2S(mdata, &rhdr, n) == 0)
765                         continue;
766
767                 if(debug)
768                         fprint(2, "sacfs:<-%F\n", &rhdr);
769
770                 thdr.data = mdata + MAXMSG;
771                 if(!fcalls[rhdr.type])
772                         err = "bad fcall type";
773                 else
774                         err = (*fcalls[rhdr.type])(newfid(rhdr.fid));
775                 if(err){
776                         thdr.type = Rerror;
777                         strncpy(thdr.ename, err, ERRLEN);
778                 }else{
779                         thdr.type = rhdr.type + 1;
780                         thdr.fid = rhdr.fid;
781                 }
782                 thdr.tag = rhdr.tag;
783                 if(debug)
784                         fprint(2, "ramfs:->%F\n", &thdr);/**/
785                 n = convS2M(&thdr, mdata);
786                 if(write(mfd[1], mdata, n) != n)
787                         error("mount write");
788         }
789 }
790
791 int
792 perm(Fid *f, Sac *s, int p)
793 {
794         ulong perm = getl(s->mode);
795         if((p*Pother) & perm)
796                 return 1;
797         if(strcmp(f->user, s->gid)==0 && ((p*Pgroup) & perm))
798                 return 1;
799         if(strcmp(f->user, s->uid)==0 && ((p*Powner) & perm))
800                 return 1;
801         return 0;
802 }
803
804 void
805 init(char *file)
806 {
807         SacHeader *hdr;
808         Dir dir;
809         int fd;
810         int i;
811         uchar *p;
812
813         notify(notifyf);
814         strcpy(user, getuser());
815
816
817         if(dirstat(file, &dir) < 0)
818                 error("bad file");
819         data = emalloc(dir.length);
820         fd = open(file, OREAD);
821         if(fd < 0)
822                 error("opening file");
823         if(read(fd, data, dir.length) < dir.length)
824                 error("reading file");
825         hdr = (SacHeader*)data;
826         if(getl(hdr->magic) != Magic)
827                 error("bad magic");
828         if(getl(hdr->length) != (ulong)(dir.length))
829                 error("bad length");
830         blocksize = getl(hdr->blocksize);
831         root.SacDir = *(SacDir*)(data + sizeof(SacHeader));
832         p = malloc(CacheSize*blocksize);
833         if(p == nil)
834                 error("allocating cache");
835         for(i=0; i<CacheSize; i++) {
836                 cache[i].data = p;
837                 p += blocksize;
838         }
839 }
840
841 void
842 error(char *s)
843 {
844         fprint(2, "%s: %s: %r\n", argv0, s);
845         exits(s);
846 }
847
848 void *
849 emalloc(ulong n)
850 {
851         void *p;
852
853         p = malloc(n);
854         if(!p)
855                 error("out of memory");
856         return p;
857 }
858
859 void *
860 erealloc(void *p, ulong n)
861 {
862         p = realloc(p, n);
863         if(!p)
864                 error("out of memory");
865         return p;
866 }
867
868 void
869 usage(void)
870 {
871         fprint(2, "usage: %s [-i infd outfd] [-s] [-m mountpoint] sacfsfile\n", argv0);
872         exits("usage");
873 }
874
875 ulong
876 getl(void *p)
877 {
878         uchar *a = p;
879
880         return (a[0]<<24) | (a[1]<<16) | (a[2]<<8) | a[3];
881 }
882