xref: /plan9-contrib/sys/src/cmd/dossrv/dosfs.c (revision d46c239f8612929b7dbade67d0d071633df3a15d)
1 #include <u.h>
2 #include <libc.h>
3 #include <auth.h>
4 #include <fcall.h>
5 #include "iotrack.h"
6 #include "dat.h"
7 #include "dosfs.h"
8 #include "fns.h"
9 
10 void
11 rversion(void)
12 {
13 	if(req->msize > Maxiosize)
14 		rep->msize = Maxiosize;
15 	else
16 		rep->msize = req->msize;
17 	rep->version = "9P2000";
18 }
19 
20 void
21 rauth(void)
22 {
23 	errno = Enoauth;
24 }
25 
26 void
27 rflush(void)
28 {
29 }
30 
31 void
32 rattach(void)
33 {
34 	Xfs *xf;
35 	Xfile *root;
36 	Dosptr *dp;
37 
38 	root = xfile(req->fid, Clean);
39 	if(!root){
40 		errno = Enomem;
41 		goto error;
42 	}
43 	root->xf = xf = getxfs(req->uname, req->aname);
44 	if(!xf)
45 		goto error;
46 	if(xf->fmt == 0 && dosfs(xf) < 0){
47 		errno = Eformat;
48 		goto error;
49 	}
50 	root->qid.type = QTDIR;
51 	root->qid.path = 0;
52 	root->qid.vers = 0;
53 	root->xf->rootqid = root->qid;
54 	dp = malloc(sizeof(Dosptr));
55 	if(dp == nil){
56 		errno = Enomem;
57 		goto error;
58 	}
59 	root->ptr = dp;
60 	rootfile(root);
61 	rep->qid = root->qid;
62 	return;
63 error:
64 	if(root)
65 		xfile(req->fid, Clunk);
66 }
67 
68 Xfile*
69 doclone(Xfile *of, int newfid)
70 {
71 	Xfile *nf, *next;
72 	Dosptr *dp;
73 
74 	nf = xfile(newfid, Clean);
75 	if(!nf){
76 		errno = Enomem;
77 		return nil;
78 	}
79 	dp = malloc(sizeof(Dosptr));
80 	if(dp == nil){
81 		errno = Enomem;
82 		return nil;
83 	}
84 	next = nf->next;
85 	*nf = *of;
86 	nf->next = next;
87 	nf->fid = req->newfid;
88 	nf->ptr = dp;
89 	refxfs(nf->xf, 1);
90 	memmove(dp, of->ptr, sizeof(Dosptr));
91 	dp->p = nil;
92 	dp->d = nil;
93 	return nf;
94 }
95 
96 void
97 rwalk(void)
98 {
99 	Xfile *f, *nf;
100 	Dosptr dp[1], savedp[1];
101 	int r, longtype;
102 	Qid saveqid;
103 
104 	rep->nwqid = 0;
105 	nf = nil;
106 	f = xfile(req->fid, Asis);
107 	if(f == nil){
108 		chat("\tno xfile\n");
109 		goto error2;
110 	}
111 	if(req->fid != req->newfid){
112 		nf = doclone(f, req->newfid);
113 		if(nf == nil){
114 			chat("\tclone failed\n");
115 			goto error2;
116 		}
117 		f = nf;
118 	}
119 
120 	saveqid = f->qid;
121 	memmove(savedp, f->ptr, sizeof(Dosptr));
122 	for(; rep->nwqid < req->nwname && rep->nwqid < MAXWELEM; rep->nwqid++){
123 		chat("\twalking %s\n", req->wname[rep->nwqid]);
124 		if(!(f->qid.type & QTDIR)){
125 			chat("\tnot dir: type=%#x\n", f->qid.type);
126 			goto error;
127 		}
128 		if(strcmp(req->wname[rep->nwqid], ".") == 0){
129 			;
130 		}else if(strcmp(req->wname[rep->nwqid], "..") == 0){
131 			if(f->qid.path != f->xf->rootqid.path){
132 				r = walkup(f, dp);
133 				if(r < 0)
134 					goto error;
135 				memmove(f->ptr, dp, sizeof(Dosptr));
136 				if(isroot(dp->addr))
137 					f->qid.path = f->xf->rootqid.path;
138 				else
139 					f->qid.path = QIDPATH(dp);
140 			}
141 		}else{
142 			fixname(req->wname[rep->nwqid]);
143 			longtype = classifyname(req->wname[rep->nwqid]);
144 			if(longtype==Invalid || getfile(f) < 0)
145 				goto error;
146 
147 			/*
148 			 * always do a search for the long name,
149 			 * because it could be filed as such
150 			 */
151 			r = searchdir(f, req->wname[rep->nwqid], dp, 0, longtype);
152 			putfile(f);
153 			if(r < 0)
154 				goto error;
155 			memmove(f->ptr, dp, sizeof(Dosptr));
156 			f->qid.path = QIDPATH(dp);
157 			f->qid.type = QTFILE;
158 			if(isroot(dp->addr))
159 				f->qid.path = f->xf->rootqid.path;
160 			else if(dp->d->attr & DDIR)
161 				f->qid.type = QTDIR;
162 			else if(dp->d->attr & DSYSTEM){
163 				f->qid.type |= QTEXCL;
164 				if(iscontig(f->xf, dp->d))
165 					f->qid.type |= QTAPPEND;
166 			}
167 //ZZZ maybe use other bits than qtexcl & qtapppend
168 			putfile(f);
169 		}
170 		rep->wqid[rep->nwqid] = f->qid;
171 	}
172 	return;
173 error:
174 	f->qid = saveqid;
175 	memmove(f->ptr, savedp, sizeof(Dosptr));
176 	if(nf != nil)
177 		xfile(req->newfid, Clunk);
178 error2:
179 	if(!errno && !rep->nwqid)
180 		errno = Enonexist;
181 }
182 
183 void
184 ropen(void)
185 {
186 	Xfile *f;
187 	Iosect *p;
188 	Dosptr *dp;
189 	int attr, omode;
190 
191 	f = xfile(req->fid, Asis);
192 	if(!f || (f->flags&Omodes)){
193 		errno = Eio;
194 		return;
195 	}
196 	dp = f->ptr;
197 	omode = 0;
198 	if(!isroot(dp->paddr) && (req->mode & ORCLOSE)){
199 		/*
200 		 * check on parent directory of file to be deleted
201 		 */
202 		p = getsect(f->xf, dp->paddr);
203 		if(p == nil){
204 			errno = Eio;
205 			return;
206 		}
207 		attr = ((Dosdir *)&p->iobuf[dp->poffset])->attr;
208 		putsect(p);
209 		if(attr & DRONLY){
210 			errno = Eperm;
211 			return;
212 		}
213 		omode |= Orclose;
214 	}else if(req->mode & ORCLOSE)
215 		omode |= Orclose;
216 	if(getfile(f) < 0){
217 		errno = Enonexist;
218 		return;
219 	}
220 	if(!isroot(dp->addr))
221 		attr = dp->d->attr;
222 	else
223 		attr = DDIR;
224 	switch(req->mode & 7){
225 	case OREAD:
226 	case OEXEC:
227 		omode |= Oread;
228 		break;
229 	case ORDWR:
230 		omode |= Oread;
231 		/* fall through */
232 	case OWRITE:
233 		omode |= Owrite;
234 		if(attr & DRONLY){
235 			errno = Eperm;
236 			goto out;
237 		}
238 		break;
239 	default:
240 		errno = Eio;
241 		goto out;
242 	}
243 	if(req->mode & OTRUNC){
244 		if(attr & DDIR || attr & DRONLY){
245 			errno = Eperm;
246 			goto out;
247 		}
248 		if(truncfile(f, 0) < 0){
249 			errno = Eio;
250 			goto out;
251 		}
252 	}
253 	f->flags |= omode;
254 	rep->qid = f->qid;
255 	rep->iounit = 0;
256 out:
257 	putfile(f);
258 }
259 
260 static int
261 mk8dot3name(Xfile *f, Dosptr *ndp, char *name, char *sname)
262 {
263 	Dosptr tmpdp;
264 	int i, longtype;
265 
266 	if(strcmp(name, ".") == 0 || strcmp(name, "..") == 0)
267 		return Invalid;
268 
269 	/*
270 	 * always do a search for the long name,
271 	 * because it could be filed as such
272 	 */
273 	fixname(name);
274 	longtype = classifyname(name);
275 	if(longtype==Invalid || searchdir(f, name, ndp, 1, longtype) < 0)
276 		return Invalid;
277 
278 	if(longtype==Short)
279 		return Short;
280 
281 	if(longtype==ShortLower){
282 		/*
283 		 * alias is the upper-case version, which we
284 		 * already know does not exist.
285 		 */
286 		strcpy(sname, name);
287 		for(i=0; sname[i]; i++)
288 			if('a' <= sname[i] && sname[i] <= 'z')
289 				sname[i] += 'A'-'a';
290 		return ShortLower;
291 	}
292 
293 	/*
294 	 * find alias for the long name
295 	 */
296 	for(i=1;; i++){
297 		mkalias(name, sname, i);
298 		if(searchdir(f, sname, &tmpdp, 0, 0) < 0)
299 			return Long;
300 		putsect(tmpdp.p);
301 	}
302 	abort();
303 	return -1;
304 }
305 
306 /*
307  * fill in a directory entry for a new file
308  */
309 static int
310 mkdentry(Xfs *xf, Dosptr *ndp, char *name, char *sname, int longtype, int nattr, long start, long length)
311 {
312 	Dosdir *nd;
313 
314 	/*
315 	 * fill in the entry
316 	 */
317 	ndp->p = getsect(xf, ndp->addr);
318 	if(ndp->p == nil
319 	|| longtype!=Short && putlongname(xf, ndp, name, sname) < 0){
320 		errno = Eio;
321 		return -1;
322 	}
323 
324 	ndp->d = (Dosdir *)&ndp->p->iobuf[ndp->offset];
325 	nd = ndp->d;
326 	memset(nd, 0, DOSDIRSIZE);
327 
328 	if(longtype!=Short)
329 		name = sname;
330 	putname(name, nd);
331 
332 	nd->attr = nattr;
333 	puttime(nd, 0);
334 	putstart(xf, nd, start);
335 	nd->length[0] = length;
336 	nd->length[1] = length>>8;
337 	nd->length[2] = length>>16;
338 	nd->length[3] = length>>24;
339 
340 	ndp->p->flags |= BMOD;
341 
342 	return 0;
343 }
344 
345 void
346 rcreate(void)
347 {
348 	Dosbpb *bp;
349 	Xfile *f;
350 	Dosptr *pdp, *ndp;
351 	Iosect *xp;
352 	Dosdir *pd, *xd;
353 	char sname[13];
354 	long start;
355 	int longtype, attr, omode, nattr;
356 
357 	f = xfile(req->fid, Asis);
358 	if(!f || (f->flags&Omodes) || getfile(f)<0){
359 		errno = Eio;
360 		return;
361 	}
362 	pdp = f->ptr;
363 	pd = pdp->d;
364 	/*
365 	 * perm check
366 	 */
367 	if(isroot(pdp->addr) && pd != nil)
368 		panic("root pd != nil");
369 	attr = pd ? pd->attr : DDIR;
370 	if(!(attr & DDIR) || (attr & DRONLY)){
371 badperm:
372 		putfile(f);
373 		errno = Eperm;
374 		return;
375 	}
376 	omode = 0;
377 	if(req->mode & ORCLOSE)
378 		omode |= Orclose;
379 	switch(req->mode & 7){
380 	case OREAD:
381 	case OEXEC:
382 		omode |= Oread;
383 		break;
384 	case ORDWR:
385 		omode |= Oread;
386 		/* fall through */
387 	case OWRITE:
388 		omode |= Owrite;
389 		if(req->perm & DMDIR)
390 			goto badperm;
391 		break;
392 	default:
393 		goto badperm;
394 	}
395 
396 	/*
397 	 * check the name, find the slot for the dentry,
398 	 * and find a good alias for a long name
399 	 */
400 	ndp = malloc(sizeof(Dosptr));
401 	if(ndp == nil){
402 		putfile(f);
403 		errno = Enomem;
404 		return;
405 	}
406 	longtype = mk8dot3name(f, ndp, req->name, sname);
407 	chat("rcreate %s longtype %d...\n", req->name, longtype);
408 	if(longtype == Invalid){
409 		free(ndp);
410 		goto badperm;
411 	}
412 
413 	/*
414 	 * allocate first cluster, if making directory
415 	 */
416 	start = 0;
417 	bp = nil;
418 	if(req->perm & DMDIR){
419 		bp = f->xf->ptr;
420 		mlock(bp);
421 		start = falloc(f->xf);
422 		unmlock(bp);
423 		if(start <= 0){
424 			free(ndp);
425 			putfile(f);
426 			errno = Eio;
427 			return;
428 		}
429 	}
430 
431 	/*
432 	 * make the entry
433 	 */
434 	nattr = 0;
435 	if((req->perm & 0222) == 0)
436 		nattr |= DRONLY;
437 	if(req->perm & DMDIR)
438 		nattr |= DDIR;
439 
440 	if(mkdentry(f->xf, ndp, req->name, sname, longtype, nattr, start, 0) < 0){
441 		if(ndp->p != nil)
442 			putsect(ndp->p);
443 		free(ndp);
444 		if(start > 0)
445 			ffree(f->xf, start);
446 		putfile(f);
447 		return;
448 	}
449 
450 	if(pd != nil){
451 		puttime(pd, 0);
452 		pdp->p->flags |= BMOD;
453 	}
454 
455 	/*
456 	 * fix up the fid
457 	 */
458 	f->ptr = ndp;
459 	f->qid.type = QTFILE;
460 	f->qid.path = QIDPATH(ndp);
461 
462 //ZZZ set type for excl, append?
463 	if(req->perm & DMDIR){
464 		f->qid.type = QTDIR;
465 		xp = getsect(f->xf, clust2sect(bp, start));
466 		if(xp == nil){
467 			errno = Eio;
468 			goto badio;
469 		}
470 		xd = (Dosdir *)&xp->iobuf[0];
471 		memmove(xd, ndp->d, DOSDIRSIZE);
472 		memset(xd->name, ' ', sizeof xd->name+sizeof xd->ext);
473 		xd->name[0] = '.';
474 		xd = (Dosdir *)&xp->iobuf[DOSDIRSIZE];
475 		if(pd)
476 			memmove(xd, pd, DOSDIRSIZE);
477 		else{
478 			memset(xd, 0, DOSDIRSIZE);
479 			puttime(xd, 0);
480 			xd->attr = DDIR;
481 		}
482 		memset(xd->name, ' ', sizeof xd->name+sizeof xd->ext);
483 		xd->name[0] = '.';
484 		xd->name[1] = '.';
485 		xp->flags |= BMOD;
486 		putsect(xp);
487 	}
488 
489 	f->flags |= omode;
490 	rep->qid = f->qid;
491 	rep->iounit = 0;
492 
493 badio:
494 	putfile(f);
495 	putsect(pdp->p);
496 	free(pdp);
497 }
498 
499 void
500 rread(void)
501 {
502 	Xfile *f;
503 	int r;
504 
505 	if (!(f=xfile(req->fid, Asis)) || !(f->flags&Oread))
506 		goto error;
507 	if(req->count > sizeof repdata)
508 		req->count = sizeof repdata;
509 	if(f->qid.type & QTDIR){
510 		if(getfile(f) < 0)
511 			goto error;
512 		r = readdir(f, repdata, req->offset, req->count);
513 	}else{
514 		if(getfile(f) < 0)
515 			goto error;
516 		r = readfile(f, repdata, req->offset, req->count);
517 	}
518 	putfile(f);
519 	if(r < 0){
520 error:
521 		errno = Eio;
522 	}else{
523 		rep->count = r;
524 		rep->data = (char*)repdata;
525 	}
526 }
527 
528 void
529 rwrite(void)
530 {
531 	Xfile *f;
532 	int r;
533 
534 	if (!(f=xfile(req->fid, Asis)) || !(f->flags&Owrite))
535 		goto error;
536 	if(getfile(f) < 0)
537 		goto error;
538 	r = writefile(f, req->data, req->offset, req->count);
539 	putfile(f);
540 	if(r < 0){
541 error:
542 		errno = Eio;
543 	}else{
544 		rep->count = r;
545 	}
546 }
547 
548 void
549 rclunk(void)
550 {
551 	xfile(req->fid, Clunk);
552 	sync();
553 }
554 
555 /*
556  * wipe out a dos directory entry
557  */
558 static void
559 doremove(Xfs *xf, Dosptr *dp)
560 {
561 	Iosect *p;
562 	int prevdo;
563 
564 	dp->p->iobuf[dp->offset] = DOSEMPTY;
565 	dp->p->flags |= BMOD;
566 	for(prevdo = dp->offset-DOSDIRSIZE; prevdo >= 0; prevdo -= DOSDIRSIZE){
567 		if(dp->p->iobuf[prevdo+11] != 0xf)
568 			break;
569 		dp->p->iobuf[prevdo] = DOSEMPTY;
570 	}
571 	if(prevdo < 0 && dp->prevaddr != -1){
572 		p = getsect(xf, dp->prevaddr);
573 		for(prevdo = ((Dosbpb*)xf->ptr)->sectsize-DOSDIRSIZE; prevdo >= 0; prevdo -= DOSDIRSIZE){
574 			if(p->iobuf[prevdo+11] != 0xf)
575 				break;
576 			p->iobuf[prevdo] = DOSEMPTY;
577 			p->flags |= BMOD;
578 		}
579 		putsect(p);
580 	}
581 }
582 
583 void
584 rremove(void)
585 {
586 	Xfile *f;
587 	Dosptr *dp;
588 	Iosect *parp;
589 	Dosdir *pard;
590 
591 	f = xfile(req->fid, Asis);
592 	parp = nil;
593 	if(f == nil){
594 		errno = Eio;
595 		goto out;
596 	}
597 	dp = f->ptr;
598 	if(isroot(dp->addr)){
599 		errno = Eperm;
600 		goto out;
601 	}
602 
603 	/*
604 	 * can't remove if parent is read only,
605 	 * it's a non-empty directory,
606 	 * or it's a read only file in the root directory
607 	 */
608 	parp = getsect(f->xf, dp->paddr);
609 	if(parp == nil
610 	|| getfile(f) < 0){
611 		errno = Eio;
612 		goto out;
613 	}
614 	pard = (Dosdir *)&parp->iobuf[dp->poffset];
615 	if(!isroot(dp->paddr) && (pard->attr & DRONLY)
616 	|| (dp->d->attr & DDIR) && emptydir(f) < 0
617 	|| isroot(dp->paddr) && (dp->d->attr&DRONLY)){
618 		errno = Eperm;
619 		goto out;
620 	}
621 	if(truncfile(f, 0) < 0){
622 		errno = Eio;
623 		goto out;
624 	}
625 	doremove(f->xf, f->ptr);
626 	if(!isroot(dp->paddr)){
627 		puttime(pard, 0);
628 		parp->flags |= BMOD;
629 	}
630 out:
631 	if(parp != nil)
632 		putsect(parp);
633 	if(f != nil)
634 		putfile(f);
635 	xfile(req->fid, Clunk);
636 	sync();
637 }
638 
639 static void
640 dostat(Xfile *f, Dir *d)
641 {
642 	Dosptr *dp;
643 	Iosect *p;
644 	char *name, namebuf[DOSNAMELEN];
645 	int islong, sum, prevdo;
646 
647 	dp = f->ptr;
648 	if(isroot(dp->addr)){
649 		memset(d, 0, sizeof(Dir));
650 		d->name = "/";
651 		d->qid.type = QTDIR;
652 		d->qid.path = f->xf->rootqid.path;
653 		d->mode = DMDIR|0777;
654 		d->uid = "bill";
655 		d->muid = "bill";
656 		d->gid = "trog";
657 	}else{
658 		/*
659 		 * assemble any long file name
660 		 */
661 		sum = aliassum(dp->d);
662 		islong = 0;
663 		name = namebuf;
664 		for(prevdo = dp->offset-DOSDIRSIZE; prevdo >= 0; prevdo -= DOSDIRSIZE){
665 			if(dp->p->iobuf[prevdo+11] != 0xf)
666 				break;
667 			name = getnamesect(namebuf, name, &dp->p->iobuf[prevdo], &islong, &sum, -1);
668 		}
669 		if(prevdo < 0 && dp->prevaddr != -1){
670 			p = getsect(f->xf, dp->prevaddr);
671 			for(prevdo = ((Dosbpb*)f->xf->ptr)->sectsize-DOSDIRSIZE; prevdo >= 0; prevdo -= DOSDIRSIZE){
672 				if(p->iobuf[prevdo+11] != 0xf)
673 					break;
674 				name = getnamesect(namebuf, name, &p->iobuf[prevdo], &islong, &sum, -1);
675 			}
676 			putsect(p);
677 		}
678 		getdir(f->xf, d, dp->d, dp->addr, dp->offset);
679 		if(islong && sum == -1 && nameok(namebuf))
680 			strcpy(d->name, namebuf);
681 	}
682 }
683 
684 void
685 rstat(void)
686 {
687 	Dir dir;
688 	Xfile *f;
689 
690 	f = xfile(req->fid, Asis);
691 	if(!f || getfile(f) < 0){
692 		errno = Eio;
693 		return;
694 	}
695 
696 	dir.name = repdata;
697 	dostat(f, &dir);
698 
699 	rep->nstat = convD2M(&dir, statbuf, sizeof statbuf);
700 	rep->stat = statbuf;
701 	putfile(f);
702 }
703 
704 void
705 rwstat(void)
706 {
707 	Dir dir, wdir;
708 	Xfile *f, pf;
709 	Dosptr *dp, ndp, pdp;
710 	Iosect *parp;
711 	Dosdir *pard, *d, od;
712 	char sname[13];
713 	ulong oaddr, ooffset;
714 	long start, length;
715 	int i, longtype, changes, attr;
716 
717 	f = xfile(req->fid, Asis);
718 	if(!f || getfile(f) < 0){
719 		errno = Eio;
720 		return;
721 	}
722 	dp = f->ptr;
723 
724 	if(isroot(dp->addr)){
725 		errno = Eperm;
726 		goto out;
727 	}
728 
729 	changes = 0;
730 	dir.name = repdata;
731 	dostat(f, &dir);
732 	if(convM2D(req->stat, req->nstat, &wdir, (char*)statbuf) != req->nstat){
733 		errno = Ebadstat;
734 		goto out;
735 	}
736 
737 	/*
738 	 * To change length, must have write permission on file.
739 	 * we only allow truncates for now.
740 	 */
741 	if(wdir.length!=~0 && wdir.length!=dir.length){
742 		if(wdir.length > dir.length || !dir.mode & 0222){
743 			errno = Eperm;
744 			goto out;
745 		}
746 	}
747 
748 	/*
749 	 * no chown or chgrp
750 	 */
751 	if(wdir.uid[0] != '\0' && strcmp(dir.uid, wdir.uid) != 0
752 	|| wdir.gid[0] != '\0' && strcmp(dir.gid, wdir.gid) != 0){
753 		errno = Eperm;
754 		goto out;
755 	}
756 
757 	/*
758 	 * mode/mtime allowed
759 	 */
760 	if(wdir.mtime != ~0 && dir.mtime != wdir.mtime)
761 		changes = 1;
762 
763 	/*
764 	 * Setting DMAPPEND (make system file contiguous)
765 	 * requires setting DMEXCL (system file).
766 	 */
767 	if(wdir.mode != ~0){
768 		if((wdir.mode & 7) != ((wdir.mode >> 3) & 7)
769 		|| (wdir.mode & 7) != ((wdir.mode >> 6) & 7)){
770 			errno = Eperm;
771 			goto out;
772 		}
773 		if((dir.mode^wdir.mode) & (DMEXCL|DMAPPEND|0777))
774 			changes = 1;
775 		if((dir.mode^wdir.mode) & DMAPPEND) {
776 			if((wdir.mode & (DMEXCL|DMAPPEND)) == DMAPPEND) {
777 				errno = Eperm;
778 				goto out;
779 			}
780 			if((wdir.mode & DMAPPEND) && makecontig(f, 0) < 0) {
781 				errno = Econtig;
782 				goto out;
783 			}
784 		}
785 	}
786 
787 
788 	/*
789 	 * to rename:
790 	 *	1) make up a fake clone
791 	 *	2) walk to parent
792 	 *	3) remove the old entry
793 	 *	4) create entry with new name
794 	 *	5) write correct mode/mtime info
795 	 * we need to remove the old entry before creating the new one
796 	 * to avoid a lock loop.
797 	 */
798 	if(wdir.name[0] != '\0' && strcmp(dir.name, wdir.name) != 0){
799 		if(utflen(wdir.name) >= DOSNAMELEN){
800 			errno = Etoolong;
801 			goto out;
802 		}
803 
804 		/*
805 		 * grab parent directory of file to be changed and check for write perm
806 		 * rename also disallowed for read-only files in root directory
807 		 */
808 		parp = getsect(f->xf, dp->paddr);
809 		if(parp == nil){
810 			errno = Eio;
811 			goto out;
812 		}
813 		pard = (Dosdir *)&parp->iobuf[dp->poffset];
814 		if(!isroot(dp->paddr) && (pard->attr & DRONLY)
815 		|| isroot(dp->paddr) && (dp->d->attr&DRONLY)){
816 			putsect(parp);
817 			errno = Eperm;
818 			goto out;
819 		}
820 
821 		/*
822 		 * retrieve info from old entry
823 		 */
824 		oaddr = dp->addr;
825 		ooffset = dp->offset;
826 		d = dp->d;
827 		od = *d;
828 		start = getstart(f->xf, d);
829 		length = GLONG(d->length);
830 		attr = d->attr;
831 
832 		/*
833 		 * temporarily release file to allow other directory ops:
834 		 * walk to parent, validate new name
835 		 * then remove old entry
836 		 */
837 		putfile(f);
838 		pf = *f;
839 		memset(&pdp, 0, sizeof(Dosptr));
840 		pdp.prevaddr = -1;
841 		pdp.naddr = -1;
842 		pdp.addr = dp->paddr;
843 		pdp.offset = dp->poffset;
844 		pdp.p = parp;
845 		if(!isroot(pdp.addr))
846 			pdp.d = (Dosdir *)&parp->iobuf[pdp.offset];
847 		pf.ptr = &pdp;
848 		longtype = mk8dot3name(&pf, &ndp, wdir.name, sname);
849 		if(longtype==Invalid){
850 			putsect(parp);
851 			errno = Eperm;
852 			return;
853 		}
854 		if(getfile(f) < 0){
855 			putsect(parp);
856 			errno = Eio;
857 			return;
858 		}
859 		doremove(f->xf, dp);
860 		putfile(f);
861 
862 		/*
863 		 * search for dir entry again, since we may be able to use the old slot,
864 		 * and we need to set up the naddr field if a long name spans the block.
865 		 * create new entry.
866 		 */
867 		if(searchdir(&pf, wdir.name, dp, 1, longtype) < 0
868 		|| mkdentry(pf.xf, dp, wdir.name, sname, longtype, attr, start, length) < 0){
869 			putsect(parp);
870 			errno = Eio;
871 			goto out;
872 		}
873 
874 		/*
875 		 * copy invisible fields
876 		 */
877 		d = dp->d;
878 		for(i = 0; i < 2; i++)
879 			d->ctime[i] = od.ctime[i];
880 		for(i = 0; i < nelem(od.cdate); i++)
881 			d->cdate[i] = od.cdate[i];
882 		for(i = 0; i < nelem(od.adate); i++)
883 			d->adate[i] = od.adate[i];
884 
885 		putsect(parp);
886 
887 		/*
888 		 * relocate up other fids to the same file, if it moved
889 		 */
890 		f->qid.path = QIDPATH(dp);
891 		if(oaddr != dp->addr || ooffset != dp->offset)
892 			dosptrreloc(f, dp, oaddr, ooffset);
893 
894 		/*
895 		 * copy fields that are not supposed to change
896 		 */
897 		if(wdir.mtime == ~0)
898 			wdir.mtime = dir.mtime;
899 		if(wdir.mode == ~0)
900 			wdir.mode = dir.mode;
901 		changes = 1;
902 	}
903 
904 	/*
905 	 * do the actual truncate
906 	 */
907 	if(wdir.length != ~0 && wdir.length != dir.length && truncfile(f, wdir.length) < 0)
908 		errno = Eio;
909 
910 	if(changes){
911 		putdir(dp->d, &wdir);
912 		dp->p->flags |= BMOD;
913 	}
914 
915 out:
916 	putfile(f);
917 	sync();
918 }
919