Subversion Repositories planix.SVN

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 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
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
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
}