1 /* $NetBSD: msdosfs_denode.c,v 1.51 2015/03/28 19:24:05 maxv Exp $ */
4 * Copyright (C) 1994, 1995, 1997 Wolfgang Solfrank.
5 * Copyright (C) 1994, 1995, 1997 TooLs GmbH.
7 * Original code by Paul Popelka (paulp@uts.amdahl.com) (see below).
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
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.
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.
35 * Written by Paul Popelka (paulp@uts.amdahl.com)
37 * You can do anything you want with this software, just don't say you wrote
38 * it, and don't remove this notice.
40 * This software is provided "as is".
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.
50 #include <sys/cdefs.h>
51 __KERNEL_RCSID(0, "$NetBSD: msdosfs_denode.c,v 1.51 2015/03/28 19:24:05 maxv Exp $");
53 #include <sys/param.h>
54 #include <sys/systm.h>
55 #include <sys/mount.h>
56 #include <sys/fstrans.h>
57 #include <sys/malloc.h>
61 #include <sys/vnode.h>
62 #include <sys/kernel.h> /* defines "time" */
63 #include <sys/dirent.h>
64 #include <sys/namei.h>
65 #include <sys/kauth.h>
67 #include <uvm/uvm_extern.h>
69 #include <fs/msdosfs/bpb.h>
70 #include <fs/msdosfs/msdosfsmount.h>
71 #include <fs/msdosfs/direntry.h>
72 #include <fs/msdosfs/denode.h>
73 #include <fs/msdosfs/fat.h>
75 struct pool msdosfs_denode_pool
;
80 struct msdosfsmount
*fhk_mount
;
81 uint32_t fhk_dircluster
;
82 uint32_t fhk_diroffset
;
85 struct rb_node fh_rbnode
;
87 #define fh_mount fh_key.fhk_mount
88 #define fh_dircluster fh_key.fhk_dircluster
89 #define fh_diroffset fh_key.fhk_diroffset
94 fh_compare_node_fh(void *ctx
, const void *b
, const void *key
)
96 const struct fh_node
* const pnp
= b
;
97 const struct fh_key
* const fhp
= key
;
99 /* msdosfs_fh_destroy() below depends on first sorting on fh_mount. */
100 if (pnp
->fh_mount
!= fhp
->fhk_mount
)
101 return (intptr_t)pnp
->fh_mount
- (intptr_t)fhp
->fhk_mount
;
102 if (pnp
->fh_dircluster
!= fhp
->fhk_dircluster
)
103 return pnp
->fh_dircluster
- fhp
->fhk_dircluster
;
104 return pnp
->fh_diroffset
- fhp
->fhk_diroffset
;
108 fh_compare_nodes(void *ctx
, const void *parent
, const void *node
)
110 const struct fh_node
* const np
= node
;
112 return fh_compare_node_fh(ctx
, parent
, &np
->fh_key
);
115 static uint32_t fh_generation
;
116 static kmutex_t fh_lock
;
117 static struct pool fh_pool
;
118 static rb_tree_t fh_rbtree
;
119 static const rb_tree_ops_t fh_rbtree_ops
= {
120 .rbto_compare_nodes
= fh_compare_nodes
,
121 .rbto_compare_key
= fh_compare_node_fh
,
122 .rbto_node_offset
= offsetof(struct fh_node
, fh_rbnode
),
126 static const struct genfs_ops msdosfs_genfsops
= {
127 .gop_size
= genfs_size
,
128 .gop_alloc
= msdosfs_gop_alloc
,
129 .gop_write
= genfs_gop_write
,
130 .gop_markupdate
= msdosfs_gop_markupdate
,
133 MALLOC_DECLARE(M_MSDOSFSFAT
);
139 malloc_type_attach(M_MSDOSFSMNT
);
140 malloc_type_attach(M_MSDOSFSFAT
);
141 malloc_type_attach(M_MSDOSFSTMP
);
142 pool_init(&msdosfs_denode_pool
, sizeof(struct denode
), 0, 0, 0,
143 "msdosnopl", &pool_allocator_nointr
, IPL_NONE
);
144 pool_init(&fh_pool
, sizeof(struct fh_node
), 0, 0, 0,
145 "msdosfhpl", &pool_allocator_nointr
, IPL_NONE
);
146 rb_tree_init(&fh_rbtree
, &fh_rbtree_ops
);
147 mutex_init(&fh_lock
, MUTEX_DEFAULT
, IPL_NONE
);
163 pool_destroy(&msdosfs_denode_pool
);
164 pool_destroy(&fh_pool
);
165 mutex_destroy(&fh_lock
);
166 malloc_type_detach(M_MSDOSFSTMP
);
167 malloc_type_detach(M_MSDOSFSFAT
);
168 malloc_type_detach(M_MSDOSFSMNT
);
172 * If deget() succeeds it returns with the gotten denode unlocked.
174 * pmp - address of msdosfsmount structure of the filesystem containing
175 * the denode of interest. The pm_dev field and the address of
176 * the msdosfsmount structure are used.
177 * dirclust - which cluster bp contains, if dirclust is 0 (root directory)
178 * diroffset is relative to the beginning of the root directory,
179 * otherwise it is cluster relative.
180 * diroffset - offset past begin of cluster of denode we want
181 * vpp - returns the address of the gotten vnode.
184 deget(struct msdosfsmount
*pmp
, u_long dirclust
, u_long diroffset
,
186 /* pmp: so we know the maj/min number */
187 /* dirclust: cluster this dir entry came from */
188 /* diroffset: index of entry within the cluster */
189 /* vpp: returns the addr of the gotten vnode */
192 struct denode_key key
;
195 * On FAT32 filesystems, root is a (more or less) normal
198 if (FAT32(pmp
) && dirclust
== MSDOSFSROOT
)
199 dirclust
= pmp
->pm_rootdirblk
;
201 memset(&key
, 0, sizeof(key
));
202 key
.dk_dirclust
= dirclust
;
203 key
.dk_diroffset
= diroffset
;
204 /* key.dk_dirgen = NULL; */
206 error
= vcache_get(pmp
->pm_mountp
, &key
, sizeof(key
), vpp
);
211 msdosfs_loadvnode(struct mount
*mp
, struct vnode
*vp
,
212 const void *key
, size_t key_len
, const void **new_key
)
216 extern int (**msdosfs_vnodeop_p
)(void *);
217 struct msdosfsmount
*pmp
;
218 struct direntry
*direntptr
;
221 struct denode_key dkey
;
223 KASSERT(key_len
== sizeof(dkey
));
224 memcpy(&dkey
, key
, key_len
);
225 KASSERT(dkey
.dk_dirgen
== NULL
);
227 pmp
= VFSTOMSDOSFS(mp
);
228 is_root
= ((dkey
.dk_dirclust
== MSDOSFSROOT
||
229 (FAT32(pmp
) && dkey
.dk_dirclust
== pmp
->pm_rootdirblk
)) &&
230 dkey
.dk_diroffset
== MSDOSFSROOT_OFS
);
233 printf("loadvnode(pmp %p, dirclust %lu, diroffset %lx, vp %p)\n",
234 pmp
, dkey
.dk_dirclust
, dkey
.dk_diroffset
, vp
);
237 ldep
= pool_get(&msdosfs_denode_pool
, PR_WAITOK
);
238 memset(ldep
, 0, sizeof *ldep
);
239 /* ldep->de_flag = 0; */
240 /* ldep->de_devvp = 0; */
241 /* ldep->de_lockf = 0; */
242 ldep
->de_dev
= pmp
->pm_dev
;
243 ldep
->de_dirclust
= dkey
.dk_dirclust
;
244 ldep
->de_diroffset
= dkey
.dk_diroffset
;
246 ldep
->de_devvp
= pmp
->pm_devvp
;
248 fc_purge(ldep
, 0); /* init the FAT cache for this denode */
251 * Copy the directory entry into the denode area of the vnode.
255 * Directory entry for the root directory. There isn't one,
256 * so we manufacture one. We should probably rummage
257 * through the root directory and find a label entry (if it
258 * exists), and then use the time and date from that entry
259 * as the time and date for the root denode.
261 ldep
->de_Attributes
= ATTR_DIRECTORY
;
263 ldep
->de_StartCluster
= pmp
->pm_rootdirblk
;
264 /* de_FileSize will be filled in further down */
266 ldep
->de_StartCluster
= MSDOSFSROOT
;
267 ldep
->de_FileSize
= pmp
->pm_rootdirsize
*
271 * fill in time and date so that dos2unixtime() doesn't
272 * spit up when called from msdosfs_getattr() with root
276 ldep
->de_CTime
= 0x0000; /* 00:00:00 */
277 ldep
->de_CDate
= (0 << DD_YEAR_SHIFT
) | (1 << DD_MONTH_SHIFT
)
278 | (1 << DD_DAY_SHIFT
);
280 ldep
->de_ADate
= ldep
->de_CDate
;
281 ldep
->de_MTime
= ldep
->de_CTime
;
282 ldep
->de_MDate
= ldep
->de_CDate
;
283 /* leave the other fields as garbage */
285 error
= readep(pmp
, ldep
->de_dirclust
, ldep
->de_diroffset
,
288 pool_put(&msdosfs_denode_pool
, ldep
);
291 DE_INTERNALIZE(ldep
, direntptr
);
296 * Fill in a few fields of the vnode and finish filling in the
299 if (ldep
->de_Attributes
& ATTR_DIRECTORY
) {
301 * Since DOS directory entries that describe directories
302 * have 0 in the filesize field, we take this opportunity
303 * to find out the length of the directory and plug it into
304 * the denode structure.
309 if (ldep
->de_StartCluster
!= MSDOSFSROOT
) {
310 error
= pcbmap(ldep
, CLUST_END
, 0, &size
, 0);
311 if (error
== E2BIG
) {
312 ldep
->de_FileSize
= de_cn2off(pmp
, size
);
315 printf("loadvnode(): pcbmap returned %d\n",
320 vref(ldep
->de_devvp
);
322 vp
->v_vflag
|= VV_ROOT
;
323 vp
->v_tag
= VT_MSDOSFS
;
324 vp
->v_op
= msdosfs_vnodeop_p
;
327 genfs_node_init(vp
, &msdosfs_genfsops
);
328 uvm_vnp_setsize(vp
, ldep
->de_FileSize
);
329 *new_key
= &ldep
->de_key
;
335 deupdat(struct denode
*dep
, int waitfor
)
338 return (msdosfs_update(DETOV(dep
), NULL
, NULL
,
339 waitfor
? UPDATE_WAIT
: 0));
343 * Truncate the file described by dep to the length specified by length.
346 detrunc(struct denode
*dep
, u_long length
, int flags
, kauth_cred_t cred
)
351 u_long chaintofree
= 0;
352 daddr_t bn
, lastblock
;
354 int isadir
= dep
->de_Attributes
& ATTR_DIRECTORY
;
356 struct msdosfsmount
*pmp
= dep
->de_pmp
;
359 printf("detrunc(): file %s, length %lu, flags %x\n", dep
->de_Name
, length
, flags
);
363 * Disallow attempts to truncate the root directory since it is of
364 * fixed size. That's just the way dos filesystems are. We use
365 * the VROOT bit in the vnode because checking for the directory
366 * bit and a startcluster of 0 in the denode is not adequate to
367 * recognize the root directory at this point in a file or
370 if ((DETOV(dep
)->v_vflag
& VV_ROOT
) && !FAT32(pmp
)) {
371 printf("detrunc(): can't truncate root directory, clust %ld, offset %ld\n",
372 dep
->de_dirclust
, dep
->de_diroffset
);
376 uvm_vnp_setsize(DETOV(dep
), length
);
378 if (dep
->de_FileSize
< length
)
379 return (deextend(dep
, length
, cred
));
380 lastblock
= de_clcount(pmp
, length
) - 1;
383 * If the desired length is 0 then remember the starting cluster of
384 * the file and set the StartCluster field in the directory entry
385 * to 0. If the desired length is not zero, then get the number of
386 * the last cluster in the shortened file. Then get the number of
387 * the first cluster in the part of the file that is to be freed.
388 * Then set the next cluster pointer in the last cluster of the
389 * file to CLUST_EOFE.
392 chaintofree
= dep
->de_StartCluster
;
393 dep
->de_StartCluster
= 0;
396 error
= pcbmap(dep
, lastblock
, 0, &eofentry
, 0);
399 printf("detrunc(): pcbmap fails %d\n", error
);
406 * If the new length is not a multiple of the cluster size then we
407 * must zero the tail end of the new last cluster in case it
408 * becomes part of the file again because of a seek.
410 if ((boff
= length
& pmp
->pm_crbomask
) != 0) {
412 bn
= cntobn(pmp
, eofentry
);
413 error
= bread(pmp
->pm_devvp
, de_bn2kb(pmp
, bn
),
414 pmp
->pm_bpcluster
, B_MODIFY
, &bp
);
417 printf("detrunc(): bread fails %d\n", error
);
421 memset((char *)bp
->b_data
+ boff
, 0,
422 pmp
->pm_bpcluster
- boff
);
428 ubc_zerorange(&DETOV(dep
)->v_uobj
, length
,
429 pmp
->pm_bpcluster
- boff
,
430 UBC_UNMAP_FLAG(DETOV(dep
)));
435 * Write out the updated directory entry. Even if the update fails
436 * we free the trailing clusters.
438 dep
->de_FileSize
= length
;
440 dep
->de_flag
|= DE_UPDATE
|DE_MODIFIED
;
441 vtruncbuf(DETOV(dep
), lastblock
+ 1, 0, 0);
442 allerror
= deupdat(dep
, 1);
444 printf("detrunc(): allerror %d, eofentry %lu\n",
448 fc_purge(dep
, lastblock
+ 1);
451 * If we need to break the cluster chain for the file then do it
454 if (eofentry
!= ~0) {
455 error
= fatentry(FAT_GET_AND_SET
, pmp
, eofentry
,
456 &chaintofree
, CLUST_EOFE
);
459 printf("detrunc(): fatentry errors %d\n", error
);
463 fc_setcache(dep
, FC_LASTFC
, de_cluster(pmp
, length
- 1),
468 * Now free the clusters removed from the file because of the
471 if (chaintofree
!= 0 && !MSDOSFSEOF(chaintofree
, pmp
->pm_fatmask
))
472 freeclusterchain(pmp
, chaintofree
);
478 * Extend the file described by dep to length specified by length.
481 deextend(struct denode
*dep
, u_long length
, kauth_cred_t cred
)
483 struct msdosfsmount
*pmp
= dep
->de_pmp
;
488 * The root of a DOS filesystem cannot be extended.
490 if ((DETOV(dep
)->v_vflag
& VV_ROOT
) && !FAT32(pmp
))
494 * Directories cannot be extended.
496 if (dep
->de_Attributes
& ATTR_DIRECTORY
)
499 if (length
<= dep
->de_FileSize
)
500 panic("deextend: file too large");
503 * Compute the number of clusters to allocate.
505 count
= de_clcount(pmp
, length
) - de_clcount(pmp
, dep
->de_FileSize
);
507 if (count
> pmp
->pm_freeclustercount
)
509 error
= extendfile(dep
, count
, NULL
, NULL
, DE_CLEAR
);
511 /* truncate the added clusters away again */
512 (void) detrunc(dep
, dep
->de_FileSize
, 0, cred
);
518 * Zero extend file range; ubc_zerorange() uses ubc_alloc() and a
519 * memset(); we set the write size so ubc won't read in file data that
522 osize
= dep
->de_FileSize
;
523 dep
->de_FileSize
= length
;
524 uvm_vnp_setwritesize(DETOV(dep
), (voff_t
)dep
->de_FileSize
);
525 dep
->de_flag
|= DE_UPDATE
|DE_MODIFIED
;
526 ubc_zerorange(&DETOV(dep
)->v_uobj
, (off_t
)osize
,
527 (size_t)(round_page(dep
->de_FileSize
) - osize
),
528 UBC_UNMAP_FLAG(DETOV(dep
)));
529 uvm_vnp_setsize(DETOV(dep
), (voff_t
)dep
->de_FileSize
);
530 return (deupdat(dep
, 1));
534 msdosfs_reclaim(void *v
)
536 struct vop_reclaim_args
/* {
539 struct vnode
*vp
= ap
->a_vp
;
540 struct mount
*mp
= vp
->v_mount
;
541 struct denode
*dep
= VTODE(vp
);
543 fstrans_start(mp
, FSTRANS_LAZY
);
545 printf("msdosfs_reclaim(): dep %p, file %s, refcnt %ld\n",
546 dep
, dep
->de_Name
, dep
->de_refcnt
);
549 if (prtactive
&& vp
->v_usecount
> 1)
550 vprint("msdosfs_reclaim(): pushing active", vp
);
552 * Remove the denode from the vnode cache.
554 vcache_remove(vp
->v_mount
, &dep
->de_key
, sizeof(dep
->de_key
));
556 * Purge old data structures associated with the denode.
559 vrele(dep
->de_devvp
);
566 * To interlock with msdosfs_sync().
568 genfs_node_destroy(vp
);
569 mutex_enter(vp
->v_interlock
);
571 mutex_exit(vp
->v_interlock
);
572 pool_put(&msdosfs_denode_pool
, dep
);
578 msdosfs_inactive(void *v
)
580 struct vop_inactive_args
/* {
584 struct vnode
*vp
= ap
->a_vp
;
585 struct mount
*mp
= vp
->v_mount
;
586 struct denode
*dep
= VTODE(vp
);
590 printf("msdosfs_inactive(): dep %p, de_Name[0] %x\n", dep
, dep
->de_Name
[0]);
593 fstrans_start(mp
, FSTRANS_LAZY
);
595 * Get rid of denodes related to stale file handles.
597 if (dep
->de_Name
[0] == SLOT_DELETED
)
601 * If the file has been deleted and it is on a read/write
602 * filesystem, then truncate the file, and mark the directory slot
603 * as empty. (This may not be necessary for the dos filesystem.)
606 printf("msdosfs_inactive(): dep %p, refcnt %ld, mntflag %x %s\n",
607 dep
, dep
->de_refcnt
, vp
->v_mount
->mnt_flag
,
608 (vp
->v_mount
->mnt_flag
& MNT_RDONLY
) ? "MNT_RDONLY" : "");
610 if (dep
->de_refcnt
<= 0 && (vp
->v_mount
->mnt_flag
& MNT_RDONLY
) == 0) {
611 if (dep
->de_FileSize
!= 0) {
612 error
= detrunc(dep
, (u_long
)0, 0, NOCRED
);
614 dep
->de_Name
[0] = SLOT_DELETED
;
615 msdosfs_fh_remove(dep
->de_pmp
,
616 dep
->de_dirclust
, dep
->de_diroffset
);
621 * If we are done with the denode, reclaim it
622 * so that it can be reused immediately.
625 printf("msdosfs_inactive(): v_usecount %d, de_Name[0] %x\n",
626 vp
->v_usecount
, dep
->de_Name
[0]);
628 *ap
->a_recycle
= (dep
->de_Name
[0] == SLOT_DELETED
);
635 msdosfs_gop_alloc(struct vnode
*vp
, off_t off
,
636 off_t len
, int flags
, kauth_cred_t cred
)
642 msdosfs_gop_markupdate(struct vnode
*vp
, int flags
)
646 if ((flags
& GOP_UPDATE_ACCESSED
) != 0) {
649 if ((flags
& GOP_UPDATE_MODIFIED
) != 0) {
653 struct denode
*dep
= VTODE(vp
);
655 dep
->de_flag
|= mask
;
660 msdosfs_fh_enter(struct msdosfsmount
*pmp
,
661 uint32_t dircluster
, uint32_t diroffset
, uint32_t *genp
)
666 fhkey
.fhk_mount
= pmp
;
667 fhkey
.fhk_dircluster
= dircluster
;
668 fhkey
.fhk_diroffset
= diroffset
;
670 mutex_enter(&fh_lock
);
671 fhp
= rb_tree_find_node(&fh_rbtree
, &fhkey
);
673 mutex_exit(&fh_lock
);
674 fhp
= pool_get(&fh_pool
, PR_WAITOK
);
675 mutex_enter(&fh_lock
);
677 fhp
->fh_gen
= fh_generation
++;
678 rb_tree_insert_node(&fh_rbtree
, fhp
);
681 mutex_exit(&fh_lock
);
686 msdosfs_fh_remove(struct msdosfsmount
*pmp
,
687 uint32_t dircluster
, uint32_t diroffset
)
692 fhkey
.fhk_mount
= pmp
;
693 fhkey
.fhk_dircluster
= dircluster
;
694 fhkey
.fhk_diroffset
= diroffset
;
696 mutex_enter(&fh_lock
);
697 fhp
= rb_tree_find_node(&fh_rbtree
, &fhkey
);
699 mutex_exit(&fh_lock
);
702 rb_tree_remove_node(&fh_rbtree
, fhp
);
703 mutex_exit(&fh_lock
);
704 pool_put(&fh_pool
, fhp
);
709 msdosfs_fh_lookup(struct msdosfsmount
*pmp
,
710 uint32_t dircluster
, uint32_t diroffset
, uint32_t *genp
)
715 fhkey
.fhk_mount
= pmp
;
716 fhkey
.fhk_dircluster
= dircluster
;
717 fhkey
.fhk_diroffset
= diroffset
;
719 mutex_enter(&fh_lock
);
720 fhp
= rb_tree_find_node(&fh_rbtree
, &fhkey
);
722 mutex_exit(&fh_lock
);
726 mutex_exit(&fh_lock
);
731 msdosfs_fh_destroy(struct msdosfsmount
*pmp
)
734 struct fh_node
*fhp
, *nfhp
;
736 fhkey
.fhk_mount
= pmp
;
737 fhkey
.fhk_dircluster
= 0;
738 fhkey
.fhk_diroffset
= 0;
740 mutex_enter(&fh_lock
);
741 for (fhp
= rb_tree_find_node_geq(&fh_rbtree
, &fhkey
);
742 fhp
!= NULL
&& fhp
->fh_mount
== pmp
; fhp
= nfhp
) {
743 nfhp
= rb_tree_iterate(&fh_rbtree
, fhp
, RB_DIR_RIGHT
);
744 rb_tree_remove_node(&fh_rbtree
, fhp
);
745 pool_put(&fh_pool
, fhp
);
748 RB_TREE_FOREACH(fhp
, &fh_rbtree
) {
749 KASSERT(fhp
->fh_mount
!= pmp
);
752 mutex_exit(&fh_lock
);