8 typedef struct Args Args;
15 typedef struct Dfile Dfile;
16 typedef struct Fid Fid;
17 typedef struct File File;
19 typedef struct Request Request;
20 typedef struct Symbol Symbol;
21 typedef struct Tardir Tardir;
23 extern int threadrforkflag = RFNAMEG;
26 Nstat = 1024, /* plenty for this application */
27 MAXSIZE = 8192+IOHDRSZ,
30 int messagesize = MAXSIZE;
38 write(2, "depend: ", 8);
40 vseprint(buf, buf+1024, fmt, arg);
42 write(2, buf, strlen(buf));
55 Symbol *next; /* hash list chaining */
57 int fno; /* file symbol is defined in */
67 uchar *refvec; /* files resolving the references */
68 uint len; /* length of file */
69 uint tarlen; /* length of tar file */
81 int use; /* use count */
82 int old; /* true if this is an superceded dfile */
84 File *file; /* files */
85 int nfile; /* number of files */
86 int flen; /* length of file table */
88 Symbol **dhash; /* hash table of symbols */
89 int hlen; /* length of hash table */
91 Dfile *next; /* hash chain */
92 char *path; /* path name of dependency file */
93 Qid qid; /* qid of the dependency file */
124 Tblocksize= 512, /* tar block size */
125 Tnamesize= 100, /* tar name size */
130 char name[Tnamesize];
138 char linkname[Tnamesize];
145 int fd; /* to kernel mount point */
160 extern void fsrun(void*);
161 extern Fid* fsgetfid(Fs*, int);
162 extern void fsputfid(Fs*, Fid*);
163 extern void fsreply(Fs*, Request*, char*);
164 extern void fsversion(Fs*, Request*, Fid*);
165 extern void fsauth(Fs*, Request*, Fid*);
166 extern void fsflush(Fs*, Request*, Fid*);
167 extern void fsattach(Fs*, Request*, Fid*);
168 extern void fswalk(Fs*, Request*, Fid*);
169 extern void fsopen(Fs*, Request*, Fid*);
170 extern void fscreate(Fs*, Request*, Fid*);
171 extern void fsread(Fs*, Request*, Fid*);
172 extern void fswrite(Fs*, Request*, Fid*);
173 extern void fsclunk(Fs*, Request*, Fid*);
174 extern void fsremove(Fs*, Request*, Fid*);
175 extern void fsstat(Fs*, Request*, Fid*);
176 extern void fswstat(Fs*, Request*, Fid*);
178 void (*fcall[])(Fs*, Request*, Fid*) =
181 [Tversion] fsversion,
195 char Eperm[] = "permission denied";
196 char Eexist[] = "file does not exist";
197 char Enotdir[] = "not a directory";
198 char Eisopen[] = "file already open";
199 char Enofid[] = "no such fid";
200 char mallocerr[] = "malloc: %r";
201 char Etoolong[] = "name too long";
203 char *dependlog = "depend";
206 Dfile *dfhash[Ndfhash]; /* dependency file hash */
207 QLock dfhlock[Ndfhash];
210 Request* allocreq(int);
212 void releasedf(Dfile*);
213 Symbol* dfsearch(Dfile*, char*);
214 void dfresolve(Dfile*, int);
215 char* mkpath(char*, char*);
216 int mktar(Dfile*, Symbol*, uchar*, uint, int);
217 void closetar(Dfile*, Symbol*);
232 erealloc(void *ReallocP, int ReallocN)
237 ReallocP = emalloc(ReallocN);
238 else if(!(ReallocP = realloc(ReallocP, ReallocN)))
239 fatal("unable to allocate %d bytes",ReallocN);
250 d = d0 = emalloc(strlen(s)+1);
257 * mount the user interface and start one request processor
276 fmtinstall('F', fcallfmt);
284 fprint(2, "usage: %s [-d] svc-name directory\n", argv0);
287 snprint(service, sizeof service, "#s/%s", argv[0]);
288 if(argv[1][0] != '/')
289 fatal("directory must be rooted");
292 fatal("opening pipe: %r");
294 /* Typically mounted before /srv exists */
295 srv = create(service, OWRITE, 0666);
298 fprint(srv, "%d", pfd[1]);
302 time(nil); /* open fd for time before losing / */
303 if(bind(argv[1], "/", MREPL) == -1)
304 fatal("can't bind %s to /", argv[1]);
306 fs = emalloc(sizeof(Fs));
309 fsarg.root = argv[1];
310 proccreate(fsrun, &fsarg, 16*1024);
311 proccreate(fsrun, &fsarg, 16*1024);
317 threadmain(int argc, char *argv[])
324 proccreate(realmain, &args, 16*1024);
328 mkpath(char *dir, char *file)
333 len = strlen(dir) + 1;
335 len += strlen(file) + 1;
338 sprint(path, "%s/%s", dir, file);
340 sprint(path, "%s", dir);
361 fatal("root %s inaccessible: %r", root);
362 fs->rootqid = d->qid;
366 r = allocreq(messagesize);
368 n = read9pmsg(fs->fd, r->buf, messagesize);
370 threadexitsall("unmounted");
372 fatal("mount read: %r");
373 if(convM2S(r->buf, n, &r->f) != n)
374 fatal("convM2S format error: %r");
377 f = fsgetfid(fs, r->f.fid);
380 fprint(2, "%F path %llux\n", &r->f, f->qid.path);
384 (*fcall[t])(fs, r, f);
390 * any request that can get queued for a delayed reply
393 allocreq(int bufsize)
397 r = emalloc(sizeof(Request)+bufsize);
403 fsgetfid(Fs *fs, int fid)
408 for(f = fs->hash[fid%Nfidhash]; f; f = f->next){
416 nf = emalloc(sizeof(Fid));
417 nf->next = fs->hash[fid%Nfidhash];
418 fs->hash[fid%Nfidhash] = nf;
427 fsputfid(Fs *fs, Fid *f)
436 for(l = &fs->hash[f->fid%Nfidhash]; nf = *l; l = &nf->next)
446 fsreply(Fs *fs, Request *r, char *err)
456 fprint(2, "%F path %llux\n", &r->f, r->fid->qid.path);
457 n = convS2M(&r->f, buf, messagesize);
459 fatal("bad convS2M");
460 if(write(fs->fd, buf, n) != n)
466 fsversion(Fs *fs, Request *r, Fid*)
468 if(r->f.msize < 256){
469 fsreply(fs, r, "version: bad message size");
472 if(messagesize > r->f.msize)
473 messagesize = r->f.msize;
474 r->f.msize = messagesize;
475 if(strncmp(r->f.version, "9P", 2) != 0)
476 r->f.version = "unknown";
478 r->f.version = "9P2000";
483 fsauth(Fs *fs, Request *r, Fid*)
485 fsreply(fs, r, "depend: authentication not required");
489 fsflush(Fs*, Request*, Fid*)
494 fsattach(Fs *fs, Request *r, Fid *f)
496 f->qid = fs->rootqid;
497 f->path = strdup("/");
498 f->df = getdf(mkpath(f->path, ".depend"));
500 /* hold down the fid till the clunk */
511 fswalk(Fs *fs, Request *r, Fid *f)
516 char errbuf[ERRMAX], *err;
523 if(f->attached == 0){
524 fsreply(fs, r, Eexist);
528 if(f->fd >= 0 || f->open)
529 fatal("walk of an open file");
532 if(r->f.newfid != r->f.fid){
533 nf = fsgetfid(fs, r->f.newfid);
536 nf->path = strdup(f->path);
546 if(r->f.nwname == 0){
555 path = strdup(f->path);
559 nwname = r->f.nwname;
563 for(; nqid<nwname; nqid++){
564 name = r->f.wname[nqid];
566 if(strcmp(name, ".") == 0){
571 qid[nqid] = qid[nqid-1];
575 if(strcmp(name, "..") == 0){
576 name = strrchr(path, '/');
578 if(name == path) /* at root */
585 errstr(errbuf, sizeof errbuf);
593 lastdf = getdf(mkpath(path, ".depend"));
597 npath = mkpath(path, name);
602 if(d !=nil && (d->qid.type & QTDIR))
606 qid[nqid].type = QTFILE;
610 dp = dfsearch(lastdf, name);
616 if(i > 4 && strcmp(&tmp[i-4], ".tar") == 0){
618 dp = dfsearch(lastdf, tmp);
627 qid[nqid].path = (uvlong)dp;
630 if(nqid == 0 && err == nil)
631 err = "file does not exist";
634 /* for error or partial success, put the cloned fid back*/
635 if(nf!=nil && (err != nil || nqid < nwname)){
642 /* return (possibly short) list of qids */
643 for(i=0; i<nqid; i++)
644 r->f.wqid[i] = qid[i];
647 /* for full success, advance f */
648 if(nqid > 0 && nqid == nwname){
653 f->qid = qid[nqid-1];
654 f->dp = (Symbol*)f->qid.path;
673 fsclone(Fs *fs, Request *r, Fid *f)
677 if(f->attached == 0){
678 fsreply(fs, r, Eexist);
681 nf = fsgetfid(fs, r->f.newfid);
685 nf->path = strdup(f->path);
699 fswalk(Fs *fs, Request *r, Fid *f)
708 if(f->attached == 0){
709 fsreply(fs, r, Enofid);
713 if(f->fd >= 0 || f->open)
714 fatal("walk of an open file");
717 if(strcmp(name, ".") == 0){
721 if(strcmp(name, "..") == 0){
722 name = strrchr(f->path, '/');
730 if(dirstat(f->path, &d) < 0){
733 fsreply(fs, r, errbuf);
736 r->f.qid = f->qid = d.qid;
739 f->df = getdf(mkpath(f->path, ".depend"));
745 path = mkpath(f->path, name);
746 if(dirstat(path, &d) < 0 || (d.qid.path & CHDIR) == 0){
747 dp = dfsearch(f->df, name);
750 if(i > 4 && strcmp(&name[i-4], ".tar") == 0){
752 dp = dfsearch(f->df, name);
756 fsreply(fs, r, Eexist);
761 d.qid.path = (uint)dp;
768 if(d.qid.path & CHDIR){
770 f->df = getdf(mkpath(f->path, ".depend"));
773 r->f.qid = f->qid = d.qid;
778 fsopen(Fs *fs, Request *r, Fid *f)
783 if(f->attached == 0){
784 fsreply(fs, r, Enofid);
788 fsreply(fs, r, Eisopen);
792 mode = r->f.mode & 3;
794 fsreply(fs, r, Eperm);
798 if(f->qid.type & QTDIR){
799 f->fd = open(f->path, OREAD);
801 errstr(errbuf, sizeof errbuf);
802 fsreply(fs, r, errbuf);
813 fscreate(Fs *fs, Request *r, Fid*)
815 fsreply(fs, r, Eperm);
819 fsread(Fs *fs, Request *r, Fid *f)
826 if(f->attached == 0){
827 fsreply(fs, r, Enofid);
830 if((int)r->f.count < 0){
831 fsreply(fs, r, "bad read count");
835 if(f->qid.type & QTDIR){
838 f->ndir = dirreadall(f->fd, &f->dir);
843 if(r->f.offset == 0) /* seeking to zero is permitted */
845 for(; f->dirindex < f->ndir; f->dirindex++){
846 if((f->dir[f->dirindex].qid.type & QTDIR) == 0)
848 len = convD2M(&f->dir[f->dirindex], r->buf+n, r->f.count-n);
854 skip = f->dirindex - f->ndir; /* # depend records already read */
857 for(i = 0; i < f->df->hlen; i++)
858 for(dp = f->df->dhash[i]; dp; dp = dp->next){
861 snprint(buf, sizeof buf, "%s.tar", dp->sym);
867 d.qid.path = (uvlong)dp;
869 d.length = f->df->file[dp->fno].tarlen;
873 len = convD2M(&d, r->buf + n, r->f.count - n);
881 n = mktar(f->df, f->dp, r->buf, r->f.offset, r->f.count);
884 r->f.data = (char*)r->buf;
890 fswrite(Fs *fs, Request *r, Fid*)
892 fsreply(fs, r, Eperm);
896 fsclunk(Fs *fs, Request *r, Fid *f)
898 if(f->attached == 0){
899 fsreply(fs, r, Enofid);
907 if((f->qid.type & QTDIR) == 0)
908 closetar(f->df, f->dp);
920 fsremove(Fs *fs, Request *r, Fid*)
922 fsreply(fs, r, Eperm);
926 fsstat(Fs *fs, Request *r, Fid *f)
932 uchar statbuf[Nstat];
934 if(f->qid.type & QTDIR)
935 n = stat(f->path, statbuf, sizeof statbuf);
943 d.qid.path = (uvlong)dp;
945 d.length = f->df->file[dp->fno].tarlen;
949 n = convD2M(&d, statbuf, sizeof statbuf);
952 errstr(err, sizeof err);
962 fswstat(Fs *fs, Request *r, Fid*)
964 fsreply(fs, r, Eperm);
971 shash(char *str, int len)
977 for(val = str; *val; val++)
978 hash = (hash*13) + *val-'a';
983 * free info about a dependency file
998 unlock(df); /* we're no longer referenced */
999 for(i = 0; i < df->nfile; i++)
1000 free(df->file[i].name);
1001 free(df->file[i].refvec);
1005 for(i = 0; i < df->hlen; i++)
1006 for(dp = df->dhash[i]; dp != nil; dp = next){
1018 * crack a dependency file
1021 newsym(char *name, int fno, Symbol **l)
1025 dp = emalloc(sizeof(Symbol));
1026 dp->sym = strdup(name);
1034 awk(Biobuf *b, char **field, int n)
1039 while(line = Brdline(b, '\n')){
1040 line[Blinelen(b)-1] = 0;
1041 while(*line == ' ' || *line == '\t')
1043 for(i = 0; i < n; i++){
1044 if(*line == 0 || *line == '#')
1047 while(*line && *line != ' ' && *line != '\t')
1049 while(*line == ' ' || *line == '\t')
1060 crackdf(Dfile *df, Biobuf *b, uint len, char *dpath)
1065 int n, inc, ok, npath;
1066 Symbol **l, *dp, *next;
1072 df->file = emalloc(df->flen*sizeof(File));
1075 df->hlen = 1 + len/8;
1076 df->dhash = emalloc(df->hlen*sizeof(Symbol*));
1079 while((n = awk(b, field, 3)) > 0){
1086 if(df->flen == df->nfile){
1088 df->file = realloc(df->file, df->flen*sizeof(File));
1091 memset(&df->file[df->nfile], 0, inc*sizeof(File));
1093 f = &df->file[df->nfile++];
1094 f->name = strdup(name);
1096 /* fall through and define as a symbol */
1100 newsym(name, df->nfile-1, &(df->dhash[shash(name, df->hlen)]));
1110 ef = &df->file[df->nfile];
1112 /* stat the files to get sizes */
1113 npath = strlen(dpath);
1114 if(npath+1+1 >= sizeof path)
1116 memmove(path, dpath, npath+1); /* include NUL */
1117 name = strrchr(path, '/') + 1;
1118 for(f = df->file; f < ef; f++){
1119 n = strlen(f->name);
1120 if(npath+1+n+3+1 > sizeof path)
1122 memmove(name, f->name, n+1); /* include NUL */
1123 ok = access(path, AEXIST);
1125 strcpy(name+n, ".Z");
1126 ok = access(path, AEXIST);
1128 strcpy(name+n, ".gz");
1129 ok = access(path, AEXIST);
1134 f->name = strdup(name);
1142 f->mtime = d->mtime;
1152 /* resolve all file references */
1153 for(f = df->file; f < ef; f++)
1154 dfresolve(df, f-df->file);
1156 /* free the referenced symbols, don't need them anymore */
1157 for(f = df->file; f < ef; f++){
1158 f->tarlen += 2*Tblocksize; /* tars trailing 0 blocks */
1159 for(dp = f->ref; dp != nil; dp = next){
1168 * get a cracked dependency file
1179 i = shash(path, Ndfhash);
1183 for(df = *l; df; df = *l){
1184 if(strcmp(path, df->path) == 0)
1191 if(d!=nil && d->qid.type == df->qid.type && d->qid.vers == df->qid.vers && d->qid.vers == df->qid.vers){
1202 fd = open(path, OREAD);
1203 if(d == nil || fd < 0){
1208 df = emalloc(sizeof(*df));
1209 b = emalloc(sizeof(Biobuf));
1211 Binit(b, fd, OREAD);
1214 crackdf(df, b, d->length, path);
1229 * stop using a dependency file. Free it if it is no longer linked in.
1232 releasedf(Dfile *df)
1241 /* remove from hash chain */
1242 i = shash(df->path, Ndfhash);
1248 if(df->old == 0 || df->use > 0){
1253 for(d = *l; d; d = *l){
1263 /* now we know it is unreferenced, remove it */
1268 * search a dependency file for a symbol
1271 dfsearch(Dfile *df, char *name)
1277 for(dp = df->dhash[shash(name, df->hlen)]; dp; dp = dp->next)
1278 if(strcmp(dp->sym, name) == 0)
1284 * resolve a single file into a vector of referenced files and the sum of their
1287 /* set a bit in the referenced file vector */
1289 set(uchar *vec, int fno)
1291 if(vec[fno/8] & (1<<(fno&7)))
1293 vec[fno/8] |= 1<<(fno&7);
1296 /* merge two referenced file vectors */
1298 merge(uchar *vec, uchar *ovec, int nfile)
1300 nfile = (nfile+7)/8;
1305 res(Dfile *df, uchar *vec, int fno)
1313 return 0; /* already set */
1314 if(f->refvec != nil){
1315 merge(vec, f->refvec, df->nfile); /* we've descended here before */
1320 for(rp = f->ref; rp; rp = rp->next){
1321 dp = dfsearch(df, rp->sym);
1324 len += res(df, vec, dp->fno);
1326 return len + Tblocksize + ((f->len + Tblocksize - 1)/Tblocksize)*Tblocksize;
1329 dfresolve(Dfile *df, int fno)
1335 vec = emalloc((df->nfile+7)/8);
1336 f->tarlen = res(df, vec, fno);
1341 * make the tar directory block for a file
1351 p = emalloc(Tblocksize);
1354 strcpy(tp->name, f->name);
1355 sprint(tp->mode, "%6o ", f->mode & 0777);
1356 sprint(tp->uid, "%6o ", 0);
1357 sprint(tp->gid, "%6o ", 0);
1358 sprint(tp->size, "%11o ", f->len);
1359 sprint(tp->mtime, "%11o ", f->mtime);
1361 /* calculate checksum */
1362 memset(tp->chksum, ' ', sizeof(tp->chksum));
1364 ep = p + Tblocksize;
1365 for (cp = p; cp < ep; cp++)
1367 sprint(tp->chksum, "%6o", sum);
1376 getfile(Dfile *df, File *f)
1379 char path[512], *name;
1384 name = strrchr(df->path, '/') + 1;
1385 n = snprint(path, sizeof path, "%.*s/%s",
1386 utfnlen(df->path, name-df->path), df->path,
1388 if(n >= sizeof path - UTFmax){
1389 syslog(0, dependlog, "path name too long: %.20s.../%.20s...", df->path, f->name);
1392 f->fd = open(path, OREAD);
1394 syslog(0, dependlog, "can't open %s: %r", path);
1400 releasefile(File *f)
1417 * return a block of a tar file
1420 mktar(Dfile *df, Symbol *dp, uchar *area, uint offset, int len)
1422 int fd, i, j, n, off;
1427 f = &df->file[dp->fno];
1432 for(i = 0; i < df->nfile && len > 0; i++){
1433 if((vec[i/8] & (1<<(i&7))) == 0)
1437 n = Tblocksize + ((f->len + Tblocksize - 1)/Tblocksize)*Tblocksize;
1443 if(offset < Tblocksize){
1445 if(offset + len > Tblocksize)
1446 j = Tblocksize - offset;
1449 //if(debug)fprint(2, "reading %d bytes dir of %s\n", j, f->name);
1450 memmove(p, buf+offset, j);
1458 off = offset - Tblocksize;
1459 if(off >= 0 && off < f->len){
1460 if(off + len > f->len)
1464 fd = getfile(df, f);
1466 //if(debug)fprint(2, "reading %d bytes from offset %d of %s\n", j, off, f->name);
1467 if(pread(fd, p, j, off) != j)
1468 syslog(0, dependlog, "%r reading %d bytes from offset %d of %s", j, off, f->name);
1478 if(offset + len > n)
1482 //if(debug)fprint(2, "filling %d bytes after %s\n", j, f->name);
1490 /* null blocks at end of tar file */
1491 if(offset < 2*Tblocksize && len > 0){
1492 if(offset + len > 2*Tblocksize)
1493 j = 2*Tblocksize - offset;
1496 //if(debug)fprint(2, "filling %d bytes at end\n", j);
1505 * close the files making up a tar file
1508 closetar(Dfile *df, Symbol *dp)
1514 f = &df->file[dp->fno];
1518 for(i = 0; i < df->nfile; i++){
1519 if((vec[i/8] & (1<<(i&7))) == 0)
1521 closefile(&df->file[i]);