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