xref: /plan9/sys/src/cmd/9660srv/9660srv.c (revision 60014d6756a98ad10929607ca84a1b7488a16cfc)
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 	int ca, cb;
246 
247 	if(isplan9)
248 		return strcmp(a, b);
249 	for(;;) {
250 		ca = *a++;
251 		cb = *b++;
252 		if(ca >= 'A' && ca <= 'Z')
253 			ca += 'a' - 'A';
254 		if(cb >= 'A' && cb <= 'Z')
255 			cb += 'a' - 'A';
256 		if(ca != cb) {
257 			if(ca > cb)
258 				return 1;
259 			return -1;
260 		}
261 		if(ca == 0)
262 			return 0;
263 	}
264 }
265 
266 static void
267 iwalk(Xfile *f, char *name)
268 {
269 	Isofile *ip = f->ptr;
270 	uchar dbuf[256];
271 	char nbuf[4*Maxname];
272 	Drec *d = (Drec*)dbuf;
273 	Dir dir;
274 	char *p;
275 	int len, vers, dvers;
276 
277 	vers = -1;
278 	if(p = strchr(name, ';')) {	/* assign = */
279 		len = p-name;
280 		if(len >= Maxname)
281 			len = Maxname-1;
282 		memmove(nbuf, name, len);
283 		vers = strtoul(p+1, 0, 10);
284 		name = nbuf;
285 	}
286 /*
287 	len = strlen(name);
288 	if(len >= Maxname){
289 		len = Maxname-1;
290 		if(name != nbuf){
291 			memmove(nbuf, name, len);
292 			name = nbuf;
293 		}
294 		name[len] = 0;
295 	}
296 */
297 
298 	chat("%d \"%s\"...", strlen(name), name);
299 	ip->offset = 0;
300 	setnames(&dir, nbuf);
301 	while(getdrec(f, d) >= 0) {
302 		dvers = rzdir(f->xf, &dir, ip->fmt, d);
303 		if(casestrcmp(f->xf->isplan9||f->xf->isrock, name, dir.name) != 0)
304 			continue;
305 		newdrec(f, d);
306 		f->qid.path = dir.qid.path;
307 		f->qid.type = dir.qid.type;
308 		USED(dvers);
309 		return;
310 	}
311 	USED(vers);
312 	error(Enonexist);
313 }
314 
315 static void
316 iopen(Xfile *f, int mode)
317 {
318 	mode &= ~OCEXEC;
319 	if(mode != OREAD && mode != OEXEC)
320 		error(Eperm);
321 	f->ptr->offset = 0;
322 	f->ptr->doffset = 0;
323 }
324 
325 static void
326 icreate(Xfile *f, char *name, long perm, int mode)
327 {
328 	USED(f, name, perm, mode);
329 	error(Eperm);
330 }
331 
332 static long
333 ireaddir(Xfile *f, uchar *buf, long offset, long count)
334 {
335 	Isofile *ip = f->ptr;
336 	Dir d;
337 	char names[4*Maxname];
338 	uchar dbuf[256];
339 	Drec *drec = (Drec *)dbuf;
340 	int n, rcnt;
341 
342 	if(offset==0){
343 		ip->offset = 0;
344 		ip->doffset = 0;
345 	}else if(offset != ip->doffset)
346 		error("seek in directory not allowed");
347 
348 	rcnt = 0;
349 	setnames(&d, names);
350 	while(rcnt < count && getdrec(f, drec) >= 0){
351 		if(drec->namelen == 1){
352 			if(drec->name[0] == 0)
353 				continue;
354 			if(drec->name[0] == 1)
355 				continue;
356 		}
357 		rzdir(f->xf, &d, ip->fmt, drec);
358 		d.qid.vers = f->qid.vers;
359 		if((n = convD2M(&d, buf+rcnt, count-rcnt)) <= BIT16SZ){
360 			ungetdrec(f);
361 			break;
362 		}
363 		rcnt += n;
364 	}
365 	ip->doffset += rcnt;
366 	return rcnt;
367 }
368 
369 static long
370 iread(Xfile *f, char *buf, vlong offset, long count)
371 {
372 	int n, o, rcnt = 0;
373 	vlong size, addr;
374 	Isofile *ip = f->ptr;
375 	Iobuf *p;
376 
377 	size = fakemax(l32(ip->d.size));
378 	if(offset >= size)
379 		return 0;
380 	if(offset+count > size)
381 		count = size - offset;
382 	addr = ((vlong)l32(ip->d.addr) + ip->d.attrlen)*ip->blksize + offset;
383 	o = addr % Sectorsize;
384 	addr /= Sectorsize;
385 	/*chat("d.addr=%ld, addr=%lld, o=%d...", l32(ip->d.addr), addr, o);*/
386 	n = Sectorsize - o;
387 
388 	while(count > 0){
389 		if(n > count)
390 			n = count;
391 		p = getbuf(f->xf->d, addr);
392 		memmove(&buf[rcnt], &p->iobuf[o], n);
393 		putbuf(p);
394 		count -= n;
395 		rcnt += n;
396 		++addr;
397 		o = 0;
398 		n = Sectorsize;
399 	}
400 	return rcnt;
401 }
402 
403 static long
404 iwrite(Xfile *f, char *buf, vlong offset, long count)
405 {
406 	USED(f, buf, offset, count);
407 	error(Eperm);
408 	return 0;
409 }
410 
411 static void
412 iclunk(Xfile *f)
413 {
414 	USED(f);
415 }
416 
417 static void
418 iremove(Xfile *f)
419 {
420 	USED(f);
421 	error(Eperm);
422 }
423 
424 static void
425 istat(Xfile *f, Dir *d)
426 {
427 	Isofile *ip = f->ptr;
428 
429 	rzdir(f->xf, d, ip->fmt, &ip->d);
430 	d->qid.vers = f->qid.vers;
431 	if(d->qid.path==f->xf->rootqid.path){
432 		d->qid.path = 0;
433 		d->qid.type = QTDIR;
434 	}
435 }
436 
437 static void
438 iwstat(Xfile *f, Dir *d)
439 {
440 	USED(f, d);
441 	error(Eperm);
442 }
443 
444 static int
445 showdrec(int fd, int fmt, void *x)
446 {
447 	Drec *d = (Drec *)x;
448 	int namelen;
449 	int syslen;
450 
451 	if(d->reclen == 0)
452 		return 0;
453 	fprint(fd, "%d %d %ld %ld ",
454 		d->reclen, d->attrlen, l32(d->addr), l32(d->size));
455 	fprint(fd, "%s 0x%2.2x %d %d %ld ",
456 		rdate(d->date, fmt), (fmt=='z' ? d->flags : d->r_flags),
457 		d->unitsize, d->gapsize, l16(d->vseqno));
458 	fprint(fd, "%d %s", d->namelen, nstr(d->name, d->namelen));
459 	if(fmt != 'J'){
460 		namelen = d->namelen + (1-(d->namelen&1));
461 		syslen = d->reclen - 33 - namelen;
462 		if(syslen != 0)
463 			fprint(fd, " %s", nstr(&d->name[namelen], syslen));
464 	}
465 	fprint(fd, "\n");
466 	return d->reclen + (d->reclen&1);
467 }
468 
469 static void
470 newdrec(Xfile *f, Drec *dp)
471 {
472 	Isofile *x = f->ptr;
473 	Isofile *n;
474 	int len;
475 
476 	len = sizeof(Isofile) - sizeof(Drec) + dp->reclen;
477 	n = ealloc(len);
478 	n->fmt = x->fmt;
479 	n->blksize = x->blksize;
480 	n->offset = 0;
481 	n->doffset = 0;
482 	memmove(&n->d, dp, dp->reclen);
483 	free(x);
484 	f->ptr = n;
485 	f->len = len;
486 }
487 
488 static void
489 ungetdrec(Xfile *f)
490 {
491 	Isofile *ip = f->ptr;
492 
493 	if(ip->offset >= ip->odelta){
494 		ip->offset -= ip->odelta;
495 		ip->odelta = 0;
496 	}
497 }
498 
499 static int
500 getdrec(Xfile *f, void *buf)
501 {
502 	Isofile *ip = f->ptr;
503 	int len = 0, boff = 0;
504 	vlong addr;
505 	uvlong size;
506 	Iobuf *p = 0;
507 
508 	if(!ip)
509 		return -1;
510 	size = fakemax(l32(ip->d.size));
511 	while(ip->offset < size){
512 		addr = (l32(ip->d.addr)+ip->d.attrlen)*ip->blksize + ip->offset;
513 		boff = addr % Sectorsize;
514 		if(boff > Sectorsize-34){
515 			ip->offset += Sectorsize-boff;
516 			continue;
517 		}
518 		p = getbuf(f->xf->d, addr/Sectorsize);
519 		len = p->iobuf[boff];
520 		if(len >= 34)
521 			break;
522 		putbuf(p);
523 		p = 0;
524 		ip->offset += Sectorsize-boff;
525 	}
526 	if(p) {
527 		memmove(buf, &p->iobuf[boff], len);
528 		putbuf(p);
529 		ip->odelta = len + (len&1);
530 		ip->offset += ip->odelta;
531 		return 0;
532 	}
533 	return -1;
534 }
535 
536 static int
537 opendotdot(Xfile *f, Xfile *pf)
538 {
539 	uchar dbuf[256];
540 	Drec *d = (Drec *)dbuf;
541 	Isofile *ip = f->ptr, *pip = pf->ptr;
542 
543 	ip->offset = 0;
544 	if(getdrec(f, d) < 0){
545 		chat("opendotdot: getdrec(.) failed...");
546 		return -1;
547 	}
548 	if(d->namelen != 1 || d->name[0] != 0){
549 		chat("opendotdot: no . entry...");
550 		return -1;
551 	}
552 	if(l32(d->addr) != l32(ip->d.addr)){
553 		chat("opendotdot: bad . address...");
554 		return -1;
555 	}
556 	if(getdrec(f, d) < 0){
557 		chat("opendotdot: getdrec(..) failed...");
558 		return -1;
559 	}
560 	if(d->namelen != 1 || d->name[0] != 1){
561 		chat("opendotdot: no .. entry...");
562 		return -1;
563 	}
564 
565 	pf->xf = f->xf;
566 	pip->fmt = ip->fmt;
567 	pip->blksize = ip->blksize;
568 	pip->offset = 0;
569 	pip->doffset = 0;
570 	pip->d = *d;
571 	return 0;
572 }
573 
574 enum {
575 	Hname = 1,
576 	Hmode = 2,
577 };
578 
579 static int
580 rzdir(Xfs *fs, Dir *d, int fmt, Drec *dp)
581 {
582 	int n, flags, i, j, lj, nl, vers, sysl, mode, l, have;
583 	uchar *s;
584 	char *p;
585 	char buf[Maxname+UTFmax+1];
586 	uchar *q;
587 	Rune r;
588 	enum { ONAMELEN = 28 };	/* old Plan 9 directory name length */
589 
590 	have = 0;
591 	flags = 0;
592 	vers = -1;
593 	d->qid.path = l32(dp->addr);
594 	d->qid.type = 0;
595 	d->qid.vers = 0;
596 	n = dp->namelen;
597 	memset(d->name, 0, Maxname);
598 	if(n == 1) {
599 		switch(dp->name[0]){
600 		case 1:
601 			d->name[1] = '.';
602 			/* fall through */
603 		case 0:
604 			d->name[0] = '.';
605 			have = Hname;
606 			break;
607 		default:
608 			d->name[0] = tolower(dp->name[0]);
609 		}
610 	} else {
611 		if(fmt == 'J'){	/* Joliet, 16-bit Unicode */
612 			q = (uchar*)dp->name;
613 			for(i=j=lj=0; i<n && j<Maxname; i+=2){
614 				lj = j;
615 				r = (q[i]<<8)|q[i+1];
616 				j += runetochar(buf+j, &r);
617 			}
618 			if(j >= Maxname)
619 				j = lj;
620 			memmove(d->name, buf, j);
621 		}else{
622 			if(n >= Maxname)
623 				n = Maxname-1;
624 			for(i=0; i<n; i++)
625 				d->name[i] = tolower(dp->name[i]);
626 		}
627 	}
628 
629 	sysl = dp->reclen-(34+dp->namelen);
630 	s = (uchar*)dp->name + dp->namelen;
631 	if(((uintptr)s) & 1) {
632 		s++;
633 		sysl--;
634 	}
635 	if(fs->isplan9 && sysl > 0) {
636 		/*
637 		 * get gid, uid, mode and possibly name
638 		 * from plan9 directory extension
639 		 */
640 		nl = *s;
641 		if(nl >= ONAMELEN)
642 			nl = ONAMELEN-1;
643 		if(nl) {
644 			memset(d->name, 0, ONAMELEN);
645 			memmove(d->name, s+1, nl);
646 		}
647 		s += 1 + *s;
648 		nl = *s;
649 		if(nl >= ONAMELEN)
650 			nl = ONAMELEN-1;
651 		memset(d->uid, 0, ONAMELEN);
652 		memmove(d->uid, s+1, nl);
653 		s += 1 + *s;
654 		nl = *s;
655 		if(nl >= ONAMELEN)
656 			nl = ONAMELEN-1;
657 		memset(d->gid, 0, ONAMELEN);
658 		memmove(d->gid, s+1, nl);
659 		s += 1 + *s;
660 		if(((uintptr)s) & 1)
661 			s++;
662 		d->mode = l32(s);
663 		if(d->mode & DMDIR)
664 			d->qid.type |= QTDIR;
665 	} else {
666 		d->mode = 0444;
667 		switch(fmt) {
668 		case 'z':
669 			if(fs->isrock)
670 				strcpy(d->gid, "ridge");
671 			else
672 				strcpy(d->gid, "iso9660");
673 			flags = dp->flags;
674 			break;
675 		case 'r':
676 			strcpy(d->gid, "sierra");
677 			flags = dp->r_flags;
678 			break;
679 		case 'J':
680 			strcpy(d->gid, "joliet");
681 			flags = dp->flags;
682 			break;
683 		case '9':
684 			strcpy(d->gid, "plan9");
685 			flags = dp->flags;
686 			break;
687 		}
688 		if(flags & 0x02){
689 			d->qid.type |= QTDIR;
690 			d->mode |= DMDIR|0111;
691 		}
692 		strcpy(d->uid, "cdrom");
693 		if(fmt!='9' && !(d->mode&DMDIR)){
694 			/*
695 			 * ISO 9660 actually requires that you always have a . and a ;,
696 			 * even if there is no version and no extension.  Very few writers
697 			 * do this.  If the version is present, we use it for qid.vers.
698 			 * If there is no extension but there is a dot, we strip it off.
699 			 * (VMS heads couldn't comprehend the dot as a file name character
700 			 * rather than as just a separator between name and extension.)
701 			 *
702 			 * We don't do this for directory names because directories are
703 			 * not allowed to have extensions and versions.
704 			 */
705 			if((p=strchr(d->name, ';')) != nil){
706 				vers = strtoul(p+1, 0, 0);
707 				d->qid.vers = vers;
708 				*p = '\0';
709 			}
710 			if((p=strchr(d->name, '.')) != nil && *(p+1)=='\0')
711 				*p = '\0';
712 		}
713 		if(fs->issusp){
714 			nl = 0;
715 			s += fs->suspoff;
716 			sysl -= fs->suspoff;
717 			for(; sysl >= 4 && have != (Hname|Hmode); sysl -= l, s += l){
718 				if(s[0] == 0 && ((uintptr)s & 1)){
719 					/* MacOS pads individual entries, contrary to spec */
720 					s++;
721 					sysl--;
722 				}
723 				l = s[2];
724 				if(s[0] == 'P' && s[1] == 'X' && s[3] == 1){
725 					/* posix file attributes */
726 					mode = l32(s+4);
727 					d->mode = mode & 0777;
728 					if((mode & 0170000) == 040000){
729 						d->mode |= DMDIR;
730 						d->qid.type |= QTDIR;
731 					}
732 					have |= Hmode;
733 				} else if(s[0] == 'N' && s[1] == 'M' && s[3] == 1){
734 					/* alternative name */
735 					if((s[4] & ~1) == 0){
736 						i = nl+l-5;
737 						if(i >= Maxname)
738 							i = Maxname-1;
739 						if((i -= nl) > 0){
740 							memmove(d->name+nl, s+5, i);
741 							nl += i;
742 						}
743 						if(s[4] == 0)
744 							have |= Hname;
745 					}
746 				} else if(s[0] == 'C' && s[1] == 'E' && s[2] >= 28){
747 					sysl = getcontin(fs->d, s, &s);
748 					continue;
749 				} else if(s[0] == 'S' && s[1] == 'T')
750 					break;
751 			}
752 		}
753 	}
754 	d->length = 0;
755 	if((d->mode & DMDIR) == 0)
756 		d->length = fakemax(l32(dp->size));
757 	d->type = 0;
758 	d->dev = 0;
759 	d->atime = gtime(dp->date);
760 	d->mtime = d->atime;
761 	return vers;
762 }
763 
764 static int
765 getcontin(Xdata *dev, uchar *p, uchar **s)
766 {
767 	long bn, off, len;
768 	Iobuf *b;
769 
770 	bn = l32(p+4);
771 	off = l32(p+12);
772 	len = l32(p+20);
773 	chat("getcontin %d...", bn);
774 	b = getbuf(dev, bn);
775 	if(b == 0){
776 		*s = 0;
777 		return 0;
778 	}
779 	*s = b->iobuf+off;
780 	putbuf(b);
781 	return len;
782 }
783 
784 static char *
785 nstr(uchar *p, int n)
786 {
787 	static char buf[132];
788 	char *q = buf;
789 
790 	while(--n >= 0){
791 		if(*p == '\\')
792 			*q++ = '\\';
793 		if(' ' <= *p && *p <= '~')
794 			*q++ = *p++;
795 		else
796 			q += sprint(q, "\\%2.2ux", *p++);
797 	}
798 	*q = 0;
799 	return buf;
800 }
801 
802 static char *
803 rdate(uchar *p, int fmt)
804 {
805 	static char buf[64];
806 	int htz, s, n;
807 
808 	n = sprint(buf, "%2.2d.%2.2d.%2.2d %2.2d:%2.2d:%2.2d",
809 		p[0], p[1], p[2], p[3], p[4], p[5]);
810 	if(fmt == 'z'){
811 		htz = p[6];
812 		if(htz >= 128){
813 			htz = 256-htz;
814 			s = '-';
815 		}else
816 			s = '+';
817 		sprint(&buf[n], " (%c%.1f)", s, (float)htz/2);
818 	}
819 	return buf;
820 }
821 
822 static char
823 dmsize[12] =
824 {
825 	31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31,
826 };
827 
828 static int
829 dysize(int y)
830 {
831 
832 	if((y%4) == 0)
833 		return 366;
834 	return 365;
835 }
836 
837 static long
838 gtime(uchar *p)	/* yMdhmsz */
839 {
840 	long t;
841 	int i, y, M, d, h, m, s, tz;
842 
843 	y=p[0]; M=p[1]; d=p[2];
844 	h=p[3]; m=p[4]; s=p[5]; tz=p[6];
845 	USED(tz);
846 	y += 1900;
847 	if (y < 1970)
848 		return 0;
849 	if (M < 1 || M > 12)
850 		return 0;
851 	if (d < 1 || d > dmsize[M-1])
852 		if (!(M == 2 && d == 29 && dysize(y) == 366))
853 			return 0;
854 	if (h > 23)
855 		return 0;
856 	if (m > 59)
857 		return 0;
858 	if (s > 59)
859 		return 0;
860 	t = 0;
861 	for(i=1970; i<y; i++)
862 		t += dysize(i);
863 	if (dysize(y)==366 && M >= 3)
864 		t++;
865 	while(--M)
866 		t += dmsize[M-1];
867 	t += d-1;
868 	t = 24*t + h;
869 	t = 60*t + m;
870 	t = 60*t + s;
871 	return t;
872 }
873 
874 #define	p	((uchar*)arg)
875 
876 static long
877 l16(void *arg)
878 {
879 	long v;
880 
881 	v = ((long)p[1]<<8)|p[0];
882 	if (v >= 0x8000L)
883 		v -= 0x10000L;
884 	return v;
885 }
886 
887 static long
888 l32(void *arg)
889 {
890 	return ((((((long)p[3]<<8)|p[2])<<8)|p[1])<<8)|p[0];
891 }
892 
893 #undef	p
894