]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/dossrv/dosfs.c
Backed out changeset 2737b9af622b
[plan9front.git] / sys / src / cmd / dossrv / dosfs.c
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 }
303
304 /*
305  * fill in a directory entry for a new file
306  */
307 static int
308 mkdentry(Xfs *xf, Dosptr *ndp, char *name, char *sname, int longtype, int nattr, long start, ulong 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         PSHORT(nd->cdate, GSHORT(nd->date));
333         PSHORT(nd->ctime, GSHORT(nd->time));
334         nd->ctimetenth = 0;
335         putstart(xf, nd, start);
336         PLONG(nd->length, length);
337
338         ndp->p->flags |= BMOD;
339
340         return 0;
341 }
342
343 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
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(getfile(f) < 0)
508                 goto error;
509         if(f->qid.type & QTDIR)
510                 r = readdir(f, repdata, req->offset, req->count);
511         else
512                 r = readfile(f, repdata, req->offset, req->count);
513         putfile(f);
514         if(r < 0){
515 error:
516                 errno = Eio;
517         }else{
518                 rep->count = r;
519                 rep->data = (char*)repdata;
520         }
521 }
522
523 void
524 rwrite(void)
525 {
526         Xfile *f;
527         int r;
528
529         if (!(f=xfile(req->fid, Asis)) || !(f->flags&Owrite))
530                 goto error;
531         if(getfile(f) < 0)
532                 goto error;
533         r = writefile(f, req->data, req->offset, req->count);
534         putfile(f);
535         if(r < 0){
536 error:
537                 errno = Eio;
538         }else{
539                 rep->count = r;
540         }
541 }
542
543 void
544 rclunk(void)
545 {
546         xfile(req->fid, Clunk);
547         sync();
548 }
549
550 /*
551  * wipe out a dos directory entry
552  */
553 static void
554 doremove(Xfs *xf, Dosptr *dp)
555 {
556         Iosect *p;
557         int prevdo;
558
559         dp->p->iobuf[dp->offset] = DOSEMPTY;
560         dp->p->flags |= BMOD;
561         for(prevdo = dp->offset-DOSDIRSIZE; prevdo >= 0; prevdo -= DOSDIRSIZE){
562                 if(dp->p->iobuf[prevdo+11] != 0xf)
563                         break;
564                 dp->p->iobuf[prevdo] = DOSEMPTY;
565         }
566         if(prevdo < 0 && dp->prevaddr != -1){
567                 p = getsect(xf, dp->prevaddr);
568                 for(prevdo = ((Dosbpb*)xf->ptr)->sectsize-DOSDIRSIZE; prevdo >= 0; prevdo -= DOSDIRSIZE){
569                         if(p->iobuf[prevdo+11] != 0xf)
570                                 break;
571                         p->iobuf[prevdo] = DOSEMPTY;
572                         p->flags |= BMOD;
573                 }
574                 putsect(p);
575         }               
576 }
577
578 void
579 rremove(void)
580 {
581         Xfile *f;
582         Dosptr *dp;
583         Iosect *parp;
584         Dosdir *pard;
585
586         f = xfile(req->fid, Asis);
587         parp = nil;
588         if(f == nil){
589                 errno = Eio;
590                 goto out;
591         }
592         dp = f->ptr;
593         if(isroot(dp->addr)){
594                 errno = Eperm;
595                 goto out;
596         }
597
598         /*
599          * can't remove if parent is read only,
600          * it's a non-empty directory,
601          * or it's a read only file in the root directory
602          */
603         parp = getsect(f->xf, dp->paddr);
604         if(parp == nil
605         || getfile(f) < 0){
606                 errno = Eio;
607                 goto out;
608         }
609         pard = (Dosdir *)&parp->iobuf[dp->poffset];
610         if(!isroot(dp->paddr) && (pard->attr & DRONLY)
611         || (dp->d->attr & DDIR) && emptydir(f) < 0
612         || isroot(dp->paddr) && (dp->d->attr&DRONLY)){
613                 errno = Eperm;
614                 goto out;
615         }
616         if(truncfile(f, 0) < 0){
617                 errno = Eio;
618                 goto out;
619         }
620         doremove(f->xf, f->ptr);
621         if(!isroot(dp->paddr)){
622                 puttime(pard, 0);
623                 parp->flags |= BMOD;
624         }
625 out:
626         if(parp != nil)
627                 putsect(parp);
628         if(f != nil)
629                 putfile(f);
630         xfile(req->fid, Clunk);
631         sync();
632 }
633
634 static int
635 dostat(Xfile *f, Dir *d)
636 {
637         Dosptr *dp;
638         Iosect *p;
639         char *name, namebuf[DOSNAMELEN];
640         int islong, sum, prevdo;
641
642         dp = f->ptr;
643         if(isroot(dp->addr)){
644                 memset(d, 0, sizeof(Dir));
645                 d->name = "/";
646                 d->qid.type = QTDIR;
647                 d->qid.path = f->xf->rootqid.path;
648                 d->mode = DMDIR|0777;
649                 d->uid = "bill";
650                 d->muid = "bill";
651                 d->gid = "trog";
652         }else{
653                 /*
654                  * assemble any long file name
655                  */
656                 sum = aliassum(dp->d);
657                 islong = 0;
658                 name = namebuf;
659                 for(prevdo = dp->offset-DOSDIRSIZE; prevdo >= 0; prevdo -= DOSDIRSIZE){
660                         if(dp->p->iobuf[prevdo+11] != 0xf)
661                                 break;
662                         name = getnamesect(namebuf, name, &dp->p->iobuf[prevdo], &islong, &sum, -1);
663                 }
664                 if(prevdo < 0 && dp->prevaddr != -1){
665                         p = getsect(f->xf, dp->prevaddr);
666                         if(p == nil)
667                                 return -1;
668                         for(prevdo = ((Dosbpb*)f->xf->ptr)->sectsize-DOSDIRSIZE; prevdo >= 0; prevdo -= DOSDIRSIZE){
669                                 if(p->iobuf[prevdo+11] != 0xf)
670                                         break;
671                                 name = getnamesect(namebuf, name, &p->iobuf[prevdo], &islong, &sum, -1);
672                         }
673                         putsect(p);
674                 }
675                 getdir(f->xf, d, dp->d, dp->addr, dp->offset);
676                 if(islong && sum == -1 && nameok(namebuf))
677                         strcpy(d->name, namebuf);
678         }
679         return 0;
680 }
681
682 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         dir.name = repdata;
694         if(dostat(f, &dir) < 0)
695                 errno = Eio;
696         else {
697                 rep->nstat = convD2M(&dir, statbuf, sizeof statbuf);
698                 rep->stat = statbuf;
699         }
700         putfile(f);
701 }
702
703 void
704 rwstat(void)
705 {
706         Dir dir, wdir;
707         Xfile *f, pf;
708         Dosptr *dp, ndp, pdp;
709         Iosect *parp;
710         Dosdir *pard, *d, od;
711         char sname[13];
712         ulong ooffset, length;
713         vlong oaddr;
714         long start;
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         if(dostat(f, &dir) < 0){
732                 errno = Eio;
733                 goto out;
734         }
735
736         if(convM2D(req->stat, req->nstat, &wdir, (char*)statbuf) != req->nstat){
737                 errno = Ebadstat;
738                 goto out;
739         }
740
741         /*
742          * To change length, must have write permission on file.
743          * we only allow truncates for now.
744          */
745         if(wdir.length!=~0 && wdir.length!=dir.length){
746                 if(wdir.length > dir.length || !dir.mode & 0222){
747                         errno = Eperm;
748                         goto out;
749                 }
750         }
751
752         /*
753          * no chown or chgrp
754          */
755         if(wdir.uid[0] != '\0' && strcmp(dir.uid, wdir.uid) != 0
756         || wdir.gid[0] != '\0' && strcmp(dir.gid, wdir.gid) != 0){
757                 errno = Eperm;
758                 goto out;
759         }
760
761         /*
762          * mode/mtime allowed
763          */
764         if(wdir.mtime != ~0 && dir.mtime != wdir.mtime)
765                 changes = 1;
766
767         /*
768          * Setting DMAPPEND (make system file contiguous)
769          * requires setting DMEXCL (system file).
770          */
771         if(wdir.mode != ~0){
772                 if((wdir.mode & 7) != ((wdir.mode >> 3) & 7)
773                 || (wdir.mode & 7) != ((wdir.mode >> 6) & 7)){
774                         errno = Eperm;
775                         goto out;
776                 }
777                 if((dir.mode^wdir.mode) & (DMEXCL|DMAPPEND|0777))
778                         changes = 1;
779                 if((dir.mode^wdir.mode) & DMAPPEND) {
780                         if((wdir.mode & (DMEXCL|DMAPPEND)) == DMAPPEND) {
781                                 errno = Eperm;
782                                 goto out;
783                         }
784                         if((wdir.mode & DMAPPEND) && makecontig(f, 0) < 0) {
785                                 errno = Econtig;
786                                 goto out;
787                         }
788                 }
789         }
790
791
792         /*
793          * to rename:
794          *      1) make up a fake clone
795          *      2) walk to parent
796          *      3) remove the old entry
797          *      4) create entry with new name
798          *      5) write correct mode/mtime info
799          * we need to remove the old entry before creating the new one
800          * to avoid a lock loop.
801          */
802         if(wdir.name[0] != '\0' && strcmp(dir.name, wdir.name) != 0){
803                 if(utflen(wdir.name) >= DOSNAMELEN){
804                         errno = Etoolong;
805                         goto out;
806                 }
807
808                 /*
809                  * grab parent directory of file to be changed and check for write perm
810                  * rename also disallowed for read-only files in root directory
811                  */
812                 parp = getsect(f->xf, dp->paddr);
813                 if(parp == nil){
814                         errno = Eio;
815                         goto out;
816                 }
817                 pard = (Dosdir *)&parp->iobuf[dp->poffset];
818                 if(!isroot(dp->paddr) && (pard->attr & DRONLY)
819                 || isroot(dp->paddr) && (dp->d->attr&DRONLY)){
820                         putsect(parp);
821                         errno = Eperm;
822                         goto out;
823                 }
824
825                 /*
826                  * retrieve info from old entry
827                  */
828                 oaddr = dp->addr;
829                 ooffset = dp->offset;
830                 d = dp->d;
831                 od = *d;
832                 start = getstart(f->xf, d);
833                 length = GLONG(d->length);
834                 attr = d->attr;
835
836                 /*
837                  * temporarily release file to allow other directory ops:
838                  * walk to parent, validate new name
839                  * then remove old entry
840                  */
841                 putfile(f);
842                 pf = *f;
843                 memset(&pdp, 0, sizeof(Dosptr));
844                 pdp.prevaddr = -1;
845                 pdp.naddr = -1;
846                 pdp.addr = dp->paddr;
847                 pdp.offset = dp->poffset;
848                 pdp.p = parp;
849                 if(!isroot(pdp.addr))
850                         pdp.d = (Dosdir *)&parp->iobuf[pdp.offset];
851                 pf.ptr = &pdp;
852                 longtype = mk8dot3name(&pf, &ndp, wdir.name, sname);
853                 if(longtype==Invalid){
854                         putsect(parp);
855                         errno = Eperm;
856                         return;
857                 }
858                 if(getfile(f) < 0){
859                         putsect(parp);
860                         errno = Eio;
861                         return;
862                 }
863                 doremove(f->xf, dp);
864                 putfile(f);
865
866                 /*
867                  * search for dir entry again, since we may be able to use the old slot,
868                  * and we need to set up the naddr field if a long name spans the block.
869                  * create new entry.
870                  */
871                 if(searchdir(&pf, wdir.name, dp, 1, longtype) < 0
872                 || mkdentry(pf.xf, dp, wdir.name, sname, longtype, attr, start, length) < 0){
873                         putsect(parp);
874                         errno = Eio;
875                         goto out;
876                 }
877
878                 /*
879                  * copy invisible fields
880                  */
881                 d = dp->d;
882                 d->ctimetenth = od.ctimetenth;
883                 for(i = 0; i < nelem(od.ctime); i++)
884                         d->ctime[i] = od.ctime[i];
885                 for(i = 0; i < nelem(od.cdate); i++)
886                         d->cdate[i] = od.cdate[i];
887                 for(i = 0; i < nelem(od.adate); i++)
888                         d->adate[i] = od.adate[i];
889
890                 putsect(parp);
891
892                 /*
893                  * relocate up other fids to the same file, if it moved
894                  */
895                 f->qid.path = QIDPATH(dp);
896                 if(oaddr != dp->addr || ooffset != dp->offset)
897                         dosptrreloc(f, dp, oaddr, ooffset);
898
899                 /*
900                  * copy fields that are not supposed to change
901                  */
902                 if(wdir.mtime == ~0)
903                         wdir.mtime = dir.mtime;
904                 if(wdir.mode == ~0)
905                         wdir.mode = dir.mode;
906                 changes = 1;
907         }
908
909         /*
910          * do the actual truncate
911          */
912         if(wdir.length != ~0 && wdir.length != dir.length && truncfile(f, wdir.length) < 0)
913                 errno = Eio;
914
915         if(changes){
916                 putdir(dp->d, &wdir);
917                 dp->p->flags |= BMOD;
918         }
919
920 out:
921         putfile(f);
922         sync();
923 }