]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/9660srv/9660srv.c
9bootfat: rename open() to fileinit and make it static as its really a internal funct...
[plan9front.git] / sys / src / cmd / 9660srv / 9660srv.c
1 #include <u.h>
2 #include <libc.h>
3 #include <auth.h>
4 #include <fcall.h>
5 #include "dat.h"
6 #include "fns.h"
7 #include "iso9660.h"
8
9 static void     ireset(void);
10 static int      iattach(Xfile*);
11 static void     iclone(Xfile*, Xfile*);
12 static void     iwalkup(Xfile*);
13 static void     iwalk(Xfile*, char*);
14 static void     iopen(Xfile*, int);
15 static void     icreate(Xfile*, char*, long, int);
16 static long     ireaddir(Xfile*, uchar*, long, long);
17 static long     iread(Xfile*, char*, vlong, long);
18 static long     iwrite(Xfile*, char*, vlong, long);
19 static void     iclunk(Xfile*);
20 static void     iremove(Xfile*);
21 static void     istat(Xfile*, Dir*);
22 static void     iwstat(Xfile*, Dir*);
23
24 static char*    nstr(uchar*, int);
25 static char*    rdate(uchar*, int);
26 static int      getcontin(Xdata*, uchar*, uchar**);
27 static int      getdrec(Xfile*, void*);
28 static void     ungetdrec(Xfile*);
29 static int      opendotdot(Xfile*, Xfile*);
30 static int      showdrec(int, int, void*);
31 static long     gtime(uchar*);
32 static long     l16(void*);
33 static long     l32(void*);
34 static void     newdrec(Xfile*, Drec*);
35 static int      rzdir(Xfs*, Dir*, int, Drec*);
36
37 Xfsub   isosub =
38 {
39         ireset, iattach, iclone, iwalkup, iwalk, iopen, icreate,
40         ireaddir, iread, iwrite, iclunk, iremove, istat, iwstat
41 };
42
43 static vlong
44 fakemax(vlong len)
45 {
46         if(len == (1UL << 31) - 1)      /* max. 9660 size? */
47                 len = (1ULL << 63) - 1; /* pretend it's vast */
48         return len;
49 }
50
51 static void
52 ireset(void)
53 {}
54
55 static int
56 iattach(Xfile *root)
57 {
58         Xfs *cd = root->xf;
59         Iobuf *p; Voldesc *v; Isofile *fp; Drec *dp;
60         int fmt, blksize, i, n, l, haveplan9;
61         Iobuf *dirp;
62         uchar dbuf[256];
63         Drec *rd = (Drec *)dbuf;
64         uchar *q, *s;
65
66         dirp = nil;
67         blksize = 0;
68         fmt = 0;
69         dp = nil;
70         haveplan9 = 0;
71         for(i=VOLDESC;i<VOLDESC+100; i++){      /* +100 for sanity */
72                 p = getbuf(cd->d, i);
73                 v = (Voldesc*)(p->iobuf);
74                 if(memcmp(v->byte, "\01CD001\01", 7) == 0){             /* iso */
75                         if(dirp)
76                                 putbuf(dirp);
77                         dirp = p;
78                         fmt = 'z';
79                         dp = (Drec*)v->z.desc.rootdir;
80                         blksize = l16(v->z.desc.blksize);
81                         chat("iso, blksize=%d...", blksize);
82
83                         v = (Voldesc*)(dirp->iobuf);
84                         haveplan9 = (strncmp((char*)v->z.boot.sysid, "PLAN 9", 6)==0);
85                         if(haveplan9){
86                                 if(noplan9) {
87                                         chat("ignoring plan9");
88                                         haveplan9 = 0;
89                                 } else {
90                                         fmt = '9';
91                                         chat("plan9 iso...");
92                                 }
93                         }
94                         continue;
95                 }
96
97                 if(memcmp(&v->byte[8], "\01CDROM\01", 7) == 0){ /* high sierra */
98                         if(dirp)
99                                 putbuf(dirp);
100                         dirp = p;
101                         fmt = 'r';
102                         dp = (Drec*)v->r.desc.rootdir;
103                         blksize = l16(v->r.desc.blksize);
104                         chat("high sierra, blksize=%d...", blksize);
105                         continue;
106                 }
107
108                 if(haveplan9==0 && !nojoliet
109                 && memcmp(v->byte, "\02CD001\01", 7) == 0){
110 chat("%d %d\n", haveplan9, nojoliet);
111                         /*
112                          * The right thing to do is walk the escape sequences looking
113                          * for one of 25 2F 4[035], but Microsoft seems to not honor
114                          * the format, which makes it hard to walk over.
115                          */
116                         q = v->z.desc.escapes;
117                         if(q[0] == 0x25 && q[1] == 0x2F && (q[2] == 0x40 || q[2] == 0x43 || q[2] == 0x45)){     /* Joliet, it appears */
118                                 if(dirp)
119                                         putbuf(dirp);
120                                 dirp = p;
121                                 fmt = 'J';
122                                 dp = (Drec*)v->z.desc.rootdir;
123                                 if(blksize != l16(v->z.desc.blksize))
124                                         fprint(2, "warning: suspicious Joliet blocksize\n");
125                                 chat("joliet...");
126                                 continue;
127                         }
128                 }
129                 putbuf(p);
130                 if(v->byte[0] == 0xFF)
131                         break;
132         }
133
134         if(fmt == 0){
135                 if(dirp)
136                         putbuf(dirp);
137                 return -1;
138         }
139         assert(dirp != nil);
140
141         if(chatty)
142                 showdrec(2, fmt, dp);
143         if(blksize > Sectorsize){
144                 chat("blksize too big...");
145                 putbuf(dirp);
146                 return -1;
147         }
148         if(waserror()){
149                 putbuf(dirp);
150                 nexterror();
151         }
152         root->len = sizeof(Isofile) - sizeof(Drec) + dp->reclen;
153         root->ptr = fp = ealloc(root->len);
154
155         if(haveplan9)
156                 root->xf->isplan9 = 1;
157
158         fp->fmt = fmt;
159         fp->blksize = blksize;
160         fp->offset = 0;
161         fp->doffset = 0;
162         memmove(&fp->d, dp, dp->reclen);
163         root->qid.path = l32(dp->addr);
164         root->qid.type = QTDIR;
165         putbuf(dirp);
166         poperror();
167         if(getdrec(root, rd) >= 0){
168                 n = rd->reclen-(34+rd->namelen);
169                 s = (uchar*)rd->name + rd->namelen;
170                 if((uintptr)s & 1){
171                         s++;
172                         n--;
173                 }
174                 if(n >= 7 && s[0] == 'S' && s[1] == 'P' && s[2] == 7 &&
175                    s[3] == 1 && s[4] == 0xBE && s[5] == 0xEF){
176                         root->xf->issusp = 1;
177                         root->xf->suspoff = s[6];
178                         n -= root->xf->suspoff;
179                         s += root->xf->suspoff;
180                         for(; n >= 4; s += l, n -= l){
181                                 l = s[2];
182                                 if(s[0] == 'E' && s[1] == 'R'){
183                                         if(!norock && s[4] == 10 && memcmp(s+8, "RRIP_1991A", 10) == 0)
184                                                 root->xf->isrock = 1;
185                                         break;
186                                 } else if(s[0] == 'C' && s[1] == 'E' && s[2] >= 28){
187                                         n = getcontin(root->xf->d, s, &s);
188                                         continue;
189                                 } else if(s[0] == 'R' && s[1] == 'R'){
190                                         if(!norock)
191                                                 root->xf->isrock = 1;
192                                         break;
193                                 } else if(s[0] == 'S' && s[1] == 'T')
194                                         break;
195                         }
196                 }
197         }
198         if(root->xf->isrock)
199                 chat("Rock Ridge...");
200         fp->offset = 0;
201         fp->doffset = 0;
202         return 0;
203 }
204
205 static void
206 iclone(Xfile *of, Xfile *nf)
207 {
208         USED(of, nf);
209 }
210
211 static void
212 iwalkup(Xfile *f)
213 {
214         long paddr;
215         uchar dbuf[256];
216         Drec *d = (Drec *)dbuf;
217         Xfile pf, ppf;
218         Isofile piso, ppiso;
219
220         memset(&pf, 0, sizeof pf);
221         memset(&ppf, 0, sizeof ppf);
222         pf.ptr = &piso;
223         ppf.ptr = &ppiso;
224         if(opendotdot(f, &pf) < 0)
225                 error("can't open pf");
226         paddr = l32(pf.ptr->d.addr);
227         if(l32(f->ptr->d.addr) == paddr)
228                 return;
229         if(opendotdot(&pf, &ppf) < 0)
230                 error("can't open ppf");
231         while(getdrec(&ppf, d) >= 0){
232                 if(l32(d->addr) == paddr){
233                         newdrec(f, d);
234                         f->qid.path = paddr;
235                         f->qid.type = QTDIR;
236                         return;
237                 }
238         }
239         error("can't find addr of ..");
240 }
241
242 static int
243 casestrcmp(int isplan9, char *a, char *b)
244 {
245         if(isplan9)
246                 return strcmp(a, b);
247         return cistrcmp(a, b);
248 }
249
250 static void
251 iwalk(Xfile *f, char *name)
252 {
253         Isofile *ip = f->ptr;
254         uchar dbuf[256];
255         char nbuf[4*Maxname];
256         Drec *d = (Drec*)dbuf;
257         Dir dir;
258         char *p;
259         int len, vers, dvers;
260
261         vers = -1;
262         if(p = strchr(name, ';')) {     /* assign = */
263                 len = p-name;
264                 if(len >= Maxname)
265                         len = Maxname-1;
266                 memmove(nbuf, name, len);
267                 vers = strtoul(p+1, 0, 10);
268                 name = nbuf;
269         }
270 /*
271         len = strlen(name);
272         if(len >= Maxname){
273                 len = Maxname-1;
274                 if(name != nbuf){
275                         memmove(nbuf, name, len);
276                         name = nbuf;
277                 }
278                 name[len] = 0;
279         }
280 */
281
282         chat("%d \"%s\"...", strlen(name), name);
283         ip->offset = 0;
284         setnames(&dir, nbuf);
285         while(getdrec(f, d) >= 0) {
286                 dvers = rzdir(f->xf, &dir, ip->fmt, d);
287                 if(casestrcmp(f->xf->isplan9||f->xf->isrock, name, dir.name) != 0)
288                         continue;
289                 newdrec(f, d);
290                 f->qid.path = dir.qid.path;
291                 f->qid.type = dir.qid.type;
292                 USED(dvers);
293                 return;
294         }
295         USED(vers);
296         error(Enonexist);
297 }
298
299 static void
300 iopen(Xfile *f, int mode)
301 {
302         mode &= ~OCEXEC;
303         if(mode != OREAD && mode != OEXEC)
304                 error(Eperm);
305         f->ptr->offset = 0;
306         f->ptr->doffset = 0;
307 }
308
309 static void
310 icreate(Xfile *f, char *name, long perm, int mode)
311 {
312         USED(f, name, perm, mode);
313         error(Eperm);
314 }
315
316 static long
317 ireaddir(Xfile *f, uchar *buf, long offset, long count)
318 {
319         Isofile *ip = f->ptr;
320         Dir d;
321         char names[4*Maxname];
322         uchar dbuf[256];
323         Drec *drec = (Drec *)dbuf;
324         int n, rcnt;
325
326         if(offset==0){
327                 ip->offset = 0;
328                 ip->doffset = 0;
329         }else if(offset != ip->doffset)
330                 error("seek in directory not allowed");
331
332         rcnt = 0;
333         setnames(&d, names);
334         while(rcnt < count && getdrec(f, drec) >= 0){
335                 if(drec->namelen == 1){
336                         if(drec->name[0] == 0)
337                                 continue;
338                         if(drec->name[0] == 1)
339                                 continue;
340                 }
341                 rzdir(f->xf, &d, ip->fmt, drec);
342                 d.qid.vers = f->qid.vers;
343                 if((n = convD2M(&d, buf+rcnt, count-rcnt)) <= BIT16SZ){
344                         ungetdrec(f);
345                         break;
346                 }
347                 rcnt += n;
348         }
349         ip->doffset += rcnt;
350         return rcnt;
351 }
352
353 static long
354 iread(Xfile *f, char *buf, vlong offset, long count)
355 {
356         int n, o, rcnt = 0;
357         vlong size, addr;
358         Isofile *ip = f->ptr;
359         Iobuf *p;
360
361         size = fakemax(l32(ip->d.size));
362         if(offset >= size)
363                 return 0;
364         if(offset+count > size)
365                 count = size - offset;
366         addr = ((vlong)l32(ip->d.addr) + ip->d.attrlen)*ip->blksize + offset;
367         o = addr % Sectorsize;
368         addr /= Sectorsize;
369         /*chat("d.addr=%ld, addr=%lld, o=%d...", l32(ip->d.addr), addr, o);*/
370         n = Sectorsize - o;
371
372         while(count > 0){
373                 if(n > count)
374                         n = count;
375                 p = getbuf(f->xf->d, addr);
376                 memmove(&buf[rcnt], &p->iobuf[o], n);
377                 putbuf(p);
378                 count -= n;
379                 rcnt += n;
380                 ++addr;
381                 o = 0;
382                 n = Sectorsize;
383         }
384         return rcnt;
385 }
386
387 static long
388 iwrite(Xfile *f, char *buf, vlong offset, long count)
389 {
390         USED(f, buf, offset, count);
391         error(Eperm);
392         return 0;
393 }
394
395 static void
396 iclunk(Xfile *f)
397 {
398         USED(f);
399 }
400
401 static void
402 iremove(Xfile *f)
403 {
404         USED(f);
405         error(Eperm);
406 }
407
408 static void
409 istat(Xfile *f, Dir *d)
410 {
411         Isofile *ip = f->ptr;
412
413         rzdir(f->xf, d, ip->fmt, &ip->d);
414         d->qid.vers = f->qid.vers;
415         if(d->qid.path==f->xf->rootqid.path){
416                 d->qid.path = 0;
417                 d->qid.type = QTDIR;
418         }
419 }
420
421 static void
422 iwstat(Xfile *f, Dir *d)
423 {
424         USED(f, d);
425         error(Eperm);
426 }
427
428 static int
429 showdrec(int fd, int fmt, void *x)
430 {
431         Drec *d = (Drec *)x;
432         int namelen;
433         int syslen;
434
435         if(d->reclen == 0)
436                 return 0;
437         fprint(fd, "%d %d %ld %ld ",
438                 d->reclen, d->attrlen, l32(d->addr), l32(d->size));
439         fprint(fd, "%s 0x%2.2x %d %d %ld ",
440                 rdate(d->date, fmt), (fmt=='z' ? d->flags : d->r_flags),
441                 d->unitsize, d->gapsize, l16(d->vseqno));
442         fprint(fd, "%d %s", d->namelen, nstr(d->name, d->namelen));
443         if(fmt != 'J'){
444                 namelen = d->namelen + (1-(d->namelen&1));
445                 syslen = d->reclen - 33 - namelen;
446                 if(syslen != 0)
447                         fprint(fd, " %s", nstr(&d->name[namelen], syslen));
448         }
449         fprint(fd, "\n");
450         return d->reclen + (d->reclen&1);
451 }
452
453 static void
454 newdrec(Xfile *f, Drec *dp)
455 {
456         Isofile *x = f->ptr;
457         Isofile *n;
458         int len;
459
460         len = sizeof(Isofile) - sizeof(Drec) + dp->reclen;
461         n = ealloc(len);
462         n->fmt = x->fmt;
463         n->blksize = x->blksize;
464         n->offset = 0;
465         n->doffset = 0;
466         memmove(&n->d, dp, dp->reclen);
467         free(x);
468         f->ptr = n;
469         f->len = len;
470 }
471
472 static void
473 ungetdrec(Xfile *f)
474 {
475         Isofile *ip = f->ptr;
476
477         if(ip->offset >= ip->odelta){
478                 ip->offset -= ip->odelta;
479                 ip->odelta = 0;
480         }
481 }
482
483 static int
484 getdrec(Xfile *f, void *buf)
485 {
486         Isofile *ip = f->ptr;
487         int len = 0, boff = 0;
488         vlong addr;
489         uvlong size;
490         Iobuf *p = 0;
491
492         if(!ip)
493                 return -1;
494         size = fakemax(l32(ip->d.size));
495         while(ip->offset < size){
496                 addr = (l32(ip->d.addr)+ip->d.attrlen)*ip->blksize + ip->offset;
497                 boff = addr % Sectorsize;
498                 if(boff > Sectorsize-34){
499                         ip->offset += Sectorsize-boff;
500                         continue;
501                 }
502                 p = getbuf(f->xf->d, addr/Sectorsize);
503                 len = p->iobuf[boff];
504                 if(len >= 34)
505                         break;
506                 putbuf(p);
507                 p = 0;
508                 ip->offset += Sectorsize-boff;
509         }
510         if(p) {
511                 memmove(buf, &p->iobuf[boff], len);
512                 putbuf(p);
513                 ip->odelta = len + (len&1);
514                 ip->offset += ip->odelta;
515                 return 0;
516         }
517         return -1;
518 }
519
520 static int
521 opendotdot(Xfile *f, Xfile *pf)
522 {
523         uchar dbuf[256];
524         Drec *d = (Drec *)dbuf;
525         Isofile *ip = f->ptr, *pip = pf->ptr;
526
527         ip->offset = 0;
528         if(getdrec(f, d) < 0){
529                 chat("opendotdot: getdrec(.) failed...");
530                 return -1;
531         }
532         if(d->namelen != 1 || d->name[0] != 0){
533                 chat("opendotdot: no . entry...");
534                 return -1;
535         }
536         if(l32(d->addr) != l32(ip->d.addr)){
537                 chat("opendotdot: bad . address...");
538                 return -1;
539         }
540         if(getdrec(f, d) < 0){
541                 chat("opendotdot: getdrec(..) failed...");
542                 return -1;
543         }
544         if(d->namelen != 1 || d->name[0] != 1){
545                 chat("opendotdot: no .. entry...");
546                 return -1;
547         }
548
549         pf->xf = f->xf;
550         pip->fmt = ip->fmt;
551         pip->blksize = ip->blksize;
552         pip->offset = 0;
553         pip->doffset = 0;
554         pip->d = *d;
555         return 0;
556 }
557
558 enum {
559         Hname = 1,
560         Hmode = 2,
561 };
562
563 static int
564 rzdir(Xfs *fs, Dir *d, int fmt, Drec *dp)
565 {
566         int n, flags, i, j, lj, nl, vers, sysl, mode, l, have;
567         uchar *s;
568         char *p;
569         char buf[Maxname+UTFmax+1];
570         uchar *q;
571         Rune r;
572         enum { ONAMELEN = 28 }; /* old Plan 9 directory name length */
573
574         have = 0;
575         flags = 0;
576         vers = -1;
577         d->qid.path = l32(dp->addr);
578         d->qid.type = 0;
579         d->qid.vers = 0;
580         n = dp->namelen;
581         memset(d->name, 0, Maxname);
582         if(n == 1) {
583                 switch(dp->name[0]){
584                 case 1:
585                         d->name[1] = '.';
586                         /* fall through */
587                 case 0:
588                         d->name[0] = '.';
589                         have = Hname;
590                         break;
591                 default:
592                         d->name[0] = tolower(dp->name[0]);
593                 }
594         } else {
595                 if(fmt == 'J'){ /* Joliet, 16-bit Unicode */
596                         q = (uchar*)dp->name;
597                         for(i=j=lj=0; i<n && j<Maxname; i+=2){
598                                 lj = j;
599                                 r = (q[i]<<8)|q[i+1];
600                                 j += runetochar(buf+j, &r);
601                         }
602                         if(j >= Maxname)
603                                 j = lj;
604                         memmove(d->name, buf, j);
605                 }else{
606                         if(n >= Maxname)
607                                 n = Maxname-1;
608                         for(i=0; i<n; i++)
609                                 d->name[i] = tolower(dp->name[i]);
610                 }
611         }
612
613         sysl = dp->reclen-(34+dp->namelen);
614         s = (uchar*)dp->name + dp->namelen;
615         if(((uintptr)s) & 1) {
616                 s++;
617                 sysl--;
618         }
619         if(fs->isplan9 && sysl > 0) {
620                 /*
621                  * get gid, uid, mode and possibly name
622                  * from plan9 directory extension
623                  */
624                 nl = *s;
625                 if(nl >= ONAMELEN)
626                         nl = ONAMELEN-1;
627                 if(nl) {
628                         memset(d->name, 0, ONAMELEN);
629                         memmove(d->name, s+1, nl);
630                 }
631                 s += 1 + *s;
632                 nl = *s;
633                 if(nl >= ONAMELEN)
634                         nl = ONAMELEN-1;
635                 memset(d->uid, 0, ONAMELEN);
636                 memmove(d->uid, s+1, nl);
637                 s += 1 + *s;
638                 nl = *s;
639                 if(nl >= ONAMELEN)
640                         nl = ONAMELEN-1;
641                 memset(d->gid, 0, ONAMELEN);
642                 memmove(d->gid, s+1, nl);
643                 s += 1 + *s;
644                 if(((uintptr)s) & 1)
645                         s++;
646                 d->mode = l32(s);
647                 if(d->mode & DMDIR)
648                         d->qid.type |= QTDIR;
649         } else {
650                 d->mode = 0444;
651                 switch(fmt) {
652                 case 'z':
653                         if(fs->isrock)
654                                 strcpy(d->gid, "ridge");
655                         else
656                                 strcpy(d->gid, "iso9660");
657                         flags = dp->flags;
658                         break;
659                 case 'r':
660                         strcpy(d->gid, "sierra");
661                         flags = dp->r_flags;
662                         break;
663                 case 'J':
664                         strcpy(d->gid, "joliet");
665                         flags = dp->flags;
666                         break;
667                 case '9':
668                         strcpy(d->gid, "plan9");
669                         flags = dp->flags;
670                         break;
671                 }
672                 if(flags & 0x02){
673                         d->qid.type |= QTDIR;
674                         d->mode |= DMDIR|0111;
675                 }
676                 strcpy(d->uid, "cdrom");
677                 if(fmt!='9' && !(d->mode&DMDIR)){
678                         /*
679                          * ISO 9660 actually requires that you always have a . and a ;,
680                          * even if there is no version and no extension.  Very few writers
681                          * do this.  If the version is present, we use it for qid.vers.
682                          * If there is no extension but there is a dot, we strip it off.
683                          * (VMS heads couldn't comprehend the dot as a file name character
684                          * rather than as just a separator between name and extension.)
685                          *
686                          * We don't do this for directory names because directories are
687                          * not allowed to have extensions and versions.
688                          */
689                         if((p=strchr(d->name, ';')) != nil){
690                                 vers = strtoul(p+1, 0, 0);
691                                 d->qid.vers = vers;
692                                 *p = '\0';
693                         }
694                         if((p=strchr(d->name, '.')) != nil && *(p+1)=='\0')
695                                 *p = '\0';
696                 }
697                 if(fs->issusp){
698                         nl = 0;
699                         s += fs->suspoff;
700                         sysl -= fs->suspoff;
701                         for(; sysl >= 4 && have != (Hname|Hmode); sysl -= l, s += l){
702                                 if(s[0] == 0 && ((uintptr)s & 1)){
703                                         /* MacOS pads individual entries, contrary to spec */
704                                         s++;
705                                         sysl--;
706                                 }
707                                 l = s[2];
708                                 if(s[0] == 'P' && s[1] == 'X' && s[3] == 1){
709                                         /* posix file attributes */
710                                         mode = l32(s+4);
711                                         d->mode = mode & 0777;
712                                         if((mode & 0170000) == 040000){
713                                                 d->mode |= DMDIR;
714                                                 d->qid.type |= QTDIR;
715                                         }
716                                         have |= Hmode;
717                                 } else if(s[0] == 'N' && s[1] == 'M' && s[3] == 1){
718                                         /* alternative name */
719                                         if((s[4] & ~1) == 0){
720                                                 i = nl+l-5;
721                                                 if(i >= Maxname)
722                                                         i = Maxname-1;
723                                                 if((i -= nl) > 0){
724                                                         memmove(d->name+nl, s+5, i);
725                                                         nl += i;
726                                                 }
727                                                 if(s[4] == 0)
728                                                         have |= Hname;
729                                         }
730                                 } else if(s[0] == 'C' && s[1] == 'E' && s[2] >= 28){
731                                         sysl = getcontin(fs->d, s, &s);
732                                         continue;
733                                 } else if(s[0] == 'S' && s[1] == 'T')
734                                         break;
735                         }
736                 }
737         }
738         d->length = 0;
739         if((d->mode & DMDIR) == 0)      
740                 d->length = fakemax(l32(dp->size));
741         d->type = 0;
742         d->dev = 0;
743         d->atime = gtime(dp->date);
744         d->mtime = d->atime;
745         return vers;
746 }
747
748 static int
749 getcontin(Xdata *dev, uchar *p, uchar **s)
750 {
751         long bn, off, len;
752         Iobuf *b;
753
754         bn = l32(p+4);
755         off = l32(p+12);
756         len = l32(p+20);
757         chat("getcontin %d...", bn);
758         b = getbuf(dev, bn);
759         if(b == 0){
760                 *s = 0;
761                 return 0;
762         }
763         *s = b->iobuf+off;
764         putbuf(b);
765         return len;
766 }
767
768 static char *
769 nstr(uchar *p, int n)
770 {
771         static char buf[132];
772         char *q = buf;
773
774         while(--n >= 0){
775                 if(*p == '\\')
776                         *q++ = '\\';
777                 if(' ' <= *p && *p <= '~')
778                         *q++ = *p++;
779                 else
780                         q += sprint(q, "\\%2.2ux", *p++);
781         }
782         *q = 0;
783         return buf;
784 }
785
786 static char *
787 rdate(uchar *p, int fmt)
788 {
789         static char buf[64];
790         int htz, s, n;
791
792         n = sprint(buf, "%2.2d.%2.2d.%2.2d %2.2d:%2.2d:%2.2d",
793                 p[0], p[1], p[2], p[3], p[4], p[5]);
794         if(fmt == 'z'){
795                 htz = p[6];
796                 if(htz >= 128){
797                         htz = 256-htz;
798                         s = '-';
799                 }else
800                         s = '+';
801                 sprint(&buf[n], " (%c%.1f)", s, (float)htz/2);
802         }
803         return buf;
804 }
805
806 static char
807 dmsize[12] =
808 {
809         31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31,
810 };
811
812 static int
813 dysize(int y)
814 {
815
816         if((y%4) == 0)
817                 return 366;
818         return 365;
819 }
820
821 static long
822 gtime(uchar *p) /* yMdhmsz */
823 {
824         long t;
825         int i, y, M, d, h, m, s, tz;
826
827         y=p[0]; M=p[1]; d=p[2];
828         h=p[3]; m=p[4]; s=p[5]; tz=p[6];
829         USED(tz);
830         y += 1900;
831         if (y < 1970)
832                 return 0;
833         if (M < 1 || M > 12)
834                 return 0;
835         if (d < 1 || d > dmsize[M-1])
836                 if (!(M == 2 && d == 29 && dysize(y) == 366))
837                         return 0;
838         if (h > 23)
839                 return 0;
840         if (m > 59)
841                 return 0;
842         if (s > 59)
843                 return 0;
844         t = 0;
845         for(i=1970; i<y; i++)
846                 t += dysize(i);
847         if (dysize(y)==366 && M >= 3)
848                 t++;
849         while(--M)
850                 t += dmsize[M-1];
851         t += d-1;
852         t = 24*t + h;
853         t = 60*t + m;
854         t = 60*t + s;
855         return t;
856 }
857
858 #define p       ((uchar*)arg)
859
860 static long
861 l16(void *arg)
862 {
863         long v;
864
865         v = ((long)p[1]<<8)|p[0];
866         if (v >= 0x8000L)
867                 v -= 0x10000L;
868         return v;
869 }
870
871 static long
872 l32(void *arg)
873 {
874         return ((((((long)p[3]<<8)|p[2])<<8)|p[1])<<8)|p[0];
875 }
876
877 #undef  p