xref: /plan9-contrib/sys/src/cmd/9660srv/9660srv.c (revision 3468a4915d661daa200976acc4f80f51aae144b2)
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
878