xref: /plan9/sys/src/cmd/dossrv/dossubs.c (revision efb32250a8587c63ac66d9426ed42bf32cc94d53)
1 #include <u.h>
2 #include <libc.h>
3 #include <bio.h>
4 #include <auth.h>
5 #include <fcall.h>
6 #include "iotrack.h"
7 #include "dat.h"
8 #include "fns.h"
9 
10 static uchar	isdos[256];
11 
12 int
isdosfs(uchar * buf)13 isdosfs(uchar *buf)
14 {
15 	/*
16 	 * When dynamic disc managers move the disc partition,
17 	 * they make it start with 0xE9.
18 	 */
19 	if(buf[0] == 0xE9)
20 		return 1;
21 
22 	/*
23 	 * Check if the jump displacement (magic[1]) is too short for a FAT.
24 	 *
25 	 * check now omitted due to digital cameras that use a 0 jump.
26 	 * the ecma-107 standard says this is okay and that interoperable fat
27 	 * implementations shouldn't assume this:
28 	 * http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-107.pdf,
29 	 * page 11.
30 	 */
31 	if(buf[0] == 0xEB && buf[2] == 0x90 /* && buf[1] >= 0x30 */)
32 		return 1;
33 	if(chatty)
34 		fprint(2, "bad sig %.2ux %.2ux %.2uxn", buf[0], buf[1], buf[2]);
35 
36 	return 0;
37 }
38 
39 int
dosfs(Xfs * xf)40 dosfs(Xfs *xf)
41 {
42 	Iosect *p, *p1;
43 	Dosboot *b;
44 	Fatinfo *fi;
45 	Dosboot32 *b32;
46 	Dosbpb *bp;
47 	long fisec, extflags;
48 	int i;
49 
50 	if(!isdos['a']){
51 		for(i = 'a'; i <= 'z'; i++)
52 			isdos[i] = 1;
53 		for(i = 'A'; i <= 'Z'; i++)
54 			isdos[i] = 1;
55 		for(i = '0'; i <= '9'; i++)
56 			isdos[i] = 1;
57 		isdos['$'] = 1;
58 		isdos['%'] = 1;
59 		isdos['''] = 1;
60 		isdos['-'] = 1;
61 		isdos['_'] = 1;
62 		isdos['@'] = 1;
63 		isdos['~'] = 1;
64 		isdos['`'] = 1;
65 		isdos['!'] = 1;
66 		isdos['('] = 1;
67 		isdos[')'] = 1;
68 		isdos['{'] = 1;
69 		isdos['}'] = 1;
70 		isdos['^'] = 1;
71 		isdos['#'] = 1;
72 		isdos['&'] = 1;
73 	}
74 
75 	p = getsect(xf, 0);
76 	if(p == 0)
77 		return -1;
78 
79 	b = (Dosboot*)p->iobuf;
80 	if(b->clustsize == 0 || isdosfs(p->iobuf) == 0){
81 		putsect(p);
82 		return -1;
83 	}
84 
85 	bp = malloc(sizeof(Dosbpb));
86 	memset(bp, 0, sizeof(Dosbpb));	/* clear lock */
87 	xf->ptr = bp;
88 	xf->fmt = 1;
89 
90 	bp->sectsize = GSHORT(b->sectsize);
91 	bp->clustsize = b->clustsize;
92 	bp->nresrv = GSHORT(b->nresrv);
93 	bp->nfats = b->nfats;
94 	bp->rootsize = GSHORT(b->rootsize);
95 	bp->volsize = GSHORT(b->volsize);
96 	if(bp->volsize == 0)
97 		bp->volsize = GLONG(b->bigvolsize);
98 	bp->mediadesc = b->mediadesc;
99 	bp->fatsize = GSHORT(b->fatsize);
100 	bp->fataddr = GSHORT(b->nresrv);
101 
102 	bp->fatinfo = 0;
103 
104 	if(bp->fatsize == 0){	/* is FAT32 */
105 		if(chatty)
106 			bootsecdump32(2, xf, (Dosboot32*)b);
107 		xf->isfat32 = 1;
108 		b32 = (Dosboot32*)b;
109 		bp->fatsize = GLONG(b32->fatsize32);
110 		if(bp->fatsize == 0){
111 			putsect(p);
112 			if(chatty)
113 				fprint(2, "fatsize 0\n");
114 			return -1;
115 		}
116 		bp->dataaddr = bp->fataddr + bp->nfats*bp->fatsize;
117 		bp->rootaddr = 0;
118 		bp->rootstart = GLONG(b32->rootstart);
119 
120 		/*
121 		 * disable fat mirroring?
122 		 */
123 		extflags = GSHORT(b32->extflags);
124 		if(extflags & 0x0080){
125 			for(i = 0; i < 4; i++){
126 				if(extflags & (1 << i)){
127 					bp->fataddr += i * bp->fatsize;
128 					bp->nfats = 1;
129 					break;
130 				}
131 			}
132 		}
133 
134 		/*
135 		 * fat free list info
136 		 */
137 		bp->freeptr = FATRESRV;
138 		fisec = GSHORT(b32->infospec);
139 		if(fisec != 0 && fisec < GSHORT(b32->nresrv)){
140 			p1 = getsect(xf, fisec);
141 			if(p1 != nil){
142 				fi = (Fatinfo*)p1->iobuf;
143 				if(GLONG(fi->sig1) == FATINFOSIG1 && GLONG(fi->sig) == FATINFOSIG){
144 					bp->fatinfo = fisec;
145 					bp->freeptr = GLONG(fi->nextfree);
146 					bp->freeclusters = GLONG(fi->freeclust);
147 					chat("fat info: %ld free clusters, next free %ld\n", bp->freeclusters, bp->freeptr);
148 				}
149 				putsect(p1);
150 			}
151 		}
152 	}else{
153 		if(chatty)
154 			bootdump(2, b);
155 		bp->rootaddr = bp->fataddr + bp->nfats*bp->fatsize;
156 		bp->rootstart = 0;
157 		i = bp->rootsize*DOSDIRSIZE + bp->sectsize-1;
158 		i /= bp->sectsize;
159 		bp->dataaddr = bp->rootaddr + i;
160 		bp->freeptr = FATRESRV;
161 	}
162 	bp->fatclusters = FATRESRV+(bp->volsize - bp->dataaddr)/bp->clustsize;
163 
164 	if(xf->isfat32)
165 		bp->fatbits = 32;
166 	else if(bp->fatclusters < 4087)
167 		bp->fatbits = 12;
168 	else
169 		bp->fatbits = 16;
170 
171 	chat("fatbits=%d (%d clusters)...", bp->fatbits, bp->fatclusters);
172 	for(i=0; i<b->nfats; i++)
173 		chat("fat %d: %ld...", i, bp->fataddr+i*bp->fatsize);
174 	chat("root: %ld...", bp->rootaddr);
175 	chat("data: %ld...", bp->dataaddr);
176 	putsect(p);
177 	return 0;
178 }
179 
180 /*
181  * initialize f to the root directory
182  * this file has no Dosdir entry,
183  * so we special case it all over.
184  */
185 void
186 rootfile(Xfile *f)
187 {
188 	Dosptr *dp;
189 
190 	dp = f->ptr;
191 	memset(dp, 0, sizeof(Dosptr));
192 	dp->prevaddr = -1;
193 }
194 
195 int
196 isroot(ulong addr)
197 {
198 	return addr == 0;
199 }
200 
201 int
202 getfile(Xfile *f)
203 {
204 	Dosptr *dp;
205 	Iosect *p;
206 
207 	dp = f->ptr;
208 	if(dp->p)
209 		panic("getfile");
210 	p = getsect(f->xf, dp->addr);
211 	if(p == nil)
212 		return -1;
213 
214 	/*
215 	 * we could also make up a Dosdir for the root
216 	 */
217 	dp->d = nil;
218 	if(!isroot(dp->addr)){
219 		if(f->qid.path != QIDPATH(dp)){
220 			chat("qid mismatch f=%#llux d=%#lux...", f->qid.path, QIDPATH(dp));
221 			putsect(p);
222 			errno = Enonexist;
223 			return -1;
224 		}
225 		dp->d = (Dosdir *)&p->iobuf[dp->offset];
226 	}
227 	dp->p = p;
228 	return 0;
229 }
230 
231 void
232 putfile(Xfile *f)
233 {
234 	Dosptr *dp;
235 
236 	dp = f->ptr;
237 	if(!dp->p)
238 		panic("putfile");
239 	putsect(dp->p);
240 	dp->p = nil;
241 	dp->d = nil;
242 }
243 
244 long
245 getstart(Xfs *xf, Dosdir *d)
246 {
247 	long start;
248 
249 	start = GSHORT(d->start);
250 	if(xf->isfat32)
251 		start |= GSHORT(d->hstart)<<16;
252 	return start;
253 }
254 
255 void
256 putstart(Xfs *xf, Dosdir *d, long start)
257 {
258 	PSHORT(d->start, start);
259 	if(xf->isfat32)
260 		PSHORT(d->hstart, start>>16);
261 }
262 
263 /*
264  * return the disk cluster for the iclust cluster in f
265  */
266 long
267 fileclust(Xfile *f, long iclust, int cflag)
268 {
269 	Dosbpb *bp;
270 	Dosptr *dp;
271 	Dosdir *d;
272 	long start, clust, nskip, next;
273 
274 	bp = f->xf->ptr;
275 	dp = f->ptr;
276 	d = dp->d;
277 	next = 0;
278 
279 	/*
280 	 * asking for the cluster of the root directory
281 	 * is not a well-formed question, since the root directory
282 	 * does not begin on a cluster boundary.
283 	 */
284 	if(!f->xf->isfat32 && isroot(dp->addr))
285 		return -1;
286 
287 	if(f->xf->isfat32 && isroot(dp->addr)){
288 		start = bp->rootstart;
289 	}else{
290 		start = getstart(f->xf, d);
291 		if(start == 0){
292 			if(!cflag)
293 				return -1;
294 			mlock(bp);
295 			start = falloc(f->xf);
296 			unmlock(bp);
297 			if(start <= 0)
298 				return -1;
299 			puttime(d, 0);
300 			putstart(f->xf, d, start);
301 			dp->p->flags |= BMOD;
302 			dp->clust = 0;
303 		}
304 	}
305 	if(dp->clust == 0 || iclust < dp->iclust){
306 		clust = start;
307 		nskip = iclust;
308 	}else{
309 		clust = dp->clust;
310 		nskip = iclust - dp->iclust;
311 	}
312 	if(chatty > 1 && nskip > 0)
313 		chat("clust %#lx, skip %ld...", clust, nskip);
314 	if(clust <= 0)
315 		return -1;
316 	if(nskip > 0){
317 		mlock(bp);
318 		while(--nskip >= 0){
319 			next = getfat(f->xf, clust);
320 			if(chatty > 1)
321 				chat("->%#lx", next);
322 			if(next > 0){
323 				clust = next;
324 				continue;
325 			}else if(!cflag)
326 				break;
327 			if(d && (d->attr&DSYSTEM)){
328 				next = cfalloc(f);
329 				if(next < 0)
330 					break;
331 				/* cfalloc will call putfat for us, since clust may change */
332 			} else {
333 				next = falloc(f->xf);
334 				if(next < 0)
335 					break;
336 				putfat(f->xf, clust, next);
337 			}
338 			clust = next;
339 		}
340 		unmlock(bp);
341 		if(next <= 0)
342 			return -1;
343 		dp->clust = clust;
344 		dp->iclust = iclust;
345 	}
346 	if(chatty > 1)
347 		chat(" clust(%#lx)=%#lx...", iclust, clust);
348 	return clust;
349 }
350 
351 /*
352  * return the disk sector for the isect disk sector in f
353  */
354 long
355 fileaddr(Xfile *f, long isect, int cflag)
356 {
357 	Dosbpb *bp;
358 	Dosptr *dp;
359 	long clust;
360 
361 	bp = f->xf->ptr;
362 	dp = f->ptr;
363 	if(!f->xf->isfat32 && isroot(dp->addr)){
364 		if(isect*bp->sectsize >= bp->rootsize*DOSDIRSIZE)
365 			return -1;
366 		return bp->rootaddr + isect;
367 	}
368 	clust = fileclust(f, isect/bp->clustsize, cflag);
369 	if(clust < 0)
370 		return -1;
371 
372 	return clust2sect(bp, clust) + isect%bp->clustsize;
373 }
374 
375 /*
376  * translate names
377  */
378 void
379 fixname(char *buf)
380 {
381 	int c;
382 	char *p;
383 
384 	p = buf;
385 	while(c = *p){
386 		if(c == ':' && trspaces)
387 			*p = ' ';
388 		p++;
389 	}
390 }
391 
392 /*
393  * classify the file name as one of
394  *	Invalid - contains a bad character
395  *	Short - short valid 8.3 name, no lowercase letters
396  *	ShortLower - short valid 8.3 name except for lowercase letters
397  *	Long - long name
398  */
399 int
400 classifyname(char *buf)
401 {
402 	char *p, *dot;
403 	int c, isextended, is8dot3, islower, ndot;
404 
405 	p = buf;
406 	isextended = 0;
407 	islower = 0;
408 	dot = nil;
409 	ndot = 0;
410 	while(c = (uchar)*p){
411 		if(c&0x80)	/* UTF8 */
412 			isextended = 1;
413 		else if(c == '.'){
414 			dot = p;
415 			ndot++;
416 		}else if(strchr("+,:;=[] ", c))
417 			isextended = 1;
418 		else if(!isdos[c])
419 			return Invalid;
420 		if('a' <= c && c <= 'z')
421 			islower = 1;
422 		p++;
423 	}
424 
425 	is8dot3 = (ndot==0 && p-buf <= 8) || (ndot==1 && dot-buf <= 8 && p-(dot+1) <= 3);
426 
427 	if(!isextended && is8dot3){
428 		if(islower)
429 			return ShortLower;
430 		return Short;
431 	}
432 	return Long;
433 }
434 
435 /*
436  * make an alias for a valid long file name
437  */
438 void
439 mkalias(char *name, char *sname, int id)
440 {
441 	Rune r;
442 	char *s, *e, sid[10];
443 	int i, esuf, v;
444 
445 	e = strrchr(name, '.');
446 	if(e == nil)
447 		e = strchr(name, '\0');
448 
449 	s = name;
450 	i = 0;
451 	while(s < e && i < 6){
452 		if(isdos[(uchar)*s])
453 			sname[i++] = *s++;
454 		else
455 			s += chartorune(&r, s);
456 	}
457 
458 	v = snprint(sid, 10, "%d", id);
459 	if(i + 1 + v > 8)
460 		i = 8 - 1 - v;
461 	sname[i++] = '~';
462 	strcpy(&sname[i], sid);
463 	i += v;
464 
465 	sname[i++] = '.';
466 	esuf = i + 3;
467 	if(esuf > 12)
468 		panic("bad mkalias");
469 	while(*e && i < esuf){
470 		if(isdos[(uchar)*e])
471 			sname[i++] = *e++;
472 		else
473 			e += chartorune(&r, e);
474 	}
475 	if(sname[i-1] == '.')
476 		i--;
477 	sname[i] = '\0';
478 }
479 
480 /*
481  * check for valid plan 9 names,
482  * rewrite ' ' to ':'
483  */
484 char isfrog[256]={
485 	/*NUL*/	1, 1, 1, 1, 1, 1, 1, 1,
486 	/*BKS*/	1, 1, 1, 1, 1, 1, 1, 1,
487 	/*DLE*/	1, 1, 1, 1, 1, 1, 1, 1,
488 	/*CAN*/	1, 1, 1, 1, 1, 1, 1, 1,
489 /*	[' ']	1,	let's try this -rsc */
490 	['/']	1,
491 	[0x7f]	1,
492 };
493 
494 int
495 nameok(char *elem)
496 {
497 	while(*elem) {
498 		if(*elem == ' ' && trspaces)
499 			*elem = ':';
500 		if(isfrog[*(uchar*)elem])
501 			return 0;
502 		elem++;
503 	}
504 	return 1;
505 }
506 
507 /*
508  * look for a directory entry matching name
509  * always searches for long names which match a short name
510  */
511 int
512 searchdir(Xfile *f, char *name, Dosptr *dp, int cflag, int longtype)
513 {
514 	Xfs *xf;
515 	Iosect *p;
516 	Dosbpb *bp;
517 	Dosdir *d;
518 	char buf[261], *bname;
519 	int isect, addr, o, addr1, addr2, prevaddr, prevaddr1, o1, islong, have, need, sum;
520 
521 	xf = f->xf;
522 	bp = xf->ptr;
523 	addr1 = -1;
524 	addr2 = -1;
525 	prevaddr1 = -1;
526 	o1 = 0;
527 	islong = 0;
528 	sum = -1;
529 
530 	need = 1;
531 	if(longtype!=Short && cflag)
532 		need += (utflen(name) + DOSRUNE-1) / DOSRUNE;
533 
534 	memset(dp, 0, sizeof(Dosptr));
535 	dp->prevaddr = -1;
536 	dp->naddr = -1;
537 	dp->paddr = ((Dosptr *)f->ptr)->addr;
538 	dp->poffset = ((Dosptr *)f->ptr)->offset;
539 
540 	have = 0;
541 	addr = -1;
542 	bname = nil;
543 	for(isect=0;; isect++){
544 		prevaddr = addr;
545 		addr = fileaddr(f, isect, cflag);
546 		if(addr < 0)
547 			break;
548 		p = getsect(xf, addr);
549 		if(p == 0)
550 			break;
551 		for(o=0; o<bp->sectsize; o+=DOSDIRSIZE){
552 			d = (Dosdir *)&p->iobuf[o];
553 			if(d->name[0] == 0x00){
554 				chat("end dir(0)...");
555 				putsect(p);
556 				if(!cflag)
557 					return -1;
558 
559 				/*
560 				 * addr1 & o1 are the start of the dirs
561 				 * addr2 is the optional second cluster used if the long name
562 				 * entry does not fit within the addr1 cluster
563 				 *
564 				 * have tells us the number of contiguous free dirs
565 				 * starting at addr1.o1; need are necessary to hold the long name.
566 				 */
567 				if(addr1 < 0){
568 					addr1 = addr;
569 					prevaddr1 = prevaddr;
570 					o1 = o;
571 				}
572 				if(addr2 < 0 && (bp->sectsize-o)/DOSDIRSIZE + have < need){
573 					addr2 = fileaddr(f, isect+1, cflag);
574 					if(addr2 < 0)
575 						goto breakout;
576 				}else if(addr2 < 0)
577 					addr2 = addr;
578 				if(addr2 == addr1)
579 					addr2 = -1;
580 				dp->addr = addr1;
581 				dp->offset = o1;
582 				dp->prevaddr = prevaddr1;
583 				dp->naddr = addr2;
584 				return 0;
585 			}
586 			if(d->name[0] == DOSEMPTY){
587 				if(chatty)
588 					fprint(2, "empty dir\n");
589 
590 				have++;
591 				if(addr1 == -1){
592 					addr1 = addr;
593 					o1 = o;
594 					prevaddr1 = prevaddr;
595 				}
596 				if(addr2 == -1 && have >= need)
597 					addr2 = addr;
598 				continue;
599 			}
600 			have = 0;
601 			if(addr2 == -1)
602 				addr1 = -1;
603 
604 			dirdump(d);
605 			if((d->attr & 0xf) == 0xf){
606 				bname = getnamesect(buf, bname, p->iobuf + o, &islong, &sum, 1);
607 				continue;
608 			}
609 			if(d->attr & DVLABEL){
610 				islong = 0;
611 				continue;
612 			}
613 			if(islong != 1 || sum != aliassum(d) || cistrcmp(bname, name) != 0){
614 				bname = buf;
615 				getname(buf, d);
616 			}
617 			islong = 0;
618 			if(cistrcmp(bname, name) != 0)
619 				continue;
620 			if(chatty)
621 				fprint(2, "found\n");
622 			if(cflag){
623 				putsect(p);
624 				return -1;
625 			}
626 			dp->addr = addr;
627 			dp->prevaddr = prevaddr;
628 			dp->offset = o;
629 			dp->p = p;
630 			dp->d = d;
631 			return 0;
632 		}
633 		putsect(p);
634 	}
635 breakout:
636 	chat("end dir(1)...");
637 	return -1;
638 }
639 
640 int
641 emptydir(Xfile *f)
642 {
643 	Xfs *xf = f->xf;
644 	Dosbpb *bp = xf->ptr;
645 	int isect, addr, o;
646 	Iosect *p;
647 	Dosdir *d;
648 
649 	for(isect=0;; isect++){
650 		addr = fileaddr(f, isect, 0);
651 		if(addr < 0)
652 			break;
653 		p = getsect(xf, addr);
654 		if(p == 0)
655 			return -1;
656 		for(o=0; o<bp->sectsize; o+=DOSDIRSIZE){
657 			d = (Dosdir *)&p->iobuf[o];
658 			if(d->name[0] == 0x00){
659 				putsect(p);
660 				return 0;
661 			}
662 			if(d->name[0] == DOSEMPTY)
663 				continue;
664 			if(d->name[0] == '.')
665 				continue;
666 			if(d->attr&DVLABEL)
667 				continue;
668 			putsect(p);
669 			return -1;
670 		}
671 		putsect(p);
672 	}
673 	return 0;
674 }
675 
676 long
677 readdir(Xfile *f, void *vbuf, long offset, long count)
678 {
679 	Xfs *xf;
680 	Dosbpb *bp;
681 	Dir dir;
682 	int isect, addr, o, islong, sum;
683 	Iosect *p;
684 	Dosdir *d;
685 	long rcnt, n;
686 	char *name, snamebuf[8+1+3+1], namebuf[DOSNAMELEN];
687 	uchar *buf;
688 
689 	buf = vbuf;
690 	rcnt = 0;
691 	xf = f->xf;
692 	bp = xf->ptr;
693 	if(count <= 0)
694 		return 0;
695 	islong = 0;
696 	sum = -1;
697 	name = nil;
698 	for(isect=0;; isect++){
699 		addr = fileaddr(f, isect, 0);
700 		if(addr < 0)
701 			break;
702 		p = getsect(xf, addr);
703 		if(p == 0)
704 			return -1;
705 		for(o=0; o<bp->sectsize; o+=DOSDIRSIZE){
706 			d = (Dosdir *)&p->iobuf[o];
707 			if(d->name[0] == 0x00){
708 				putsect(p);
709 				return rcnt;
710 			}
711 			if(d->name[0] == DOSEMPTY)
712 				continue;
713 			dirdump(d);
714 			if(d->name[0] == '.'){
715 				if(d->name[1] == ' ' || d->name[1] == 0)
716 					continue;
717 				if(d->name[1] == '.' &&
718 				  (d->name[2] == ' ' || d->name[2] == 0))
719 					continue;
720 			}
721 			if((d->attr & 0xf) == 0xf){
722 				name = getnamesect(namebuf, name, p->iobuf+o, &islong, &sum, 1);
723 				continue;
724 			}
725 			if(d->attr & DVLABEL){
726 				islong = 0;
727 				continue;
728 			}
729 			dir.name = snamebuf;
730 			getdir(xf, &dir, d, addr, o);
731 			if(islong == 1 && nameok(name) && sum == aliassum(d))
732 				dir.name = name;
733 			islong = 0;
734 			n = convD2M(&dir, &buf[rcnt], count - rcnt);
735 			name = nil;
736 			if(n <= BIT16SZ){	/* no room for next entry */
737 				putsect(p);
738 				return rcnt;
739 			}
740 			rcnt += n;
741 			if(offset > 0){
742 				offset -= rcnt;
743 				rcnt = 0;
744 				islong = 0;
745 				continue;
746 			}
747 			if(rcnt == count){
748 				putsect(p);
749 				return rcnt;
750 			}
751 		}
752 		putsect(p);
753 	}
754 	return rcnt;
755 }
756 
757 /*
758  * set up ndp for a directory's parent
759  * the hardest part is setting up paddr
760  */
761 int
762 walkup(Xfile *f, Dosptr *ndp)
763 {
764 	Dosbpb *bp;
765 	Dosptr *dp;
766 	Dosdir *xd;
767 	Iosect *p;
768 	long k, o, so, start, pstart, ppstart, st, ppclust;
769 
770 	bp = f->xf->ptr;
771 	dp = f->ptr;
772 	memset(ndp, 0, sizeof(Dosptr));
773 	ndp->prevaddr = -1;
774 	ndp->naddr = -1;
775 	ndp->addr = dp->paddr;
776 	ndp->offset = dp->poffset;
777 
778 	chat("walkup: paddr=%#lx...", dp->paddr);
779 
780 	/*
781 	 * root's paddr is always itself
782 	 */
783 	if(isroot(dp->paddr))
784 		return 0;
785 
786 	/*
787 	 * find the start of our parent's directory
788 	 */
789 	p = getsect(f->xf, dp->paddr);
790 	if(p == nil)
791 		goto error;
792 	xd = (Dosdir *)&p->iobuf[dp->poffset];
793 	dirdump(xd);
794 	start = getstart(f->xf, xd);
795 	chat("start=%#lx...", start);
796 	if(start == 0)
797 		goto error;
798 	putsect(p);
799 
800 	/*
801 	 * verify that parent's . points to itself
802 	 */
803 	p = getsect(f->xf, clust2sect(bp, start));
804 	if(p == nil)
805 		goto error;
806 	xd = (Dosdir *)p->iobuf;
807 	dirdump(xd);
808 	st = getstart(f->xf, xd);
809 	if(xd->name[0]!='.' || xd->name[1]!=' ' || start!=st)
810 		goto error;
811 
812 	/*
813 	 * parent's .. is the next entry, and has start of parent's parent
814 	 */
815 	xd++;
816 	dirdump(xd);
817 	if(xd->name[0] != '.' || xd->name[1] != '.')
818 		goto error;
819 	pstart = getstart(f->xf, xd);
820 	putsect(p);
821 
822 	/*
823 	 * we're done if parent is root
824 	 */
825 	if(pstart == 0 || f->xf->isfat32 && pstart == bp->rootstart)
826 		return 0;
827 
828 	/*
829 	 * verify that parent's . points to itself
830 	 */
831 	p = getsect(f->xf, clust2sect(bp, pstart));
832 	if(p == 0){
833 		chat("getsect %ld failed\n", pstart);
834 		goto error;
835 	}
836 	xd = (Dosdir *)p->iobuf;
837 	dirdump(xd);
838 	st = getstart(f->xf, xd);
839 	if(xd->name[0]!='.' || xd->name[1]!=' ' || pstart!=st)
840 		goto error;
841 
842 	/*
843 	 * parent's parent's .. is the next entry, and has start of parent's parent's parent
844 	 */
845 	xd++;
846 	dirdump(xd);
847 	if(xd->name[0] != '.' || xd->name[1] != '.')
848 		goto error;
849 	ppstart = getstart(f->xf, xd);
850 	putsect(p);
851 
852 	/*
853 	 * open parent's parent's parent, and walk through it until parent's parent is found
854 	 * need this to find parent's parent's addr and offset
855 	 */
856 	ppclust = ppstart;
857 	if(f->xf->isfat32 && ppclust == 0){
858 		ppclust = bp->rootstart;
859 		chat("ppclust 0, resetting to rootstart\n");
860 	}
861 	k = ppclust ? clust2sect(bp, ppclust) : bp->rootaddr;
862 	p = getsect(f->xf, k);
863 	if(p == nil){
864 		chat("getsect %ld failed\n", k);
865 		goto error;
866 	}
867 	xd = (Dosdir *)p->iobuf;
868 	dirdump(xd);
869 	if(ppstart){
870 		st = getstart(f->xf, xd);
871 		if(xd->name[0]!='.' || xd->name[1]!=' ' || ppstart!=st)
872 			goto error;
873 	}
874 	for(so=1;; so++){
875 		for(o=0; o<bp->sectsize; o+=DOSDIRSIZE){
876 			xd = (Dosdir *)&p->iobuf[o];
877 			if(xd->name[0] == 0x00){
878 				chat("end dir\n");
879 				goto error;
880 			}
881 			if(xd->name[0] == DOSEMPTY)
882 				continue;
883 			st = getstart(f->xf, xd);
884 			if(st == pstart)
885 				goto out;
886 		}
887 		if(ppclust){
888 			if(so%bp->clustsize == 0){
889 				mlock(bp);
890 				ppclust = getfat(f->xf, ppclust);
891 				unmlock(bp);
892 				if(ppclust < 0){
893 					chat("getfat %ld failed\n", ppclust);
894 					goto error;
895 				}
896 			}
897 			k = clust2sect(bp, ppclust) +
898 				so%bp->clustsize;
899 		}else{
900 			if(so*bp->sectsize >= bp->rootsize*DOSDIRSIZE)
901 				goto error;
902 			k = bp->rootaddr + so;
903 		}
904 		putsect(p);
905 		p = getsect(f->xf, k);
906 		if(p == 0){
907 			chat("getsect %ld failed\n", k);
908 			goto error;
909 		}
910 	}
911 out:
912 	putsect(p);
913 	ndp->paddr = k;
914 	ndp->poffset = o;
915 	return 0;
916 
917 error:
918 	if(p)
919 		putsect(p);
920 	return -1;
921 }
922 
923 long
924 readfile(Xfile *f, void *vbuf, long offset, long count)
925 {
926 	Xfs *xf = f->xf;
927 	Dosbpb *bp = xf->ptr;
928 	Dosptr *dp = f->ptr;
929 	Dosdir *d = dp->d;
930 	int isect, addr, o, c;
931 	Iosect *p;
932 	uchar *buf;
933 	long length, rcnt;
934 
935 	rcnt = 0;
936 	length = GLONG(d->length);
937 	buf = vbuf;
938 	if(offset >= length)
939 		return 0;
940 	if(offset+count >= length)
941 		count = length - offset;
942 	isect = offset/bp->sectsize;
943 	o = offset%bp->sectsize;
944 	while(count > 0){
945 		addr = fileaddr(f, isect++, 0);
946 		if(addr < 0)
947 			break;
948 		c = bp->sectsize - o;
949 		if(c > count)
950 			c = count;
951 		p = getsect(xf, addr);
952 		if(p == 0)
953 			return -1;
954 		memmove(&buf[rcnt], &p->iobuf[o], c);
955 		putsect(p);
956 		count -= c;
957 		rcnt += c;
958 		o = 0;
959 	}
960 	return rcnt;
961 }
962 
963 long
964 writefile(Xfile *f, void *vbuf, long offset, long count)
965 {
966 	Xfs *xf = f->xf;
967 	Dosbpb *bp = xf->ptr;
968 	Dosptr *dp = f->ptr;
969 	Dosdir *d = dp->d;
970 	int isect, addr = 0, o, c;
971 	Iosect *p;
972 	uchar *buf;
973 	long length, rcnt = 0, dlen;
974 
975 	buf = vbuf;
976 	isect = offset/bp->sectsize;
977 	o = offset%bp->sectsize;
978 	while(count > 0){
979 		addr = fileaddr(f, isect++, 1);
980 		if(addr < 0)
981 			break;
982 		c = bp->sectsize - o;
983 		if(c > count)
984 			c = count;
985 		if(c == bp->sectsize){
986 			p = getosect(xf, addr);
987 			p->flags = 0;
988 		}else{
989 			p = getsect(xf, addr);
990 			if(p == nil)
991 				return -1;
992 		}
993 		memmove(&p->iobuf[o], &buf[rcnt], c);
994 		p->flags |= BMOD;
995 		putsect(p);
996 		count -= c;
997 		rcnt += c;
998 		o = 0;
999 	}
1000 	if(rcnt <= 0 && addr < 0)
1001 		return -1;
1002 	length = 0;
1003 	dlen = GLONG(d->length);
1004 	if(rcnt > 0)
1005 		length = offset+rcnt;
1006 	else if(dp->addr && dp->clust){
1007 		c = bp->clustsize*bp->sectsize;
1008 		if(dp->iclust > (dlen+c-1)/c)
1009 			length = c*dp->iclust;
1010 	}
1011 	if(length > dlen)
1012 		PLONG(d->length, length);
1013 	puttime(d, 0);
1014 	dp->p->flags |= BMOD;
1015 	return rcnt;
1016 }
1017 
1018 int
1019 truncfile(Xfile *f, long length)
1020 {
1021 	Xfs *xf = f->xf;
1022 	Dosbpb *bp = xf->ptr;
1023 	Dosptr *dp = f->ptr;
1024 	Dosdir *d = dp->d;
1025 	long clust, next, n;
1026 
1027 	mlock(bp);
1028 	clust = getstart(f->xf, d);
1029 	n = length;
1030 	if(n <= 0)
1031 		putstart(f->xf, d, 0);
1032 	else
1033 		n -= bp->sectsize;
1034 	while(clust > 0){
1035 		next = getfat(xf, clust);
1036 		if(n <= 0)
1037 			putfat(xf, clust, 0);
1038 		else
1039 			n -= bp->clustsize*bp->sectsize;
1040 		clust = next;
1041 	}
1042 	unmlock(bp);
1043 	PLONG(d->length, length);
1044 	dp->iclust = 0;
1045 	dp->clust = 0;
1046 	dp->p->flags |= BMOD;
1047 	return 0;
1048 }
1049 
1050 void
1051 putdir(Dosdir *d, Dir *dp)
1052 {
1053 	if(dp->mode != ~0){
1054 		if(dp->mode & 2)
1055 			d->attr &= ~DRONLY;
1056 		else
1057 			d->attr |= DRONLY;
1058 		if(dp->mode & DMEXCL)
1059 			d->attr |= DSYSTEM;
1060 		else
1061 			d->attr &= ~DSYSTEM;
1062 	}
1063 	if(dp->mtime != ~0)
1064 		puttime(d, dp->mtime);
1065 }
1066 
1067 /*
1068  * should extend this to deal with
1069  * creation and access dates
1070  */
1071 void
1072 getdir(Xfs *xfs, Dir *dp, Dosdir *d, int addr, int offset)
1073 {
1074 	if(d == nil || addr == 0)
1075 		panic("getdir on root");
1076 	dp->type = 0;
1077 	dp->dev = 0;
1078 	getname(dp->name, d);
1079 
1080 	dp->qid.path = addr*(Sectorsize/DOSDIRSIZE) +
1081 			offset/DOSDIRSIZE;
1082 	dp->qid.vers = 0;
1083 
1084 	if(d->attr & DRONLY)
1085 		dp->mode = 0444;
1086 	else
1087 		dp->mode = 0666;
1088 	dp->atime = gtime(d);
1089 	dp->mtime = dp->atime;
1090 	dp->qid.type = QTFILE;
1091 	if(d->attr & DDIR){
1092 		dp->qid.type = QTDIR;
1093 		dp->mode |= DMDIR|0111;
1094 		dp->length = 0;
1095 	}else
1096 		dp->length = GLONG(d->length);
1097 	if(d->attr & DSYSTEM){
1098 		dp->mode |= DMEXCL;
1099 		if(iscontig(xfs, d))
1100 			dp->mode |= DMAPPEND;
1101 	}
1102 
1103 	dp->uid = "bill";
1104 	dp->muid = "bill";
1105 	dp->gid = "trog";
1106 }
1107 
1108 void
1109 getname(char *p, Dosdir *d)
1110 {
1111 	int c, i;
1112 
1113 	for(i=0; i<8; i++){
1114 		c = d->name[i];
1115 		if(c == '\0' || c == ' ')
1116 			break;
1117 		if(i == 0 && c == 0x05)
1118 			c = 0xe5;
1119 		*p++ = c;
1120 	}
1121 	for(i=0; i<3; i++){
1122 		c = d->ext[i];
1123 		if(c == '\0' || c == ' ')
1124 			break;
1125 		if(i == 0)
1126 			*p++ = '.';
1127 		*p++ = c;
1128 	}
1129 	*p = 0;
1130 }
1131 
1132 static char*
1133 getnamerunes(char *dst, uchar *buf, int step)
1134 {
1135 	int i;
1136 	Rune r;
1137 	char dbuf[DOSRUNE * UTFmax + 1], *d;
1138 
1139 	d = dbuf;
1140 	r = 1;
1141 	for(i = 1; r && i < 11; i += 2){
1142 		r = buf[i] | (buf[i+1] << 8);
1143 		d += runetochar(d, &r);
1144 	}
1145 	for(i = 14; r && i < 26; i += 2){
1146 		r = buf[i] | (buf[i+1] << 8);
1147 		d += runetochar(d, &r);
1148 	}
1149 	for(i = 28; r && i < 32; i += 2){
1150 		r = buf[i] | (buf[i+1] << 8);
1151 		d += runetochar(d, &r);
1152 	}
1153 
1154 	if(step == 1)
1155 		dst -= d - dbuf;
1156 
1157 	memmove(dst, dbuf, d - dbuf);
1158 
1159 	if(step == -1){
1160 		dst += d - dbuf;
1161 		*dst = '\0';
1162 	}
1163 
1164 	return dst;
1165 }
1166 
1167 char*
1168 getnamesect(char *dbuf, char *d, uchar *buf, int *islong, int *sum, int step)
1169 {
1170 	/*
1171 	 * validation checks to make sure we're
1172 	 * making up a consistent name
1173 	 */
1174 	if(buf[11] != 0xf || buf[12] != 0){
1175 		*islong = 0;
1176 		return nil;
1177 	}
1178 	if(step == 1){
1179 		if((buf[0] & 0xc0) == 0x40){
1180 			*islong = buf[0] & 0x3f;
1181 			*sum = buf[13];
1182 			d = dbuf + DOSNAMELEN;
1183 			*--d = '\0';
1184 		}else if(*islong && *islong == buf[0] + 1 && *sum == buf[13]){
1185 			*islong = buf[0];
1186 		}else{
1187 			*islong = 0;
1188 			return nil;
1189 		}
1190 	}else{
1191 		if(*islong + 1 == (buf[0] & 0xbf) && *sum == buf[13]){
1192 			*islong = buf[0] & 0x3f;
1193 			if(buf[0] & 0x40)
1194 				*sum = -1;
1195 		}else{
1196 			*islong = 0;
1197 			*sum = -1;
1198 			return nil;
1199 		}
1200 	}
1201 	if(*islong > 20){
1202 		*islong = 0;
1203 		*sum = -1;
1204 		return nil;
1205 	}
1206 
1207 	return getnamerunes(d, buf, step);
1208 }
1209 
1210 void
1211 putname(char *p, Dosdir *d)
1212 {
1213 	int i;
1214 
1215 	memset(d->name, ' ', sizeof d->name+sizeof d->ext);
1216 	for(i=0; i<sizeof d->name; i++){
1217 		if(*p == 0 || *p == '.')
1218 			break;
1219 		d->name[i] = toupper(*p++);
1220 	}
1221 	p = strrchr(p, '.');
1222 	if(p){
1223 		for(i=0; i<sizeof d->ext; i++){
1224 			if(*++p == 0)
1225 				break;
1226 			d->ext[i] = toupper(*p);
1227 		}
1228 	}
1229 }
1230 
1231 static void
1232 putnamesect(uchar *slot, Rune *longname, int curslot, int first, int sum)
1233 {
1234 	Rune r;
1235 	Dosdir ds;
1236 	int i, j;
1237 
1238 	memset(&ds, 0xff, sizeof ds);
1239 	ds.attr = 0xf;
1240 	ds.reserved[0] = 0;
1241 	ds.reserved[1] = sum;
1242 	ds.start[0] = 0;
1243 	ds.start[1] = 0;
1244 	if(first)
1245 		ds.name[0] = 0x40 | curslot;
1246 	else
1247 		ds.name[0] = curslot;
1248 	memmove(slot, &ds, sizeof ds);
1249 
1250 	j = (curslot-1) * DOSRUNE;
1251 
1252 	for(i = 1; i < 11; i += 2){
1253 		r = longname[j++];
1254 		slot[i] = r;
1255 		slot[i+1] = r >> 8;
1256 		if(r == 0)
1257 			return;
1258 	}
1259 	for(i = 14; i < 26; i += 2){
1260 		r = longname[j++];
1261 		slot[i] = r;
1262 		slot[i+1] = r >> 8;
1263 		if(r == 0)
1264 			return;
1265 	}
1266 	for(i = 28; i < 32; i += 2){
1267 		r = longname[j++];
1268 		slot[i] = r;
1269 		slot[i+1] = r >> 8;
1270 		if(r == 0)
1271 			return;
1272 	}
1273 }
1274 
1275 int
1276 aliassum(Dosdir *d)
1277 {
1278 	int i, sum;
1279 
1280 	if(d == nil)
1281 		return -1;
1282 	sum = 0;
1283 	for(i = 0; i < 11; i++)
1284 		sum = (((sum&1)<<7) | ((sum&0xfe)>>1)) + d->name[i];
1285 	return sum & 0xff;
1286 }
1287 
1288 int
1289 putlongname(Xfs *xf, Dosptr *ndp, char *name, char sname[13])
1290 {
1291 	Dosbpb *bp;
1292 	Dosdir tmpd;
1293 	Rune longname[DOSNAMELEN+1];
1294 	int i, first, sum, nds, len;
1295 
1296 	/* calculate checksum */
1297 	putname(sname, &tmpd);
1298 	sum = aliassum(&tmpd);
1299 
1300 	bp = xf->ptr;
1301 	first = 1;
1302 	len = utftorunes(longname, name, DOSNAMELEN);
1303 	if(chatty){
1304 		chat("utftorunes %s =", name);
1305 		for(i=0; i<len; i++)
1306 			chat(" %.4X", longname[i]);
1307 		chat("\n");
1308 	}
1309 	for(nds = (len + DOSRUNE-1) / DOSRUNE; nds > 0; nds--){
1310 		putnamesect(&ndp->p->iobuf[ndp->offset], longname, nds, first, sum);
1311 		first = 0;
1312 		ndp->offset += 32;
1313 		if(ndp->offset == bp->sectsize){
1314 			chat("long name moving over sector boundary\n");
1315 			ndp->p->flags |= BMOD;
1316 			putsect(ndp->p);
1317 			ndp->p = nil;
1318 
1319 			/*
1320 			 * switch to the next cluster for a long entry
1321 			 * naddr should be set up correctly by searchdir
1322 			 */
1323 			ndp->prevaddr = ndp->addr;
1324 			ndp->addr = ndp->naddr;
1325 			ndp->naddr = -1;
1326 			if(ndp->addr == -1)
1327 				return -1;
1328 			ndp->p = getsect(xf, ndp->addr);
1329 			if(ndp->p == nil)
1330 				return -1;
1331 			ndp->offset = 0;
1332 			ndp->d = (Dosdir *)&ndp->p->iobuf[ndp->offset];
1333 		}
1334 	}
1335 	return 0;
1336 }
1337 
1338 long
1339 getfat(Xfs *xf, int n)
1340 {
1341 	Dosbpb *bp = xf->ptr;
1342 	Iosect *p;
1343 	ulong k, sect;
1344 	int o, fb;
1345 
1346 	if(n < FATRESRV || n >= bp->fatclusters)
1347 		return -1;
1348 	fb = bp->fatbits;
1349 	k = (fb * n) >> 3;
1350 	if(k >= bp->fatsize*bp->sectsize)
1351 		panic("getfat");
1352 	sect = k/bp->sectsize + bp->fataddr;
1353 	o = k%bp->sectsize;
1354 	p = getsect(xf, sect);
1355 	if(p == nil)
1356 		return -1;
1357 	k = p->iobuf[o++];
1358 	if(o >= bp->sectsize){
1359 		putsect(p);
1360 		p = getsect(xf, sect+1);
1361 		if(p == nil)
1362 			return -1;
1363 		o = 0;
1364 	}
1365 	k |= p->iobuf[o++]<<8;
1366 	if(fb == 32){
1367 		/* fat32 is really fat28 */
1368 		k |= p->iobuf[o++] << 16;
1369 		k |= (p->iobuf[o] & 0x0f) << 24;
1370 		fb = 28;
1371 	}
1372 	putsect(p);
1373 	if(fb == 12){
1374 		if(n&1)
1375 			k >>= 4;
1376 		else
1377 			k &= 0xfff;
1378 	}
1379 	if(chatty > 1)
1380 		chat("fat(%#x)=%#lx...", n, k);
1381 
1382 	/*
1383 	 * This is a very strange check for out of range.
1384 	 * As a concrete example, for a 16-bit FAT,
1385 	 * FFF8 through FFFF all signify ``end of cluster chain.''
1386 	 * This generalizes to other-sized FATs.
1387 	 */
1388 	if(k >= (1 << fb) - 8)
1389 		return -1;
1390 
1391 	return k;
1392 }
1393 
1394 void
1395 putfat(Xfs *xf, int n, ulong val)
1396 {
1397 	Fatinfo *fi;
1398 	Dosbpb *bp;
1399 	Iosect *p;
1400 	ulong k, sect, esect;
1401 	int o;
1402 
1403 	bp = xf->ptr;
1404 	if(n < FATRESRV || n >= bp->fatclusters)
1405 		panic("putfat n=%d", n);
1406 	k = (bp->fatbits * n) >> 3;
1407 	if(k >= bp->fatsize*bp->sectsize)
1408 		panic("putfat");
1409 	sect = k/bp->sectsize + bp->fataddr;
1410 	esect = sect + bp->nfats * bp->fatsize;
1411 	for(; sect<esect; sect+=bp->fatsize){
1412 		o = k%bp->sectsize;
1413 		p = getsect(xf, sect);
1414 		if(p == nil)
1415 			continue;
1416 		switch(bp->fatbits){
1417 		case 12:
1418 			if(n&1){
1419 				p->iobuf[o] &= 0x0f;
1420 				p->iobuf[o++] |= val<<4;
1421 				if(o >= bp->sectsize){
1422 					p->flags |= BMOD;
1423 					putsect(p);
1424 					p = getsect(xf, sect+1);
1425 					if(p == nil)
1426 						continue;
1427 					o = 0;
1428 				}
1429 				p->iobuf[o] = val>>4;
1430 			}else{
1431 				p->iobuf[o++] = val;
1432 				if(o >= bp->sectsize){
1433 					p->flags |= BMOD;
1434 					putsect(p);
1435 					p = getsect(xf, sect+1);
1436 					if(p == nil)
1437 						continue;
1438 					o = 0;
1439 				}
1440 				p->iobuf[o] &= 0xf0;
1441 				p->iobuf[o] |= (val>>8) & 0x0f;
1442 			}
1443 			break;
1444 		case 16:
1445 			p->iobuf[o++] = val;
1446 			p->iobuf[o] = val>>8;
1447 			break;
1448 		case 32:	/* fat32 is really fat28 */
1449 			p->iobuf[o++] = val;
1450 			p->iobuf[o++] = val>>8;
1451 			p->iobuf[o++] = val>>16;
1452 			p->iobuf[o] = (p->iobuf[o] & 0xf0) | ((val>>24) & 0x0f);
1453 			break;
1454 		default:
1455 			panic("putfat fatbits");
1456 		}
1457 		p->flags |= BMOD;
1458 		putsect(p);
1459 	}
1460 
1461 	if(val == 0)
1462 		bp->freeclusters++;
1463 	else
1464 		bp->freeclusters--;
1465 
1466 	if(bp->fatinfo){
1467 		p = getsect(xf, bp->fatinfo);
1468 		if(p != nil){
1469 			fi = (Fatinfo*)p->iobuf;
1470 			PLONG(fi->nextfree, bp->freeptr);
1471 			PLONG(fi->freeclust, bp->freeclusters);
1472 			p->flags |= BMOD;
1473 			putsect(p);
1474 		}
1475 	}
1476 }
1477 
1478 /*
1479  * Contiguous falloc; if we can, use lastclust+1.
1480  * Otherwise, move the file to get some space.
1481  * If there just isn't enough contiguous space
1482  * anywhere on disk, fail.
1483  */
1484 int
1485 cfalloc(Xfile *f)
1486 {
1487 	int l;
1488 
1489 	if((l=makecontig(f, 8)) >= 0)
1490 		return l;
1491 	return makecontig(f, 1);
1492 }
1493 
1494 /*
1495  * Check whether a file is contiguous.
1496  */
1497 int
1498 iscontig(Xfs *xf, Dosdir *d)
1499 {
1500 	long clust, next;
1501 
1502 	clust = getstart(xf, d);
1503 	if(clust <= 0)
1504 		return 1;
1505 
1506 	for(;;) {
1507 		next = getfat(xf, clust);
1508 		if(next < 0)
1509 			return 1;
1510 		if(next != clust+1)
1511 			return 0;
1512 		clust = next;
1513 	}
1514 }
1515 
1516 /*
1517  * Make a file contiguous, with nextra clusters of
1518  * free space after it for later expansion.
1519  * Return the number of the first new cluster.
1520  */
1521 int
1522 makecontig(Xfile *f, int nextra)
1523 {
1524 	Dosbpb *bp;
1525 	Dosdir *d;
1526 	Dosptr *dp;
1527 	Xfs *xf;
1528 	Iosect *wp, *rp;
1529 	long clust, next, last, start, rclust, wclust, eclust, ostart;
1530 	int isok, i, n, nclust, nrun, rs, ws;
1531 
1532 	xf = f->xf;
1533 	bp = xf->ptr;
1534 	dp = f->ptr;
1535 	d = dp->d;
1536 
1537 	isok = 1;
1538 	nclust = 0;
1539 	clust = fileclust(f, 0, 0);
1540 	chat("clust %#lux", clust);
1541 	if(clust != -1) {
1542 		for(;;) {
1543 			nclust++;
1544 			chat(".");
1545 			next = getfat(xf, clust);
1546 			if(next <= 0)
1547 				break;
1548 			if(next != clust+1)
1549 				isok = 0;
1550 			clust = next;
1551 		}
1552 	}
1553 	chat("nclust %d\n", nclust);
1554 
1555 	if(isok && clust != -1) {
1556 		eclust = clust+1;	/* eclust = first cluster past file */
1557 		assert(eclust == fileclust(f, 0, 0)+nclust);
1558 		for(i=0; i<nextra; i++)
1559 			if(getfat(xf, eclust+i) != 0)
1560 				break;
1561 		if(i == nextra) {	/* they were all free */
1562 			chat("eclust=%#lx, getfat eclust-1 = %#lux\n", eclust, getfat(xf, eclust-1));
1563 			assert(getfat(xf, eclust-1) == 0xffffffff);
1564 			putfat(xf, eclust-1, eclust);
1565 			putfat(xf, eclust, 0xffffffff);
1566 			bp->freeptr = clust+1;	/* to help keep the blocks free */
1567 			return eclust;
1568 		}
1569 	}
1570 
1571 	/* need to search for nclust+nextra contiguous free blocks */
1572 	last = -1;
1573 	n = bp->freeptr;
1574 	nrun = 0;
1575 	for(;;){
1576 		if(getfat(xf, n) == 0) {
1577 			if(last+1 == n)
1578 				nrun++;
1579 			else
1580 				nrun = 1;
1581 			if(nrun >= nclust+nextra)
1582 				break;
1583 			last = n;
1584 		}
1585 		if(++n >= bp->fatclusters)
1586 			n = FATRESRV;
1587 		if(n == bp->freeptr) {
1588 			errno = Econtig;
1589 			return -1;
1590 		}
1591 	}
1592 	bp->freeptr = n+1;
1593 
1594 	/* copy old data over */
1595 	start = n+1 - nrun;
1596 
1597 	/* sanity check */
1598 	for(i=0; i<nclust+nextra; i++)
1599 		assert(getfat(xf, start+i) == 0);
1600 
1601 	chat("relocate chain %lux -> 0x%lux len %d\n", fileclust(f, 0, 0), start, nclust);
1602 
1603 	wclust = start;
1604 	for(rclust = fileclust(f, 0, 0); rclust > 0; rclust = next){
1605 		rs = clust2sect(bp, rclust);
1606 		ws = clust2sect(bp, wclust);
1607 		for(i=0; i<bp->clustsize; i++, rs++, ws++){
1608 			rp = getsect(xf, rs);
1609 			if(rp == nil)
1610 				return -1;
1611 			wp = getosect(xf, ws);
1612 			assert(wp != nil);
1613 			memmove(wp->iobuf, rp->iobuf, bp->sectsize);
1614 			wp->flags = BMOD;
1615 			putsect(rp);
1616 			putsect(wp);
1617 		}
1618 		chat("move cluster %#lx -> %#lx...", rclust, wclust);
1619 		next = getfat(xf, rclust);
1620 		putfat(xf, wclust, wclust+1);
1621 		wclust++;
1622 	}
1623 
1624 	/* now wclust points at the first new cluster; chain it in */
1625 	chat("wclust 0x%lux start 0x%lux (fat->0x%lux) nclust %d\n", wclust, start, getfat(xf, start), nclust);
1626 	assert(wclust == start+nclust);
1627 	putfat(xf, wclust, 0xffffffff);	/* end of file */
1628 
1629 	/* update directory entry to point at new start */
1630 	ostart = fileclust(f, 0, 0);
1631 	putstart(xf, d, start);
1632 
1633 	/* check our work */
1634 	i = 0;
1635 	clust = fileclust(f, 0, 0);
1636 	if(clust != -1) {
1637 		for(;;) {
1638 			i++;
1639 			next = getfat(xf, clust);
1640 			if(next <= 0)
1641 				break;
1642 			assert(next == clust+1);
1643 			clust = next;
1644 		}
1645 	}
1646 	chat("chain check: len %d\n", i);
1647 	assert(i == nclust+1);
1648 
1649 	/* succeeded; remove old chain. */
1650 	for(rclust = ostart; rclust > 0; rclust = next){
1651 		next = getfat(xf, rclust);
1652 		putfat(xf, rclust, 0);	/* free cluster */
1653 	}
1654 
1655 	return start+nclust;
1656 }
1657 
1658 int
1659 falloc(Xfs *xf)
1660 {
1661 	Dosbpb *bp = xf->ptr;
1662 	Iosect *p;
1663 	int n, i, k;
1664 
1665 	n = bp->freeptr;
1666 	for(;;){
1667 		if(getfat(xf, n) == 0)
1668 			break;
1669 		if(++n >= bp->fatclusters)
1670 			n = FATRESRV;
1671 		if(n == bp->freeptr)
1672 			return -1;
1673 	}
1674 	bp->freeptr = n+1;
1675 	if(bp->freeptr >= bp->fatclusters)
1676 		bp->freeptr = FATRESRV;
1677 	putfat(xf, n, 0xffffffff);
1678 	k = clust2sect(bp, n);
1679 	for(i=0; i<bp->clustsize; i++){
1680 		p = getosect(xf, k+i);
1681 		memset(p->iobuf, 0, bp->sectsize);
1682 		p->flags = BMOD;
1683 		putsect(p);
1684 	}
1685 	return n;
1686 }
1687 
1688 void
1689 ffree(Xfs *xf, long start)
1690 {
1691 	putfat(xf, start, 0);
1692 }
1693 
1694 long
1695 clust2sect(Dosbpb *bp, long clust)
1696 {
1697 	return bp->dataaddr + (clust - FATRESRV) * bp->clustsize;
1698 }
1699 
1700 long
1701 sect2clust(Dosbpb *bp, long sect)
1702 {
1703 	long c;
1704 
1705 	c = (sect - bp->dataaddr) / bp->clustsize + FATRESRV;
1706 	assert(sect == clust2sect(bp, c));
1707 	return c;
1708 }
1709 
1710 void
1711 puttime(Dosdir *d, long s)
1712 {
1713 	Tm *t;
1714 	ushort x;
1715 
1716 	if(s == 0)
1717 		s = time(0);
1718 	t = localtime(s);
1719 
1720 	x = (t->hour<<11) | (t->min<<5) | (t->sec>>1);
1721 	PSHORT(d->time, x);
1722 	x = ((t->year-80)<<9) | ((t->mon+1)<<5) | t->mday;
1723 	PSHORT(d->date, x);
1724 }
1725 
1726 long
1727 gtime(Dosdir *dp)
1728 {
1729 	Tm tm;
1730 	int i;
1731 
1732 	i = GSHORT(dp->time);
1733 	tm.hour = i >> 11;
1734 	tm.min = (i >> 5) & 63;
1735 	tm.sec = (i & 31) << 1;
1736 	i = GSHORT(dp->date);
1737 	tm.year = 80 + (i >> 9);
1738 	tm.mon = ((i >> 5) & 15) - 1;
1739 	tm.mday = i & 31;
1740 	tm.zone[0] = '\0';
1741 	tm.tzoff = 0;
1742 	tm.yday = 0;
1743 
1744 	return tm2sec(&tm);
1745 }
1746 
1747 /*
1748  * structure dumps for debugging
1749  */
1750 void
1751 bootdump(int fd, Dosboot *b)
1752 {
1753 	Biobuf bp;
1754 
1755 	Binit(&bp, fd, OWRITE);
1756 	Bprint(&bp, "magic: 0x%2.2x 0x%2.2x 0x%2.2x\n",
1757 		b->magic[0], b->magic[1], b->magic[2]);
1758 	Bprint(&bp, "version: \"%8.8s\"\n", (char*)b->version);
1759 	Bprint(&bp, "sectsize: %d\n", GSHORT(b->sectsize));
1760 	Bprint(&bp, "clustsize: %d\n", b->clustsize);
1761 	Bprint(&bp, "nresrv: %d\n", GSHORT(b->nresrv));
1762 	Bprint(&bp, "nfats: %d\n", b->nfats);
1763 	Bprint(&bp, "rootsize: %d\n", GSHORT(b->rootsize));
1764 	Bprint(&bp, "volsize: %d\n", GSHORT(b->volsize));
1765 	Bprint(&bp, "mediadesc: 0x%2.2x\n", b->mediadesc);
1766 	Bprint(&bp, "fatsize: %d\n", GSHORT(b->fatsize));
1767 	Bprint(&bp, "trksize: %d\n", GSHORT(b->trksize));
1768 	Bprint(&bp, "nheads: %d\n", GSHORT(b->nheads));
1769 	Bprint(&bp, "nhidden: %ld\n", GLONG(b->nhidden));
1770 	Bprint(&bp, "bigvolsize: %ld\n", GLONG(b->bigvolsize));
1771 	Bprint(&bp, "driveno: %d\n", b->driveno);
1772 	Bprint(&bp, "reserved0: 0x%2.2x\n", b->reserved0);
1773 	Bprint(&bp, "bootsig: 0x%2.2x\n", b->bootsig);
1774 	Bprint(&bp, "volid: 0x%8.8lux\n", GLONG(b->volid));
1775 	Bprint(&bp, "label: \"%11.11s\"\n", (char*)b->label);
1776 	Bterm(&bp);
1777 }
1778 
1779 void
1780 bootdump32(int fd, Dosboot32 *b)
1781 {
1782 	Biobuf bp;
1783 
1784 	Binit(&bp, fd, OWRITE);
1785 	Bprint(&bp, "magic: 0x%2.2x 0x%2.2x 0x%2.2x\n",
1786 		b->magic[0], b->magic[1], b->magic[2]);
1787 	Bprint(&bp, "version: \"%8.8s\"\n", (char*)b->version);
1788 	Bprint(&bp, "sectsize: %d\n", GSHORT(b->sectsize));
1789 	Bprint(&bp, "clustsize: %d\n", b->clustsize);
1790 	Bprint(&bp, "nresrv: %d\n", GSHORT(b->nresrv));
1791 	Bprint(&bp, "nfats: %d\n", b->nfats);
1792 	Bprint(&bp, "rootsize: %d\n", GSHORT(b->rootsize));
1793 	Bprint(&bp, "volsize: %d\n", GSHORT(b->volsize));
1794 	Bprint(&bp, "mediadesc: 0x%2.2x\n", b->mediadesc);
1795 	Bprint(&bp, "fatsize: %d\n", GSHORT(b->fatsize));
1796 	Bprint(&bp, "trksize: %d\n", GSHORT(b->trksize));
1797 	Bprint(&bp, "nheads: %d\n", GSHORT(b->nheads));
1798 	Bprint(&bp, "nhidden: %ld\n", GLONG(b->nhidden));
1799 	Bprint(&bp, "bigvolsize: %ld\n", GLONG(b->bigvolsize));
1800 	Bprint(&bp, "fatsize32: %ld\n", GLONG(b->fatsize32));
1801 	Bprint(&bp, "extflags: %d\n", GSHORT(b->extflags));
1802 	Bprint(&bp, "version: %d\n", GSHORT(b->version1));
1803 	Bprint(&bp, "rootstart: %ld\n", GLONG(b->rootstart));
1804 	Bprint(&bp, "infospec: %d\n", GSHORT(b->infospec));
1805 	Bprint(&bp, "backupboot: %d\n", GSHORT(b->backupboot));
1806 	Bprint(&bp, "reserved: %d %d %d %d %d %d %d %d %d %d %d %d\n",
1807 		b->reserved[0], b->reserved[1], b->reserved[2], b->reserved[3],
1808 		b->reserved[4], b->reserved[5], b->reserved[6], b->reserved[7],
1809 		b->reserved[8], b->reserved[9], b->reserved[10], b->reserved[11]);
1810 	Bterm(&bp);
1811 }
1812 
1813 void
1814 bootsecdump32(int fd, Xfs *xf, Dosboot32 *b32)
1815 {
1816 	Fatinfo *fi;
1817 	Iosect *p1;
1818 	int fisec, bsec, res;
1819 
1820 	fprint(fd, "\nfat32\n");
1821 	bootdump32(fd, b32);
1822 	res = GSHORT(b32->nresrv);
1823 	bsec = GSHORT(b32->backupboot);
1824 	if(bsec < res && bsec != 0){
1825 		p1 = getsect(xf, bsec);
1826 		if(p1 == nil)
1827 			fprint(fd, "\ncouldn't get backup boot sector: %r\n");
1828 		else{
1829 			fprint(fd, "\nbackup boot\n");
1830 			bootdump32(fd, (Dosboot32*)p1->iobuf);
1831 			putsect(p1);
1832 		}
1833 	}else if(bsec != 0xffff)
1834 		fprint(fd, "bad backup boot sector: %d reserved %d\n", bsec, res);
1835 	fisec = GSHORT(b32->infospec);
1836 	if(fisec < res && fisec != 0){
1837 		p1 = getsect(xf, fisec);
1838 		if(p1 == nil)
1839 			fprint(fd, "\ncouldn't get fat info sector: %r\n");
1840 		else{
1841 			fprint(fd, "\nfat info %d\n", fisec);
1842 			fi = (Fatinfo*)p1->iobuf;
1843 			fprint(fd, "sig1: 0x%lux sb 0x%lux\n", GLONG(fi->sig1), FATINFOSIG1);
1844 			fprint(fd, "sig: 0x%lux sb 0x%lux\n", GLONG(fi->sig), FATINFOSIG);
1845 			fprint(fd, "freeclust: %lud\n", GLONG(fi->freeclust));
1846 			fprint(fd, "nextfree: %lud\n", GLONG(fi->nextfree));
1847 			fprint(fd, "reserved: %lud %lud %lud\n", GLONG(fi->resrv), GLONG(fi->resrv+4), GLONG(fi->resrv+8));
1848 			putsect(p1);
1849 		}
1850 	}else if(fisec != 0xffff)
1851 		fprint(2, "bad fat info sector: %d reserved %d\n", bsec, res);
1852 }
1853 
1854 void
1855 dirdump(void *vdbuf)
1856 {
1857 	static char attrchar[] = "rhsvda67";
1858 	Dosdir *d;
1859 	char *name, namebuf[DOSNAMELEN];
1860 	char buf[128], *s, *ebuf;
1861 	uchar *dbuf;
1862 	int i;
1863 
1864 	if(!chatty)
1865 		return;
1866 
1867 	d = vdbuf;
1868 
1869 	ebuf = buf + sizeof(buf);
1870 	if(d->attr == 0xf){
1871 		dbuf = vdbuf;
1872 		name = namebuf + DOSNAMELEN;
1873 		*--name = '\0';
1874 		name = getnamerunes(name, dbuf, 1);
1875 		seprint(buf, ebuf, "\"%s\" %2.2x %2.2ux %2.2ux %d", name, dbuf[0], dbuf[12], dbuf[13], GSHORT(d->start));
1876 	}else{
1877 		s = seprint(buf, ebuf, "\"%.8s.%.3s\" ", (char*)d->name, (char*)d->ext);
1878 		for(i=7; i>=0; i--)
1879 			*s++ = d->attr&(1<<i) ? attrchar[i] : '-';
1880 
1881 		i = GSHORT(d->time);
1882 		s = seprint(s, ebuf, " %2.2d:%2.2d:%2.2d", i>>11, (i>>5)&63, (i&31)<<1);
1883 		i = GSHORT(d->date);
1884 		s = seprint(s, ebuf, " %2.2d.%2.2d.%2.2d", 80+(i>>9), (i>>5)&15, i&31);
1885 
1886 		i = GSHORT(d->ctime);
1887 		s = seprint(s, ebuf, " %2.2d:%2.2d:%2.2d", i>>11, (i>>5)&63, (i&31)<<1);
1888 		i = GSHORT(d->cdate);
1889 		s = seprint(s, ebuf, " %2.2d.%2.2d.%2.2d", 80+(i>>9), (i>>5)&15, i&31);
1890 
1891 		i = GSHORT(d->adate);
1892 		s = seprint(s, ebuf, " %2.2d.%2.2d.%2.2d", 80+(i>>9), (i>>5)&15, i&31);
1893 
1894 		seprint(s, ebuf, " %d %d", GSHORT(d->start), GSHORT(d->length));
1895 	}
1896 	chat("%s\n", buf);
1897 }
1898 
1899 int
1900 cistrcmp(char *s1, char *s2)
1901 {
1902 	int c1, c2;
1903 
1904 	while(*s1){
1905 		c1 = *s1++;
1906 		c2 = *s2++;
1907 
1908 		if(c1 >= 'A' && c1 <= 'Z')
1909 			c1 -= 'A' - 'a';
1910 
1911 		if(c2 >= 'A' && c2 <= 'Z')
1912 			c2 -= 'A' - 'a';
1913 
1914 		if(c1 != c2)
1915 			return c1 - c2;
1916 	}
1917 	return -*s2;
1918 }
1919 
1920 int
1921 utftorunes(Rune *rr, char *s, int n)
1922 {
1923 	Rune *r, *re;
1924 	int c;
1925 
1926 	r = rr;
1927 	re = r + n - 1;
1928 	while(c = (uchar)*s){
1929 		if(c < Runeself){
1930 			*r = c;
1931 			s++;
1932 		}else
1933 			s += chartorune(r, s);
1934 		r++;
1935 		if(r >= re)
1936 			break;
1937 	}
1938 	*r = 0;
1939 	return r - rr;
1940 }
1941