]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/dossrv/dosfs.c
kernel: keep segment locked for data2txt
[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, 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
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(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
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
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
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
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
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
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
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 }