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