1 /*        $NetBSD: msdosfs_lookup.c,v 1.41 2022/08/06 18:26:42 andvar Exp $     */
2 
3 /*-
4  * Copyright (C) 1994, 1995, 1997 Wolfgang Solfrank.
5  * Copyright (C) 1994, 1995, 1997 TooLs GmbH.
6  * All rights reserved.
7  * Original code by Paul Popelka (paulp@uts.amdahl.com) (see below).
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  * 3. All advertising materials mentioning features or use of this software
18  *    must display the following acknowledgement:
19  *        This product includes software developed by TooLs GmbH.
20  * 4. The name of TooLs GmbH may not be used to endorse or promote products
21  *    derived from this software without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR
24  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
25  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
26  * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
27  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
28  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
29  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
30  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
31  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
32  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33  */
34 /*
35  * Written by Paul Popelka (paulp@uts.amdahl.com)
36  *
37  * You can do anything you want with this software, just don't say you wrote
38  * it, and don't remove this notice.
39  *
40  * This software is provided "as is".
41  *
42  * The author supplies this software to be publicly redistributed on the
43  * understanding that the author is not responsible for the correct
44  * functioning of this software in any circumstances and is not liable for
45  * any damages caused by this software.
46  *
47  * October 1992
48  */
49 
50 #if HAVE_NBTOOL_CONFIG_H
51 #include "nbtool_config.h"
52 #endif
53 
54 #include <sys/cdefs.h>
55 __KERNEL_RCSID(0, "$NetBSD: msdosfs_lookup.c,v 1.41 2022/08/06 18:26:42 andvar Exp $");
56 
57 #include <sys/param.h>
58 
59 #ifdef _KERNEL
60 #include <sys/systm.h>
61 #include <sys/mount.h>
62 #include <sys/kauth.h>
63 #include <sys/namei.h>
64 #include <sys/dirent.h>
65 #include <sys/buf.h>
66 #include <sys/vnode.h>
67 #include <sys/atomic.h>
68 #else
69 #include <ffs/buf.h>
70 #endif /* _KERNEL */
71 
72 #include <fs/msdosfs/bpb.h>
73 #include <fs/msdosfs/direntry.h>
74 #include <fs/msdosfs/denode.h>
75 #include <fs/msdosfs/msdosfsmount.h>
76 #include <fs/msdosfs/fat.h>
77 
78 
79 #ifdef _KERNEL
80 /*
81  * When we search a directory the blocks containing directory entries are
82  * read and examined.  The directory entries contain information that would
83  * normally be in the inode of a unix filesystem.  This means that some of
84  * a directory's contents may also be in memory resident denodes (sort of
85  * an inode).  This can cause problems if we are searching while some other
86  * process is modifying a directory.  To prevent one process from accessing
87  * incompletely modified directory information we depend upon being the
88  * sole owner of a directory block.  bread/brelse provide this service.
89  * This being the case, when a process modifies a directory it must first
90  * acquire the disk block that contains the directory entry to be modified.
91  * Then update the disk block and the denode, and then write the disk block
92  * out to disk.  This way disk blocks containing directory entries and in
93  * memory denode's will be in synch.
94  */
95 int
msdosfs_lookup(void * v)96 msdosfs_lookup(void *v)
97 {
98           struct vop_lookup_v2_args /* {
99                     struct vnode *a_dvp;
100                     struct vnode **a_vpp;
101                     struct componentname *a_cnp;
102           } */ *ap = v;
103           struct vnode *vdp = ap->a_dvp;
104           struct vnode **vpp = ap->a_vpp;
105           struct componentname *cnp = ap->a_cnp;
106           daddr_t bn;
107           int error;
108           int slotcount;
109           int slotoffset = 0;
110           int frcn;
111           u_long cluster;
112           int blkoff;
113           int diroff;
114           int blsize;
115           int isadir;                   /* ~0 if found direntry is a directory   */
116           u_long scn;                   /* starting cluster number               */
117           struct denode *dp;
118           struct msdosfsmount *pmp;
119           struct buf *bp = 0;
120           struct direntry *dep;
121           u_char dosfilename[12];
122           int flags;
123           int nameiop = cnp->cn_nameiop;
124           int wincnt = 1;
125           int chksum = -1, chksum_ok;
126           int olddos = 1;
127 
128           flags = cnp->cn_flags;
129 
130 #ifdef MSDOSFS_DEBUG
131           printf("msdosfs_lookup(): looking for %.*s\n",
132                     (int)cnp->cn_namelen, cnp->cn_nameptr);
133 #endif
134           dp = VTODE(vdp);
135           pmp = dp->de_pmp;
136           *vpp = NULL;
137 #ifdef MSDOSFS_DEBUG
138           printf("msdosfs_lookup(): vdp %p, dp %p, Attr %02x\n",
139               vdp, dp, dp->de_Attributes);
140 #endif
141 
142           /*
143            * Check accessibility of directory.
144            */
145           if ((error = VOP_ACCESS(vdp, VEXEC, cnp->cn_cred)) != 0)
146                     return (error);
147 
148           if ((flags & ISLASTCN) && (vdp->v_mount->mnt_flag & MNT_RDONLY) &&
149               (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME))
150                     return (EROFS);
151 
152           /*
153            * We now have a segment name to search for, and a directory to search.
154            *
155            * Before tediously performing a linear scan of the directory,
156            * check the name cache to see if the directory/name pair
157            * we are looking for is known already.
158            */
159           if (cache_lookup(vdp, cnp->cn_nameptr, cnp->cn_namelen,
160                                cnp->cn_nameiop, cnp->cn_flags, NULL, vpp)) {
161                     return *vpp == NULLVP ? ENOENT: 0;
162           }
163 
164           /* May need to restart the lookup with an exclusive lock. */
165           if (VOP_ISLOCKED(vdp) != LK_EXCLUSIVE)
166                     return ENOLCK;
167 
168           /*
169            * If they are going after the . or .. entry in the root directory,
170            * they won't find it.  DOS filesystems don't have them in the root
171            * directory.  So, we fake it. deget() is in on this scam too.
172            */
173           if ((vdp->v_vflag & VV_ROOT) && cnp->cn_nameptr[0] == '.' &&
174               (cnp->cn_namelen == 1 ||
175                     (cnp->cn_namelen == 2 && cnp->cn_nameptr[1] == '.'))) {
176                     isadir = ATTR_DIRECTORY;
177                     scn = MSDOSFSROOT;
178 #ifdef MSDOSFS_DEBUG
179                     printf("msdosfs_lookup(): looking for . or .. in root directory\n");
180 #endif
181                     cluster = MSDOSFSROOT;
182                     blkoff = MSDOSFSROOT_OFS;
183                     goto foundroot;
184           }
185 
186           switch (msdosfs_unix2dosfn((const u_char *)cnp->cn_nameptr, dosfilename,
187               cnp->cn_namelen, 0)) {
188           case 0:
189                     return (EINVAL);
190           case 1:
191                     break;
192           case 2:
193                     wincnt = msdosfs_winSlotCnt((const u_char *)cnp->cn_nameptr,
194                         cnp->cn_namelen, pmp->pm_flags & MSDOSFSMNT_UTF8) + 1;
195                     break;
196           case 3:
197                     olddos = 0;
198                     wincnt = msdosfs_winSlotCnt((const u_char *)cnp->cn_nameptr,
199                         cnp->cn_namelen, pmp->pm_flags & MSDOSFSMNT_UTF8) + 1;
200                     break;
201           }
202           if (pmp->pm_flags & MSDOSFSMNT_SHORTNAME)
203                     wincnt = 1;
204 
205           /*
206            * Suppress search for slots unless creating
207            * file and at end of pathname, in which case
208            * we watch for a place to put the new file in
209            * case it doesn't already exist.
210            */
211           slotcount = wincnt;
212           if ((nameiop == CREATE || nameiop == RENAME) &&
213               (flags & ISLASTCN))
214                     slotcount = 0;
215 
216 #ifdef MSDOSFS_DEBUG
217           printf("msdosfs_lookup(): dos filename: %s\n", dosfilename);
218 #endif
219           /*
220            * Search the directory pointed at by vdp for the name pointed at
221            * by cnp->cn_nameptr.
222            */
223 
224           /*
225            * The outer loop ranges over the clusters that make up the
226            * directory.  Note that the root directory is different from all
227            * other directories.  It has a fixed number of blocks that are not
228            * part of the pool of allocatable clusters.  So, we treat it a
229            * little differently. The root directory starts at "cluster" 0.
230            */
231           diroff = 0;
232           for (frcn = 0; diroff < dp->de_FileSize; frcn++) {
233                     if ((error = msdosfs_pcbmap(dp, frcn, &bn, &cluster,
234                         &blsize)) != 0) {
235                               if (error == E2BIG)
236                                         break;
237                               return (error);
238                     }
239                     error = bread(pmp->pm_devvp, de_bn2kb(pmp, bn), blsize,
240                         0, &bp);
241                     if (error) {
242                               return (error);
243                     }
244                     for (blkoff = 0; blkoff < blsize;
245                          blkoff += sizeof(struct direntry),
246                          diroff += sizeof(struct direntry)) {
247                               dep = (struct direntry *)((char *)bp->b_data + blkoff);
248                               /*
249                                * If the slot is empty and we are still looking
250                                * for an empty then remember this one.  If the
251                                * slot is not empty then check to see if it
252                                * matches what we are looking for.  If the slot
253                                * has never been filled with anything, then the
254                                * remainder of the directory has never been used,
255                                * so there is no point in searching it.
256                                */
257                               if (dep->deName[0] == SLOT_EMPTY ||
258                                   dep->deName[0] == SLOT_DELETED) {
259                                         /*
260                                          * Drop memory of previous long matches
261                                          */
262                                         chksum = -1;
263 
264                                         if (slotcount < wincnt) {
265                                                   slotcount++;
266                                                   slotoffset = diroff;
267                                         }
268                                         if (dep->deName[0] == SLOT_EMPTY) {
269                                                   brelse(bp, 0);
270                                                   goto notfound;
271                                         }
272                               } else {
273                                         /*
274                                          * If there wasn't enough space for our
275                                          * winentries, forget about the empty space
276                                          */
277                                         if (slotcount < wincnt)
278                                                   slotcount = 0;
279 
280                                         /*
281                                          * Check for Win95 long filename entry
282                                          */
283                                         if (dep->deAttributes == ATTR_WIN95) {
284                                                   if (pmp->pm_flags & MSDOSFSMNT_SHORTNAME)
285                                                             continue;
286 
287                                                   chksum = msdosfs_winChkName((const u_char *)cnp->cn_nameptr,
288                                                                           cnp->cn_namelen,
289                                                                           (struct winentry *)dep,
290                                                                           chksum,
291                                                                           pmp->pm_flags & MSDOSFSMNT_UTF8);
292                                                   continue;
293                                         }
294 
295                                         /*
296                                          * Ignore volume labels (anywhere, not just
297                                          * the root directory).
298                                          */
299                                         if (dep->deAttributes & ATTR_VOLUME) {
300                                                   chksum = -1;
301                                                   continue;
302                                         }
303 
304                                         /*
305                                          * Check for a checksum or name match
306                                          */
307                                         chksum_ok =
308                                             (chksum == msdosfs_winChksum(dep->deName));
309                                         if (!chksum_ok && (
310                                                   !olddos ||
311                                                   memcmp(&dosfilename[0],dep->deName,8) ||
312                                                   memcmp(&dosfilename[8],dep->deExtension,3))) {
313                                                   chksum = -1;
314                                                   continue;
315                                         }
316 #ifdef MSDOSFS_DEBUG
317                                         printf("msdosfs_lookup(): match blkoff %d, diroff %d\n",
318                                             blkoff, diroff);
319 #endif
320                                         /*
321                                          * Remember where this directory
322                                          * entry came from for whoever did
323                                          * this lookup.
324                                          */
325                                         dp->de_crap.mlr_fndoffset = diroff;
326                                         if (chksum_ok && nameiop == RENAME) {
327                                                   /*
328                                                    * Target had correct long name
329                                                    * directory entries, reuse them
330                                                    * as needed.
331                                                    */
332                                                   dp->de_crap.mlr_fndcnt = wincnt - 1;
333                                         } else {
334                                                   /*
335                                                    * Long name directory entries
336                                                    * not present or corrupt, can only
337                                                    * reuse dos directory entry.
338                                                    */
339                                                   dp->de_crap.mlr_fndcnt = 0;
340                                         }
341 
342                                         goto found;
343                               }
344                     }         /* for (blkoff = 0; .... */
345                     /*
346                      * Release the buffer holding the directory cluster just
347                      * searched.
348                      */
349                     brelse(bp, 0);
350           }         /* for (frcn = 0; ; frcn++) */
351 
352 notfound:
353           /*
354            * We hold no disk buffers at this point.
355            */
356 
357           /*
358            * If we get here we didn't find the entry we were looking for. But
359            * that's ok if we are creating or renaming and are at the end of
360            * the pathname and the directory hasn't been removed.
361            */
362 #ifdef MSDOSFS_DEBUG
363           printf("msdosfs_lookup(): op %d, refcnt %ld, slotcount %d, slotoffset %d\n",
364               nameiop, dp->de_refcnt, slotcount, slotoffset);
365 #endif
366           if ((nameiop == CREATE || nameiop == RENAME) &&
367               (flags & ISLASTCN) && dp->de_refcnt != 0) {
368                     /*
369                      * Access for write is interpreted as allowing
370                      * creation of files in the directory.
371                      */
372                     error = VOP_ACCESS(vdp, VWRITE, cnp->cn_cred);
373                     if (error)
374                               return (error);
375 
376                     /*
377                      * Fixup the slot description to point to the place where
378                      * we might put the new DOS direntry (putting the Win95
379                      * long name entries before that)
380                      */
381                     if (!slotcount) {
382                               slotcount = 1;
383                               slotoffset = diroff;
384                     }
385                     if (wincnt > slotcount) {
386                               slotoffset +=
387                                         sizeof(struct direntry) * (wincnt - slotcount);
388                     }
389 
390                     /*
391                      * Return an indication of where the new directory
392                      * entry should be put.
393                      */
394                     dp->de_crap.mlr_fndoffset = slotoffset;
395                     dp->de_crap.mlr_fndcnt = wincnt - 1;
396 
397                     /*
398                      * We return with the directory locked, so that
399                      * the parameters we set up above will still be
400                      * valid if we actually decide to do a direnter().
401                      * We return ni_vp == NULL to indicate that the entry
402                      * does not currently exist; we leave a pointer to
403                      * the (locked) directory inode in ndp->ni_dvp.
404                      *
405                      * NB - if the directory is unlocked, then this
406                      * information cannot be used.
407                      */
408                     return (EJUSTRETURN);
409           }
410 
411 #if 0
412           /*
413            * Insert name into cache (as non-existent) if appropriate.
414            *
415            * XXX Negative caching is broken for msdosfs because the name
416            * cache doesn't understand peculiarities such as case insensitivity
417            * and 8.3 filenames.  Hence, it may not invalidate all negative
418            * entries if a file with this name is later created.
419            * e.g. creating a file 'foo' won't invalidate a negative entry
420            * for 'FOO'.
421            */
422           if (nameiop != CREATE)
423                     cache_enter(vdp, *vpp, cnp->cn_nameptr, cnp->cn_namelen,
424                                   cnp->cn_flags);
425 #endif
426 
427           return (ENOENT);
428 
429 found:
430           /*
431            * NOTE:  We still have the buffer with matched directory entry at
432            * this point.
433            */
434           isadir = dep->deAttributes & ATTR_DIRECTORY;
435           scn = getushort(dep->deStartCluster);
436           if (FAT32(pmp)) {
437                     scn |= getushort(dep->deHighClust) << 16;
438                     if (scn == pmp->pm_rootdirblk) {
439                               /*
440                                * There should actually be 0 here.
441                                * Just ignore the error.
442                                */
443                               scn = MSDOSFSROOT;
444                     }
445           }
446 
447           if (isadir) {
448                     cluster = scn;
449                     if (cluster == MSDOSFSROOT)
450                               blkoff = MSDOSFSROOT_OFS;
451                     else
452                               blkoff = 0;
453           } else if (cluster == MSDOSFSROOT)
454                     blkoff = diroff;
455 
456           /*
457            * Now release buf to allow deget to read the entry again.
458            * Reserving it here and giving it to deget could result
459            * in a deadlock.
460            */
461           brelse(bp, 0);
462 
463 foundroot:
464           /*
465            * If we entered at foundroot, then we are looking for the . or ..
466            * entry of the filesystems root directory.  isadir and scn were
467            * setup before jumping here.  And, bp is already null.
468            */
469           if (FAT32(pmp) && scn == MSDOSFSROOT)
470                     scn = pmp->pm_rootdirblk;
471 
472           /*
473            * If deleting, and at end of pathname, return
474            * parameters which can be used to remove file.
475            * Lock the inode, being careful with ".".
476            */
477           if (nameiop == DELETE && (flags & ISLASTCN)) {
478                     /*
479                      * Don't allow deleting the root.
480                      */
481                     if (blkoff == MSDOSFSROOT_OFS)
482                               return EINVAL;
483 
484                     /*
485                      * Write access to directory required to delete files.
486                      */
487                     error = VOP_ACCESS(vdp, VWRITE, cnp->cn_cred);
488                     if (error)
489                               return (error);
490 
491                     /*
492                      * Return pointer to current entry in dp->i_offset.
493                      * Save directory inode pointer in ndp->ni_dvp for dirremove().
494                      */
495                     if (dp->de_StartCluster == scn && isadir) {       /* "." */
496                               vref(vdp);
497                               *vpp = vdp;
498                               return (0);
499                     }
500                     error = msdosfs_deget(pmp, cluster, blkoff, vpp);
501                     return error;
502           }
503 
504           /*
505            * If rewriting (RENAME), return the inode and the
506            * information required to rewrite the present directory
507            * Must get inode of directory entry to verify it's a
508            * regular file, or empty directory.
509            */
510           if (nameiop == RENAME && (flags & ISLASTCN)) {
511 
512                     if (vdp->v_mount->mnt_flag & MNT_RDONLY)
513                               return (EROFS);
514 
515                     if (blkoff == MSDOSFSROOT_OFS)
516                               return EINVAL;
517 
518                     error = VOP_ACCESS(vdp, VWRITE, cnp->cn_cred);
519                     if (error)
520                               return (error);
521 
522                     /*
523                      * Careful about locking second inode.
524                      * This can only occur if the target is ".".
525                      */
526                     if (dp->de_StartCluster == scn && isadir)
527                               return (EISDIR);
528 
529                     error = msdosfs_deget(pmp, cluster, blkoff, vpp);
530                     return error;
531           }
532 
533           if (dp->de_StartCluster == scn && isadir) {
534                     vref(vdp);          /* we want ourself, ie "." */
535                     *vpp = vdp;
536           } else if ((error = msdosfs_deget(pmp, cluster, blkoff, vpp)) != 0) {
537                     return error;
538           }
539 
540           /*
541            * Insert name into cache if appropriate.
542            */
543           cache_enter(vdp, *vpp, cnp->cn_nameptr, cnp->cn_namelen, cnp->cn_flags);
544 
545           return 0;
546 }
547 #endif /* _KERNEL */
548 
549 /*
550  * dep  - directory entry to copy into the directory
551  * ddep - directory to add to
552  * depp - return the address of the denode for the created directory entry
553  *          if depp != 0
554  * cnp  - componentname needed for Win95 long filenames
555  */
556 int
msdosfs_createde(struct denode * dep,struct denode * ddep,const struct msdosfs_lookup_results * mlr,struct denode ** depp,struct componentname * cnp)557 msdosfs_createde(struct denode *dep, struct denode *ddep,
558     const struct msdosfs_lookup_results *mlr,
559     struct denode **depp, struct componentname *cnp)
560 {
561           int error, rberror;
562           u_long dirclust, clusoffset;
563           u_long fndoffset, havecnt = 0, wcnt = 1, i;
564           struct direntry *ndep;
565           struct msdosfsmount *pmp = ddep->de_pmp;
566           struct buf *bp;
567           daddr_t bn;
568           int blsize;
569 #ifdef _KERNEL
570           int async = ddep->de_pmp->pm_mountp->mnt_flag & MNT_ASYNC;
571 #else
572 #define async 0
573 #endif
574 
575 #ifdef MSDOSFS_DEBUG
576           printf("createde(dep %p, ddep %p, depp %p, cnp %p)\n",
577               dep, ddep, depp, cnp);
578 #endif
579 
580           /*
581            * If no space left in the directory then allocate another cluster
582            * and chain it onto the end of the file.  There is one exception
583            * to this.  That is, if the root directory has no more space it
584            * can NOT be expanded.  extendfile() checks for and fails attempts
585            * to extend the root directory.  We just return an error in that
586            * case.
587            */
588           if (mlr->mlr_fndoffset >= ddep->de_FileSize) {
589                     u_long needlen = ddep->de_crap.mlr_fndoffset
590                         + sizeof(struct direntry) - ddep->de_FileSize;
591                     dirclust = de_clcount(pmp, needlen);
592                     if ((error = msdosfs_extendfile(ddep, dirclust, 0, 0,
593                         DE_CLEAR)) != 0) {
594                               (void)msdosfs_detrunc(ddep, ddep->de_FileSize, 0,
595                                   NOCRED);
596                               goto err_norollback;
597                     }
598 
599                     /*
600                      * Update the size of the directory
601                      */
602                     ddep->de_FileSize += de_cn2off(pmp, dirclust);
603           }
604 
605           /*
606            * We just read in the cluster with space.  Copy the new directory
607            * entry in.  Then write it to disk. NOTE:  DOS directories
608            * do not get smaller as clusters are emptied.
609            */
610           error = msdosfs_pcbmap(ddep, de_cluster(pmp, mlr->mlr_fndoffset),
611                            &bn, &dirclust, &blsize);
612           if (error)
613                     goto err_norollback;
614           clusoffset = mlr->mlr_fndoffset;
615           if (dirclust != MSDOSFSROOT)
616                     clusoffset &= pmp->pm_crbomask;
617           if ((error = bread(pmp->pm_devvp, de_bn2kb(pmp, bn), blsize,
618               B_MODIFY, &bp)) != 0) {
619                     goto err_norollback;
620           }
621           ndep = bptoep(pmp, bp, clusoffset);
622 
623           DE_EXTERNALIZE(ndep, dep);
624 
625           /*
626            * Now write the Win95 long name
627            */
628           if (mlr->mlr_fndcnt > 0) {
629                     u_int8_t chksum = msdosfs_winChksum(ndep->deName);
630                     const u_char *un = (const u_char *)cnp->cn_nameptr;
631                     int unlen = cnp->cn_namelen;
632                     u_long xhavecnt;
633 
634                     fndoffset = mlr->mlr_fndoffset;
635                     xhavecnt = mlr->mlr_fndcnt + 1;
636 
637                     for(; wcnt < xhavecnt; wcnt++) {
638                               if ((fndoffset & pmp->pm_crbomask) == 0) {
639                                         /* we should never get here if ddep is root
640                                          * directory */
641 
642                                         if (async)
643                                                   (void) bdwrite(bp);
644                                         else if ((error = bwrite(bp)) != 0)
645                                                   goto rollback;
646 
647                                         fndoffset -= sizeof(struct direntry);
648                                         error = msdosfs_pcbmap(ddep,
649                                                          de_cluster(pmp, fndoffset),
650                                                          &bn, 0, &blsize);
651                                         if (error)
652                                                   goto rollback;
653 
654                                         error = bread(pmp->pm_devvp, de_bn2kb(pmp, bn),
655                                             blsize, B_MODIFY, &bp);
656                                         if (error) {
657                                                   goto rollback;
658                                         }
659                                         ndep = bptoep(pmp, bp,
660                                                             fndoffset & pmp->pm_crbomask);
661                               } else {
662                                         ndep--;
663                                         fndoffset -= sizeof(struct direntry);
664                               }
665                               if (!msdosfs_unix2winfn(un, unlen,
666                                                   (struct winentry *)ndep,
667                                                   wcnt, chksum,
668                                                   ddep->de_pmp->pm_flags & MSDOSFSMNT_UTF8))
669                                         break;
670                     }
671           }
672 
673           if (async)
674                     bdwrite(bp);
675           else if ((error = bwrite(bp)) != 0)
676                     goto rollback;
677 
678           /*
679            * If they want us to return with the denode gotten.
680            */
681           if (depp) {
682                     u_long diroffset = clusoffset;
683 
684                     if (dep->de_Attributes & ATTR_DIRECTORY) {
685                               dirclust = dep->de_StartCluster;
686                               if (FAT32(pmp) && dirclust == pmp->pm_rootdirblk)
687                                         dirclust = MSDOSFSROOT;
688                               if (dirclust == MSDOSFSROOT)
689                                         diroffset = MSDOSFSROOT_OFS;
690                               else
691                                         diroffset = 0;
692                     }
693 #ifdef MAKEFS
694                     error = msdosfs_deget(pmp, dirclust, diroffset, depp);
695 #else
696                     struct vnode *vp;
697 
698                     error = msdosfs_deget(pmp, dirclust, diroffset, &vp);
699                     if (error == 0)
700                               *depp = VTODE(vp);
701                     else
702                               *depp = NULL;
703 #endif
704                     return error;
705           }
706 
707           return 0;
708 
709     rollback:
710           /*
711            * Mark all slots modified so far as deleted. Note that we
712            * can't just call removede(), since directory is not in
713            * consistent state.
714            */
715           fndoffset = mlr->mlr_fndoffset;
716           rberror = msdosfs_pcbmap(ddep, de_cluster(pmp, fndoffset),
717                  &bn, NULL, &blsize);
718           if (rberror)
719                     goto err_norollback;
720           if ((rberror = bread(pmp->pm_devvp, de_bn2kb(pmp, bn), blsize,
721               B_MODIFY, &bp)) != 0) {
722                     goto err_norollback;
723           }
724           ndep = bptoep(pmp, bp, clusoffset);
725 
726           havecnt = mlr->mlr_fndcnt + 1;
727           for(i = wcnt; i <= havecnt; i++) {
728                     /* mark entry as deleted */
729                     ndep->deName[0] = SLOT_DELETED;
730 
731                     if ((fndoffset & pmp->pm_crbomask) == 0) {
732                               /* we should never get here if ddep is root
733                                * directory */
734 
735                               if (async)
736                                         bdwrite(bp);
737                               else if ((rberror = bwrite(bp)) != 0)
738                                         goto err_norollback;
739 
740                               fndoffset -= sizeof(struct direntry);
741                               rberror = msdosfs_pcbmap(ddep,
742                                                de_cluster(pmp, fndoffset),
743                                                &bn, 0, &blsize);
744                               if (rberror)
745                                         goto err_norollback;
746 
747                               rberror = bread(pmp->pm_devvp, de_bn2kb(pmp, bn),
748                                   blsize, B_MODIFY, &bp);
749                               if (rberror) {
750                                         goto err_norollback;
751                               }
752                               ndep = bptoep(pmp, bp, fndoffset);
753                     } else {
754                               ndep--;
755                               fndoffset -= sizeof(struct direntry);
756                     }
757           }
758 
759           /* ignore any further error */
760           if (async)
761                     (void) bdwrite(bp);
762           else
763                     (void) bwrite(bp);
764 
765     err_norollback:
766           return error;
767 }
768 
769 /*
770  * Be sure a directory is empty except for "." and "..". Return 1 if empty,
771  * return 0 if not empty or error.
772  */
773 int
msdosfs_dosdirempty(struct denode * dep)774 msdosfs_dosdirempty(struct denode *dep)
775 {
776           int blsize;
777           int error;
778           u_long cn;
779           daddr_t bn;
780           struct buf *bp;
781           struct msdosfsmount *pmp = dep->de_pmp;
782           struct direntry *dentp;
783 
784           /*
785            * Since the filesize field in directory entries for a directory is
786            * zero, we just have to feel our way through the directory until
787            * we hit end of file.
788            */
789           for (cn = 0;; cn++) {
790                     if ((error = msdosfs_pcbmap(dep, cn, &bn, 0, &blsize)) != 0) {
791                               if (error == E2BIG)
792                                         return (1);         /* it's empty */
793                               return (0);
794                     }
795                     error = bread(pmp->pm_devvp, de_bn2kb(pmp, bn), blsize,
796                         0, &bp);
797                     if (error) {
798                               return (0);
799                     }
800                     for (dentp = (struct direntry *)bp->b_data;
801                          (char *)dentp < (char *)bp->b_data + blsize;
802                          dentp++) {
803                               if (dentp->deName[0] != SLOT_DELETED &&
804                                   (dentp->deAttributes & ATTR_VOLUME) == 0) {
805                                         /*
806                                          * In dos directories an entry whose name
807                                          * starts with SLOT_EMPTY (0) starts the
808                                          * beginning of the unused part of the
809                                          * directory, so we can just return that it
810                                          * is empty.
811                                          */
812                                         if (dentp->deName[0] == SLOT_EMPTY) {
813                                                   brelse(bp, 0);
814                                                   return (1);
815                                         }
816                                         /*
817                                          * Any names other than "." and ".." in a
818                                          * directory mean it is not empty.
819                                          */
820                                         if (memcmp(dentp->deName, ".          ", 11) &&
821                                             memcmp(dentp->deName, "..         ", 11)) {
822                                                   brelse(bp, 0);
823 #ifdef MSDOSFS_DEBUG
824                                                   printf("dosdirempty(): found %.11s, %d, %d\n",
825                                                       dentp->deName, dentp->deName[0],
826                                                             dentp->deName[1]);
827 #endif
828                                                   return (0);         /* not empty */
829                                         }
830                               }
831                     }
832                     brelse(bp, 0);
833           }
834           /* NOTREACHED */
835 }
836 
837 /*
838  * Read in the disk block containing the directory entry (dirclu, dirofs)
839  * and return the address of the buf header, and the address of the
840  * directory entry within the block.
841  */
842 int
msdosfs_readep(struct msdosfsmount * pmp,u_long dirclust,u_long diroffset,struct buf ** bpp,struct direntry ** epp)843 msdosfs_readep(struct msdosfsmount *pmp, u_long dirclust, u_long diroffset,
844     struct buf **bpp, struct direntry **epp)
845 {
846           int error;
847           daddr_t bn;
848           int blsize;
849 
850           blsize = pmp->pm_bpcluster;
851           if (dirclust == MSDOSFSROOT
852               && de_blk(pmp, diroffset + blsize) > pmp->pm_rootdirsize)
853                     blsize = de_bn2off(pmp, pmp->pm_rootdirsize) & pmp->pm_crbomask;
854           bn = detobn(pmp, dirclust, diroffset);
855           if ((error = bread(pmp->pm_devvp, de_bn2kb(pmp, bn), blsize,
856               0, bpp)) != 0) {
857                     *bpp = NULL;
858                     return (error);
859           }
860           if (epp)
861                     *epp = bptoep(pmp, *bpp, diroffset);
862           return (0);
863 }
864 
865 /*
866  * Read in the disk block containing the directory entry dep came from and
867  * return the address of the buf header, and the address of the directory
868  * entry within the block.
869  */
870 int
msdosfs_readde(struct denode * dep,struct buf ** bpp,struct direntry ** epp)871 msdosfs_readde(struct denode *dep, struct buf **bpp, struct direntry **epp)
872 {
873           return (msdosfs_readep(dep->de_pmp, dep->de_dirclust, dep->de_diroffset,
874                               bpp, epp));
875 }
876 
877 /*
878  * Remove a directory entry. At this point the file represented by the
879  * directory entry to be removed is still full length until noone has it
880  * open.  When the file no longer being used msdosfs_inactive() is called
881  * and will truncate the file to 0 length.  When the vnode containing the
882  * denode is needed for some other purpose by VFS it will call
883  * msdosfs_reclaim() which will remove the denode from the denode cache.
884  */
885 int
msdosfs_removede(struct denode * pdep,struct denode * dep,const struct msdosfs_lookup_results * mlr)886 msdosfs_removede(struct denode *pdep, struct denode *dep,
887     const struct msdosfs_lookup_results *mlr)
888           /* pdep:   directory where the entry is removed */
889           /* dep:    file to be removed */
890           /* mlr:    position of dep in pdep from lookup */
891 {
892           int error;
893           struct direntry *ep;
894           struct buf *bp;
895           daddr_t bn;
896           int blsize;
897           struct msdosfsmount *pmp = pdep->de_pmp;
898           u_long offset = mlr->mlr_fndoffset;
899 #ifdef _KERNEL
900           int async = pdep->de_pmp->pm_mountp->mnt_flag & MNT_ASYNC;
901 #else
902 #define async 0
903 #endif
904 
905 #ifdef MSDOSFS_DEBUG
906           printf("removede(): filename %s, dep %p, offset %08lx\n",
907               dep->de_Name, dep, offset);
908 #endif
909 
910           if (--dep->de_refcnt == 0) {
911 #ifndef MAKEFS
912                     struct denode_key old_key = dep->de_key;
913                     struct denode_key new_key = dep->de_key;
914 
915                     KASSERT(new_key.dk_dirgen == NULL);
916                     new_key.dk_dirgen = dep;
917                     vcache_rekey_enter(pmp->pm_mountp, DETOV(dep), &old_key,
918                         sizeof(old_key), &new_key, sizeof(new_key));
919                     dep->de_key = new_key;
920                     vcache_rekey_exit(pmp->pm_mountp, DETOV(dep), &old_key,
921                         sizeof(old_key), &dep->de_key, sizeof(dep->de_key));
922 #endif
923           }
924           offset += sizeof(struct direntry);
925           do {
926                     offset -= sizeof(struct direntry);
927                     error = msdosfs_pcbmap(pdep, de_cluster(pmp, offset), &bn, 0,
928                         &blsize);
929                     if (error)
930                               return error;
931                     error = bread(pmp->pm_devvp, de_bn2kb(pmp, bn), blsize,
932                         B_MODIFY, &bp);
933                     if (error) {
934                               return error;
935                     }
936                     ep = bptoep(pmp, bp, offset);
937                     /*
938                      * Check whether, if we came here the second time, i.e.
939                      * when underflowing into the previous block, the last
940                      * entry in this block is a longfilename entry, too.
941                      */
942                     if (ep->deAttributes != ATTR_WIN95
943                         && offset != mlr->mlr_fndoffset) {
944                               brelse(bp, 0);
945                               break;
946                     }
947                     offset += sizeof(struct direntry);
948                     while (1) {
949                               /*
950                                * We are a bit aggressive here in that we delete any Win95
951                                * entries preceding this entry, not just the ones we "own".
952                                * Since these presumably aren't valid anyway,
953                                * there should be no harm.
954                                */
955                               offset -= sizeof(struct direntry);
956                               ep--->deName[0] = SLOT_DELETED;
957                               if ((pmp->pm_flags & MSDOSFSMNT_NOWIN95)
958                                   || !(offset & pmp->pm_crbomask)
959                                   || ep->deAttributes != ATTR_WIN95)
960                                         break;
961                     }
962                     if (async)
963                               bdwrite(bp);
964                     else if ((error = bwrite(bp)) != 0)
965                               return error;
966           } while (!(pmp->pm_flags & MSDOSFSMNT_NOWIN95)
967               && !(offset & pmp->pm_crbomask)
968               && offset);
969           return 0;
970 }
971 
972 /*
973  * Create a unique DOS name in dvp
974  */
975 int
msdosfs_uniqdosname(struct denode * dep,struct componentname * cnp,u_char * cp)976 msdosfs_uniqdosname(struct denode *dep, struct componentname *cnp, u_char *cp)
977 {
978           struct msdosfsmount *pmp = dep->de_pmp;
979           struct direntry *dentp;
980           int gen;
981           int blsize;
982           u_long cn;
983           daddr_t bn;
984           struct buf *bp;
985           int error;
986 
987           for (gen = 1;; gen++) {
988                     /*
989                      * Generate DOS name with generation number
990                      */
991                     if (!msdosfs_unix2dosfn((const u_char *)cnp->cn_nameptr, cp,
992                         cnp->cn_namelen, gen))
993                               return gen == 1 ? EINVAL : EEXIST;
994 
995                     /*
996                      * Now look for a dir entry with this exact name
997                      */
998                     for (cn = error = 0; !error; cn++) {
999                               if ((error = msdosfs_pcbmap(dep, cn, &bn, 0,
1000                                   &blsize)) != 0) {
1001                                         if (error == E2BIG) /* EOF reached and not found */
1002                                                   return 0;
1003                                         return error;
1004                               }
1005                               error = bread(pmp->pm_devvp, de_bn2kb(pmp, bn), blsize,
1006                                   0, &bp);
1007                               if (error) {
1008                                         return error;
1009                               }
1010                               for (dentp = (struct direntry *)bp->b_data;
1011                                    (char *)dentp < (char *)bp->b_data + blsize;
1012                                    dentp++) {
1013                                         if (dentp->deName[0] == SLOT_EMPTY) {
1014                                                   /*
1015                                                    * Last used entry and not found
1016                                                    */
1017                                                   brelse(bp, 0);
1018                                                   return 0;
1019                                         }
1020                                         /*
1021                                          * Ignore volume labels and Win95 entries
1022                                          */
1023                                         if (dentp->deAttributes & ATTR_VOLUME)
1024                                                   continue;
1025                                         if (!memcmp(dentp->deName, cp, 11)) {
1026                                                   error = EEXIST;
1027                                                   break;
1028                                         }
1029                               }
1030                               brelse(bp, 0);
1031                     }
1032           }
1033 }
1034 
1035 /*
1036  * Find any Win'95 long filename entry in directory dep
1037  */
1038 int
msdosfs_findwin95(struct denode * dep)1039 msdosfs_findwin95(struct denode *dep)
1040 {
1041           struct msdosfsmount *pmp = dep->de_pmp;
1042           struct direntry *dentp;
1043           int blsize, win95;
1044           u_long cn;
1045           daddr_t bn;
1046           struct buf *bp;
1047 
1048           win95 = 1;
1049           /*
1050            * Read through the directory looking for Win'95 entries
1051            * XXX Note: Error currently handled just as EOF
1052            */
1053           for (cn = 0;; cn++) {
1054                     if (msdosfs_pcbmap(dep, cn, &bn, 0, &blsize))
1055                               return win95;
1056                     if (bread(pmp->pm_devvp, de_bn2kb(pmp, bn), blsize,
1057                         0, &bp)) {
1058                               return win95;
1059                     }
1060                     for (dentp = (struct direntry *)bp->b_data;
1061                          (char *)dentp < (char *)bp->b_data + blsize;
1062                          dentp++) {
1063                               if (dentp->deName[0] == SLOT_EMPTY) {
1064                                         /*
1065                                          * Last used entry and not found
1066                                          */
1067                                         brelse(bp, 0);
1068                                         return win95;
1069                               }
1070                               if (dentp->deName[0] == SLOT_DELETED) {
1071                                         /*
1072                                          * Ignore deleted files
1073                                          * Note: might be an indication of Win'95
1074                                          * anyway XXX
1075                                          */
1076                                         continue;
1077                               }
1078                               if (dentp->deAttributes == ATTR_WIN95) {
1079                                         brelse(bp, 0);
1080                                         return 1;
1081                               }
1082                               win95 = 0;
1083                     }
1084                     brelse(bp, 0);
1085           }
1086 }
1087