xref: /plan9/sys/src/cmd/dossrv/dosfs.c (revision b85a83648eec38fe82b6f00adfd7828ceec5ee8d)
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
rversion(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
rauth(void)21 rauth(void)
22 {
23 	errno = Enoauth;
24 }
25 
26 void
rflush(void)27 rflush(void)
28 {
29 }
30 
31 void
rattach(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*
doclone(Xfile * of,int newfid)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
rwalk(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
ropen(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
mk8dot3name(Xfile * f,Dosptr * ndp,char * name,char * sname)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 }
303 
304 /*
305  * fill in a directory entry for a new file
306  */
307 static int
mkdentry(Xfs * xf,Dosptr * ndp,char * name,char * sname,int longtype,int nattr,long start,long length)308 mkdentry(Xfs *xf, Dosptr *ndp, char *name, char *sname, int longtype, int nattr, long start, long length)
309 {
310 	Dosdir *nd;
311 
312 	/*
313 	 * fill in the entry
314 	 */
315 	ndp->p = getsect(xf, ndp->addr);
316 	if(ndp->p == nil
317 	|| longtype!=Short && putlongname(xf, ndp, name, sname) < 0){
318 		errno = Eio;
319 		return -1;
320 	}
321 
322 	ndp->d = (Dosdir *)&ndp->p->iobuf[ndp->offset];
323 	nd = ndp->d;
324 	memset(nd, 0, DOSDIRSIZE);
325 
326 	if(longtype!=Short)
327 		name = sname;
328 	putname(name, nd);
329 
330 	nd->attr = nattr;
331 	puttime(nd, 0);
332 	putstart(xf, nd, start);
333 	nd->length[0] = length;
334 	nd->length[1] = length>>8;
335 	nd->length[2] = length>>16;
336 	nd->length[3] = length>>24;
337 
338 	ndp->p->flags |= BMOD;
339 
340 	return 0;
341 }
342 
343 void
rcreate(void)344 rcreate(void)
345 {
346 	Dosbpb *bp;
347 	Xfile *f;
348 	Dosptr *pdp, *ndp;
349 	Iosect *xp;
350 	Dosdir *pd, *xd;
351 	char sname[13];
352 	long start;
353 	int longtype, attr, omode, nattr;
354 
355 	f = xfile(req->fid, Asis);
356 	if(!f || (f->flags&Omodes) || getfile(f)<0){
357 		errno = Eio;
358 		return;
359 	}
360 	pdp = f->ptr;
361 	pd = pdp->d;
362 	/*
363 	 * perm check
364 	 */
365 	if(isroot(pdp->addr) && pd != nil)
366 		panic("root pd != nil");
367 	attr = pd ? pd->attr : DDIR;
368 	if(!(attr & DDIR) || (attr & DRONLY)){
369 badperm:
370 		putfile(f);
371 		errno = Eperm;
372 		return;
373 	}
374 	omode = 0;
375 	if(req->mode & ORCLOSE)
376 		omode |= Orclose;
377 	switch(req->mode & 7){
378 	case OREAD:
379 	case OEXEC:
380 		omode |= Oread;
381 		break;
382 	case ORDWR:
383 		omode |= Oread;
384 		/* fall through */
385 	case OWRITE:
386 		omode |= Owrite;
387 		if(req->perm & DMDIR)
388 			goto badperm;
389 		break;
390 	default:
391 		goto badperm;
392 	}
393 
394 	/*
395 	 * check the name, find the slot for the dentry,
396 	 * and find a good alias for a long name
397 	 */
398 	ndp = malloc(sizeof(Dosptr));
399 	if(ndp == nil){
400 		putfile(f);
401 		errno = Enomem;
402 		return;
403 	}
404 	longtype = mk8dot3name(f, ndp, req->name, sname);
405 	chat("rcreate %s longtype %d...\n", req->name, longtype);
406 	if(longtype == Invalid){
407 		free(ndp);
408 		goto badperm;
409 	}
410 
411 	/*
412 	 * allocate first cluster, if making directory
413 	 */
414 	start = 0;
415 	bp = nil;
416 	if(req->perm & DMDIR){
417 		bp = f->xf->ptr;
418 		mlock(bp);
419 		start = falloc(f->xf);
420 		unmlock(bp);
421 		if(start <= 0){
422 			free(ndp);
423 			putfile(f);
424 			errno = Eio;
425 			return;
426 		}
427 	}
428 
429 	/*
430 	 * make the entry
431 	 */
432 	nattr = 0;
433 	if((req->perm & 0222) == 0)
434 		nattr |= DRONLY;
435 	if(req->perm & DMDIR)
436 		nattr |= DDIR;
437 
438 	if(mkdentry(f->xf, ndp, req->name, sname, longtype, nattr, start, 0) < 0){
439 		if(ndp->p != nil)
440 			putsect(ndp->p);
441 		free(ndp);
442 		if(start > 0)
443 			ffree(f->xf, start);
444 		putfile(f);
445 		return;
446 	}
447 
448 	if(pd != nil){
449 		puttime(pd, 0);
450 		pdp->p->flags |= BMOD;
451 	}
452 
453 	/*
454 	 * fix up the fid
455 	 */
456 	f->ptr = ndp;
457 	f->qid.type = QTFILE;
458 	f->qid.path = QIDPATH(ndp);
459 
460 //ZZZ set type for excl, append?
461 	if(req->perm & DMDIR){
462 		f->qid.type = QTDIR;
463 		xp = getsect(f->xf, clust2sect(bp, start));
464 		if(xp == nil){
465 			errno = Eio;
466 			goto badio;
467 		}
468 		xd = (Dosdir *)&xp->iobuf[0];
469 		memmove(xd, ndp->d, DOSDIRSIZE);
470 		memset(xd->name, ' ', sizeof xd->name+sizeof xd->ext);
471 		xd->name[0] = '.';
472 		xd = (Dosdir *)&xp->iobuf[DOSDIRSIZE];
473 		if(pd)
474 			memmove(xd, pd, DOSDIRSIZE);
475 		else{
476 			memset(xd, 0, DOSDIRSIZE);
477 			puttime(xd, 0);
478 			xd->attr = DDIR;
479 		}
480 		memset(xd->name, ' ', sizeof xd->name+sizeof xd->ext);
481 		xd->name[0] = '.';
482 		xd->name[1] = '.';
483 		xp->flags |= BMOD;
484 		putsect(xp);
485 	}
486 
487 	f->flags |= omode;
488 	rep->qid = f->qid;
489 	rep->iounit = 0;
490 
491 badio:
492 	putfile(f);
493 	putsect(pdp->p);
494 	free(pdp);
495 }
496 
497 void
rread(void)498 rread(void)
499 {
500 	Xfile *f;
501 	int r;
502 
503 	if (!(f=xfile(req->fid, Asis)) || !(f->flags&Oread))
504 		goto error;
505 	if(req->count > sizeof repdata)
506 		req->count = sizeof repdata;
507 	if(f->qid.type & QTDIR){
508 		if(getfile(f) < 0)
509 			goto error;
510 		r = readdir(f, repdata, req->offset, req->count);
511 	}else{
512 		if(getfile(f) < 0)
513 			goto error;
514 		r = readfile(f, repdata, req->offset, req->count);
515 	}
516 	putfile(f);
517 	if(r < 0){
518 error:
519 		errno = Eio;
520 	}else{
521 		rep->count = r;
522 		rep->data = (char*)repdata;
523 	}
524 }
525 
526 void
rwrite(void)527 rwrite(void)
528 {
529 	Xfile *f;
530 	int r;
531 
532 	if (!(f=xfile(req->fid, Asis)) || !(f->flags&Owrite))
533 		goto error;
534 	if(getfile(f) < 0)
535 		goto error;
536 	r = writefile(f, req->data, req->offset, req->count);
537 	putfile(f);
538 	if(r < 0){
539 error:
540 		errno = Eio;
541 	}else{
542 		rep->count = r;
543 	}
544 }
545 
546 void
rclunk(void)547 rclunk(void)
548 {
549 	xfile(req->fid, Clunk);
550 	sync();
551 }
552 
553 /*
554  * wipe out a dos directory entry
555  */
556 static void
doremove(Xfs * xf,Dosptr * dp)557 doremove(Xfs *xf, Dosptr *dp)
558 {
559 	Iosect *p;
560 	int prevdo;
561 
562 	dp->p->iobuf[dp->offset] = DOSEMPTY;
563 	dp->p->flags |= BMOD;
564 	for(prevdo = dp->offset-DOSDIRSIZE; prevdo >= 0; prevdo -= DOSDIRSIZE){
565 		if(dp->p->iobuf[prevdo+11] != 0xf)
566 			break;
567 		dp->p->iobuf[prevdo] = DOSEMPTY;
568 	}
569 	if(prevdo < 0 && dp->prevaddr != -1){
570 		p = getsect(xf, dp->prevaddr);
571 		for(prevdo = ((Dosbpb*)xf->ptr)->sectsize-DOSDIRSIZE; prevdo >= 0; prevdo -= DOSDIRSIZE){
572 			if(p->iobuf[prevdo+11] != 0xf)
573 				break;
574 			p->iobuf[prevdo] = DOSEMPTY;
575 			p->flags |= BMOD;
576 		}
577 		putsect(p);
578 	}
579 }
580 
581 void
rremove(void)582 rremove(void)
583 {
584 	Xfile *f;
585 	Dosptr *dp;
586 	Iosect *parp;
587 	Dosdir *pard;
588 
589 	f = xfile(req->fid, Asis);
590 	parp = nil;
591 	if(f == nil){
592 		errno = Eio;
593 		goto out;
594 	}
595 	dp = f->ptr;
596 	if(isroot(dp->addr)){
597 		errno = Eperm;
598 		goto out;
599 	}
600 
601 	/*
602 	 * can't remove if parent is read only,
603 	 * it's a non-empty directory,
604 	 * or it's a read only file in the root directory
605 	 */
606 	parp = getsect(f->xf, dp->paddr);
607 	if(parp == nil
608 	|| getfile(f) < 0){
609 		errno = Eio;
610 		goto out;
611 	}
612 	pard = (Dosdir *)&parp->iobuf[dp->poffset];
613 	if(!isroot(dp->paddr) && (pard->attr & DRONLY)
614 	|| (dp->d->attr & DDIR) && emptydir(f) < 0
615 	|| isroot(dp->paddr) && (dp->d->attr&DRONLY)){
616 		errno = Eperm;
617 		goto out;
618 	}
619 	if(truncfile(f, 0) < 0){
620 		errno = Eio;
621 		goto out;
622 	}
623 	doremove(f->xf, f->ptr);
624 	if(!isroot(dp->paddr)){
625 		puttime(pard, 0);
626 		parp->flags |= BMOD;
627 	}
628 out:
629 	if(parp != nil)
630 		putsect(parp);
631 	if(f != nil)
632 		putfile(f);
633 	xfile(req->fid, Clunk);
634 	sync();
635 }
636 
637 static void
dostat(Xfile * f,Dir * d)638 dostat(Xfile *f, Dir *d)
639 {
640 	Dosptr *dp;
641 	Iosect *p;
642 	char *name, namebuf[DOSNAMELEN];
643 	int islong, sum, prevdo;
644 
645 	dp = f->ptr;
646 	if(isroot(dp->addr)){
647 		memset(d, 0, sizeof(Dir));
648 		d->name = "/";
649 		d->qid.type = QTDIR;
650 		d->qid.path = f->xf->rootqid.path;
651 		d->mode = DMDIR|0777;
652 		d->uid = "bill";
653 		d->muid = "bill";
654 		d->gid = "trog";
655 	}else{
656 		/*
657 		 * assemble any long file name
658 		 */
659 		sum = aliassum(dp->d);
660 		islong = 0;
661 		name = namebuf;
662 		for(prevdo = dp->offset-DOSDIRSIZE; prevdo >= 0; prevdo -= DOSDIRSIZE){
663 			if(dp->p->iobuf[prevdo+11] != 0xf)
664 				break;
665 			name = getnamesect(namebuf, name, &dp->p->iobuf[prevdo], &islong, &sum, -1);
666 		}
667 		if(prevdo < 0 && dp->prevaddr != -1){
668 			p = getsect(f->xf, dp->prevaddr);
669 			for(prevdo = ((Dosbpb*)f->xf->ptr)->sectsize-DOSDIRSIZE; prevdo >= 0; prevdo -= DOSDIRSIZE){
670 				if(p->iobuf[prevdo+11] != 0xf)
671 					break;
672 				name = getnamesect(namebuf, name, &p->iobuf[prevdo], &islong, &sum, -1);
673 			}
674 			putsect(p);
675 		}
676 		getdir(f->xf, d, dp->d, dp->addr, dp->offset);
677 		if(islong && sum == -1 && nameok(namebuf))
678 			strcpy(d->name, namebuf);
679 	}
680 }
681 
682 void
rstat(void)683 rstat(void)
684 {
685 	Dir dir;
686 	Xfile *f;
687 
688 	f = xfile(req->fid, Asis);
689 	if(!f || getfile(f) < 0){
690 		errno = Eio;
691 		return;
692 	}
693 
694 	dir.name = repdata;
695 	dostat(f, &dir);
696 
697 	rep->nstat = convD2M(&dir, statbuf, sizeof statbuf);
698 	rep->stat = statbuf;
699 	putfile(f);
700 }
701 
702 void
rwstat(void)703 rwstat(void)
704 {
705 	Dir dir, wdir;
706 	Xfile *f, pf;
707 	Dosptr *dp, ndp, pdp;
708 	Iosect *parp;
709 	Dosdir *pard, *d, od;
710 	char sname[13];
711 	ulong oaddr, ooffset;
712 	long start, length;
713 	int i, longtype, changes, attr;
714 
715 	f = xfile(req->fid, Asis);
716 	if(!f || getfile(f) < 0){
717 		errno = Eio;
718 		return;
719 	}
720 	dp = f->ptr;
721 
722 	if(isroot(dp->addr)){
723 		errno = Eperm;
724 		goto out;
725 	}
726 
727 	changes = 0;
728 	dir.name = repdata;
729 	dostat(f, &dir);
730 	if(convM2D(req->stat, req->nstat, &wdir, (char*)statbuf) != req->nstat){
731 		errno = Ebadstat;
732 		goto out;
733 	}
734 
735 	/*
736 	 * To change length, must have write permission on file.
737 	 * we only allow truncates for now.
738 	 */
739 	if(wdir.length!=~0 && wdir.length!=dir.length){
740 		if(wdir.length > dir.length || !dir.mode & 0222){
741 			errno = Eperm;
742 			goto out;
743 		}
744 	}
745 
746 	/*
747 	 * no chown or chgrp
748 	 */
749 	if(wdir.uid[0] != '\0' && strcmp(dir.uid, wdir.uid) != 0
750 	|| wdir.gid[0] != '\0' && strcmp(dir.gid, wdir.gid) != 0){
751 		errno = Eperm;
752 		goto out;
753 	}
754 
755 	/*
756 	 * mode/mtime allowed
757 	 */
758 	if(wdir.mtime != ~0 && dir.mtime != wdir.mtime)
759 		changes = 1;
760 
761 	/*
762 	 * Setting DMAPPEND (make system file contiguous)
763 	 * requires setting DMEXCL (system file).
764 	 */
765 	if(wdir.mode != ~0){
766 		if((wdir.mode & 7) != ((wdir.mode >> 3) & 7)
767 		|| (wdir.mode & 7) != ((wdir.mode >> 6) & 7)){
768 			errno = Eperm;
769 			goto out;
770 		}
771 		if((dir.mode^wdir.mode) & (DMEXCL|DMAPPEND|0777))
772 			changes = 1;
773 		if((dir.mode^wdir.mode) & DMAPPEND) {
774 			if((wdir.mode & (DMEXCL|DMAPPEND)) == DMAPPEND) {
775 				errno = Eperm;
776 				goto out;
777 			}
778 			if((wdir.mode & DMAPPEND) && makecontig(f, 0) < 0) {
779 				errno = Econtig;
780 				goto out;
781 			}
782 		}
783 	}
784 
785 
786 	/*
787 	 * to rename:
788 	 *	1) make up a fake clone
789 	 *	2) walk to parent
790 	 *	3) remove the old entry
791 	 *	4) create entry with new name
792 	 *	5) write correct mode/mtime info
793 	 * we need to remove the old entry before creating the new one
794 	 * to avoid a lock loop.
795 	 */
796 	if(wdir.name[0] != '\0' && strcmp(dir.name, wdir.name) != 0){
797 		if(utflen(wdir.name) >= DOSNAMELEN){
798 			errno = Etoolong;
799 			goto out;
800 		}
801 
802 		/*
803 		 * grab parent directory of file to be changed and check for write perm
804 		 * rename also disallowed for read-only files in root directory
805 		 */
806 		parp = getsect(f->xf, dp->paddr);
807 		if(parp == nil){
808 			errno = Eio;
809 			goto out;
810 		}
811 		pard = (Dosdir *)&parp->iobuf[dp->poffset];
812 		if(!isroot(dp->paddr) && (pard->attr & DRONLY)
813 		|| isroot(dp->paddr) && (dp->d->attr&DRONLY)){
814 			putsect(parp);
815 			errno = Eperm;
816 			goto out;
817 		}
818 
819 		/*
820 		 * retrieve info from old entry
821 		 */
822 		oaddr = dp->addr;
823 		ooffset = dp->offset;
824 		d = dp->d;
825 		od = *d;
826 		start = getstart(f->xf, d);
827 		length = GLONG(d->length);
828 		attr = d->attr;
829 
830 		/*
831 		 * temporarily release file to allow other directory ops:
832 		 * walk to parent, validate new name
833 		 * then remove old entry
834 		 */
835 		putfile(f);
836 		pf = *f;
837 		memset(&pdp, 0, sizeof(Dosptr));
838 		pdp.prevaddr = -1;
839 		pdp.naddr = -1;
840 		pdp.addr = dp->paddr;
841 		pdp.offset = dp->poffset;
842 		pdp.p = parp;
843 		if(!isroot(pdp.addr))
844 			pdp.d = (Dosdir *)&parp->iobuf[pdp.offset];
845 		pf.ptr = &pdp;
846 		longtype = mk8dot3name(&pf, &ndp, wdir.name, sname);
847 		if(longtype==Invalid){
848 			putsect(parp);
849 			errno = Eperm;
850 			return;
851 		}
852 		if(getfile(f) < 0){
853 			putsect(parp);
854 			errno = Eio;
855 			return;
856 		}
857 		doremove(f->xf, dp);
858 		putfile(f);
859 
860 		/*
861 		 * search for dir entry again, since we may be able to use the old slot,
862 		 * and we need to set up the naddr field if a long name spans the block.
863 		 * create new entry.
864 		 */
865 		if(searchdir(&pf, wdir.name, dp, 1, longtype) < 0
866 		|| mkdentry(pf.xf, dp, wdir.name, sname, longtype, attr, start, length) < 0){
867 			putsect(parp);
868 			errno = Eio;
869 			goto out;
870 		}
871 
872 		/*
873 		 * copy invisible fields
874 		 */
875 		d = dp->d;
876 		for(i = 0; i < 2; i++)
877 			d->ctime[i] = od.ctime[i];
878 		for(i = 0; i < nelem(od.cdate); i++)
879 			d->cdate[i] = od.cdate[i];
880 		for(i = 0; i < nelem(od.adate); i++)
881 			d->adate[i] = od.adate[i];
882 
883 		putsect(parp);
884 
885 		/*
886 		 * relocate up other fids to the same file, if it moved
887 		 */
888 		f->qid.path = QIDPATH(dp);
889 		if(oaddr != dp->addr || ooffset != dp->offset)
890 			dosptrreloc(f, dp, oaddr, ooffset);
891 
892 		/*
893 		 * copy fields that are not supposed to change
894 		 */
895 		if(wdir.mtime == ~0)
896 			wdir.mtime = dir.mtime;
897 		if(wdir.mode == ~0)
898 			wdir.mode = dir.mode;
899 		changes = 1;
900 	}
901 
902 	/*
903 	 * do the actual truncate
904 	 */
905 	if(wdir.length != ~0 && wdir.length != dir.length && truncfile(f, wdir.length) < 0)
906 		errno = Eio;
907 
908 	if(changes){
909 		putdir(dp->d, &wdir);
910 		dp->p->flags |= BMOD;
911 	}
912 
913 out:
914 	putfile(f);
915 	sync();
916 }
917