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