1 | /* $NetBSD: msdosfs_denode.c,v 1.52 2016/08/20 12:37:07 hannken 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 | #include <sys/cdefs.h> |
51 | __KERNEL_RCSID(0, "$NetBSD: msdosfs_denode.c,v 1.52 2016/08/20 12:37:07 hannken Exp $" ); |
52 | |
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> |
58 | #include <sys/pool.h> |
59 | #include <sys/proc.h> |
60 | #include <sys/buf.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> |
66 | |
67 | #include <uvm/uvm_extern.h> |
68 | |
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> |
74 | |
75 | struct pool msdosfs_denode_pool; |
76 | |
77 | extern int prtactive; |
78 | |
79 | struct fh_key { |
80 | struct msdosfsmount *fhk_mount; |
81 | uint32_t fhk_dircluster; |
82 | uint32_t fhk_diroffset; |
83 | }; |
84 | struct fh_node { |
85 | struct rb_node fh_rbnode; |
86 | struct fh_key fh_key; |
87 | #define fh_mount fh_key.fhk_mount |
88 | #define fh_dircluster fh_key.fhk_dircluster |
89 | #define fh_diroffset fh_key.fhk_diroffset |
90 | uint32_t fh_gen; |
91 | }; |
92 | |
93 | static int |
94 | fh_compare_node_fh(void *ctx, const void *b, const void *key) |
95 | { |
96 | const struct fh_node * const pnp = b; |
97 | const struct fh_key * const fhp = key; |
98 | |
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; |
105 | } |
106 | |
107 | static int |
108 | fh_compare_nodes(void *ctx, const void *parent, const void *node) |
109 | { |
110 | const struct fh_node * const np = node; |
111 | |
112 | return fh_compare_node_fh(ctx, parent, &np->fh_key); |
113 | } |
114 | |
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), |
123 | .rbto_context = NULL |
124 | }; |
125 | |
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, |
131 | }; |
132 | |
133 | MALLOC_DECLARE(M_MSDOSFSFAT); |
134 | |
135 | void |
136 | msdosfs_init(void) |
137 | { |
138 | |
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); |
148 | } |
149 | |
150 | /* |
151 | * Reinitialize. |
152 | */ |
153 | |
154 | void |
155 | msdosfs_reinit(void) |
156 | { |
157 | |
158 | } |
159 | |
160 | void |
161 | msdosfs_done(void) |
162 | { |
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); |
169 | } |
170 | |
171 | /* |
172 | * If deget() succeeds it returns with the gotten denode unlocked. |
173 | * |
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. |
182 | */ |
183 | int |
184 | deget(struct msdosfsmount *pmp, u_long dirclust, u_long diroffset, |
185 | struct vnode **vpp) |
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 */ |
190 | { |
191 | int error; |
192 | struct denode_key key; |
193 | |
194 | /* |
195 | * On FAT32 filesystems, root is a (more or less) normal |
196 | * directory |
197 | */ |
198 | if (FAT32(pmp) && dirclust == MSDOSFSROOT) |
199 | dirclust = pmp->pm_rootdirblk; |
200 | |
201 | memset(&key, 0, sizeof(key)); |
202 | key.dk_dirclust = dirclust; |
203 | key.dk_diroffset = diroffset; |
204 | /* key.dk_dirgen = NULL; */ |
205 | |
206 | error = vcache_get(pmp->pm_mountp, &key, sizeof(key), vpp); |
207 | return error; |
208 | } |
209 | |
210 | int |
211 | msdosfs_loadvnode(struct mount *mp, struct vnode *vp, |
212 | const void *key, size_t key_len, const void **new_key) |
213 | { |
214 | bool is_root; |
215 | int error; |
216 | extern int (**msdosfs_vnodeop_p)(void *); |
217 | struct msdosfsmount *pmp; |
218 | struct direntry *direntptr; |
219 | struct denode *ldep; |
220 | struct buf *bp; |
221 | struct denode_key dkey; |
222 | |
223 | KASSERT(key_len == sizeof(dkey)); |
224 | memcpy(&dkey, key, key_len); |
225 | KASSERT(dkey.dk_dirgen == NULL); |
226 | |
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); |
231 | |
232 | #ifdef MSDOSFS_DEBUG |
233 | printf("loadvnode(pmp %p, dirclust %lu, diroffset %lx, vp %p)\n" , |
234 | pmp, dkey.dk_dirclust, dkey.dk_diroffset, vp); |
235 | #endif |
236 | |
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; |
245 | ldep->de_pmp = pmp; |
246 | ldep->de_devvp = pmp->pm_devvp; |
247 | ldep->de_refcnt = 1; |
248 | fc_purge(ldep, 0); /* init the FAT cache for this denode */ |
249 | |
250 | /* |
251 | * Copy the directory entry into the denode area of the vnode. |
252 | */ |
253 | if (is_root) { |
254 | /* |
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. |
260 | */ |
261 | ldep->de_Attributes = ATTR_DIRECTORY; |
262 | if (FAT32(pmp)) |
263 | ldep->de_StartCluster = pmp->pm_rootdirblk; |
264 | /* de_FileSize will be filled in further down */ |
265 | else { |
266 | ldep->de_StartCluster = MSDOSFSROOT; |
267 | ldep->de_FileSize = pmp->pm_rootdirsize * |
268 | pmp->pm_BytesPerSec; |
269 | } |
270 | /* |
271 | * fill in time and date so that dos2unixtime() doesn't |
272 | * spit up when called from msdosfs_getattr() with root |
273 | * denode |
274 | */ |
275 | ldep->de_CHun = 0; |
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); |
279 | /* Jan 1, 1980 */ |
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 */ |
284 | } else { |
285 | error = readep(pmp, ldep->de_dirclust, ldep->de_diroffset, |
286 | &bp, &direntptr); |
287 | if (error) { |
288 | pool_put(&msdosfs_denode_pool, ldep); |
289 | return error; |
290 | } |
291 | DE_INTERNALIZE(ldep, direntptr); |
292 | brelse(bp, 0); |
293 | } |
294 | |
295 | /* |
296 | * Fill in a few fields of the vnode and finish filling in the |
297 | * denode. |
298 | */ |
299 | if (ldep->de_Attributes & ATTR_DIRECTORY) { |
300 | /* |
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. |
305 | */ |
306 | u_long size; |
307 | |
308 | vp->v_type = VDIR; |
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); |
313 | error = 0; |
314 | } else |
315 | printf("loadvnode(): pcbmap returned %d\n" , |
316 | error); |
317 | } |
318 | } else |
319 | vp->v_type = VREG; |
320 | vref(ldep->de_devvp); |
321 | if (is_root) |
322 | vp->v_vflag |= VV_ROOT; |
323 | vp->v_tag = VT_MSDOSFS; |
324 | vp->v_op = msdosfs_vnodeop_p; |
325 | vp->v_data = ldep; |
326 | ldep->de_vnode = vp; |
327 | genfs_node_init(vp, &msdosfs_genfsops); |
328 | uvm_vnp_setsize(vp, ldep->de_FileSize); |
329 | *new_key = &ldep->de_key; |
330 | |
331 | return 0; |
332 | } |
333 | |
334 | int |
335 | deupdat(struct denode *dep, int waitfor) |
336 | { |
337 | |
338 | return (msdosfs_update(DETOV(dep), NULL, NULL, |
339 | waitfor ? UPDATE_WAIT : 0)); |
340 | } |
341 | |
342 | /* |
343 | * Truncate the file described by dep to the length specified by length. |
344 | */ |
345 | int |
346 | detrunc(struct denode *dep, u_long length, int flags, kauth_cred_t cred) |
347 | { |
348 | int error; |
349 | int allerror; |
350 | u_long eofentry; |
351 | u_long chaintofree = 0; |
352 | daddr_t bn, lastblock; |
353 | int boff; |
354 | int isadir = dep->de_Attributes & ATTR_DIRECTORY; |
355 | struct buf *bp; |
356 | struct msdosfsmount *pmp = dep->de_pmp; |
357 | |
358 | #ifdef MSDOSFS_DEBUG |
359 | printf("detrunc(): file %s, length %lu, flags %x\n" , dep->de_Name, length, flags); |
360 | #endif |
361 | |
362 | /* |
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 |
368 | * directory's life. |
369 | */ |
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); |
373 | return (EINVAL); |
374 | } |
375 | |
376 | uvm_vnp_setsize(DETOV(dep), length); |
377 | |
378 | if (dep->de_FileSize < length) |
379 | return (deextend(dep, length, cred)); |
380 | lastblock = de_clcount(pmp, length) - 1; |
381 | |
382 | /* |
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. |
390 | */ |
391 | if (length == 0) { |
392 | chaintofree = dep->de_StartCluster; |
393 | dep->de_StartCluster = 0; |
394 | eofentry = ~0; |
395 | } else { |
396 | error = pcbmap(dep, lastblock, 0, &eofentry, 0); |
397 | if (error) { |
398 | #ifdef MSDOSFS_DEBUG |
399 | printf("detrunc(): pcbmap fails %d\n" , error); |
400 | #endif |
401 | return (error); |
402 | } |
403 | } |
404 | |
405 | /* |
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. |
409 | */ |
410 | if ((boff = length & pmp->pm_crbomask) != 0) { |
411 | if (isadir) { |
412 | bn = cntobn(pmp, eofentry); |
413 | error = bread(pmp->pm_devvp, de_bn2kb(pmp, bn), |
414 | pmp->pm_bpcluster, B_MODIFY, &bp); |
415 | if (error) { |
416 | #ifdef MSDOSFS_DEBUG |
417 | printf("detrunc(): bread fails %d\n" , error); |
418 | #endif |
419 | return (error); |
420 | } |
421 | memset((char *)bp->b_data + boff, 0, |
422 | pmp->pm_bpcluster - boff); |
423 | if (flags & IO_SYNC) |
424 | bwrite(bp); |
425 | else |
426 | bdwrite(bp); |
427 | } else { |
428 | ubc_zerorange(&DETOV(dep)->v_uobj, length, |
429 | pmp->pm_bpcluster - boff, |
430 | UBC_UNMAP_FLAG(DETOV(dep))); |
431 | } |
432 | } |
433 | |
434 | /* |
435 | * Write out the updated directory entry. Even if the update fails |
436 | * we free the trailing clusters. |
437 | */ |
438 | dep->de_FileSize = length; |
439 | if (!isadir) |
440 | dep->de_flag |= DE_UPDATE|DE_MODIFIED; |
441 | vtruncbuf(DETOV(dep), lastblock + 1, 0, 0); |
442 | allerror = deupdat(dep, 1); |
443 | #ifdef MSDOSFS_DEBUG |
444 | printf("detrunc(): allerror %d, eofentry %lu\n" , |
445 | allerror, eofentry); |
446 | #endif |
447 | |
448 | fc_purge(dep, lastblock + 1); |
449 | |
450 | /* |
451 | * If we need to break the cluster chain for the file then do it |
452 | * now. |
453 | */ |
454 | if (eofentry != ~0) { |
455 | error = fatentry(FAT_GET_AND_SET, pmp, eofentry, |
456 | &chaintofree, CLUST_EOFE); |
457 | if (error) { |
458 | #ifdef MSDOSFS_DEBUG |
459 | printf("detrunc(): fatentry errors %d\n" , error); |
460 | #endif |
461 | return (error); |
462 | } |
463 | fc_setcache(dep, FC_LASTFC, de_cluster(pmp, length - 1), |
464 | eofentry); |
465 | } |
466 | |
467 | /* |
468 | * Now free the clusters removed from the file because of the |
469 | * truncation. |
470 | */ |
471 | if (chaintofree != 0 && !MSDOSFSEOF(chaintofree, pmp->pm_fatmask)) |
472 | freeclusterchain(pmp, chaintofree); |
473 | |
474 | return (allerror); |
475 | } |
476 | |
477 | /* |
478 | * Extend the file described by dep to length specified by length. |
479 | */ |
480 | int |
481 | deextend(struct denode *dep, u_long length, kauth_cred_t cred) |
482 | { |
483 | struct msdosfsmount *pmp = dep->de_pmp; |
484 | u_long count, osize; |
485 | int error; |
486 | |
487 | /* |
488 | * The root of a DOS filesystem cannot be extended. |
489 | */ |
490 | if ((DETOV(dep)->v_vflag & VV_ROOT) && !FAT32(pmp)) |
491 | return (EINVAL); |
492 | |
493 | /* |
494 | * Directories cannot be extended. |
495 | */ |
496 | if (dep->de_Attributes & ATTR_DIRECTORY) |
497 | return (EISDIR); |
498 | |
499 | if (length <= dep->de_FileSize) |
500 | panic("deextend: file too large" ); |
501 | |
502 | /* |
503 | * Compute the number of clusters to allocate. |
504 | */ |
505 | count = de_clcount(pmp, length) - de_clcount(pmp, dep->de_FileSize); |
506 | if (count > 0) { |
507 | if (count > pmp->pm_freeclustercount) |
508 | return (ENOSPC); |
509 | error = extendfile(dep, count, NULL, NULL, DE_CLEAR); |
510 | if (error) { |
511 | /* truncate the added clusters away again */ |
512 | (void) detrunc(dep, dep->de_FileSize, 0, cred); |
513 | return (error); |
514 | } |
515 | } |
516 | |
517 | /* |
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 |
520 | * is zero'd later. |
521 | */ |
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)); |
531 | } |
532 | |
533 | int |
534 | msdosfs_reclaim(void *v) |
535 | { |
536 | struct vop_reclaim_args /* { |
537 | struct vnode *a_vp; |
538 | } */ *ap = v; |
539 | struct vnode *vp = ap->a_vp; |
540 | struct mount *mp = vp->v_mount; |
541 | struct denode *dep = VTODE(vp); |
542 | |
543 | fstrans_start(mp, FSTRANS_LAZY); |
544 | #ifdef MSDOSFS_DEBUG |
545 | printf("msdosfs_reclaim(): dep %p, file %s, refcnt %ld\n" , |
546 | dep, dep->de_Name, dep->de_refcnt); |
547 | #endif |
548 | |
549 | if (prtactive && vp->v_usecount > 1) |
550 | vprint("msdosfs_reclaim(): pushing active" , vp); |
551 | /* |
552 | * Purge old data structures associated with the denode. |
553 | */ |
554 | if (dep->de_devvp) { |
555 | vrele(dep->de_devvp); |
556 | dep->de_devvp = 0; |
557 | } |
558 | #if 0 /* XXX */ |
559 | dep->de_flag = 0; |
560 | #endif |
561 | /* |
562 | * To interlock with msdosfs_sync(). |
563 | */ |
564 | genfs_node_destroy(vp); |
565 | mutex_enter(vp->v_interlock); |
566 | vp->v_data = NULL; |
567 | mutex_exit(vp->v_interlock); |
568 | pool_put(&msdosfs_denode_pool, dep); |
569 | fstrans_done(mp); |
570 | return (0); |
571 | } |
572 | |
573 | int |
574 | msdosfs_inactive(void *v) |
575 | { |
576 | struct vop_inactive_args /* { |
577 | struct vnode *a_vp; |
578 | bool *a_recycle; |
579 | } */ *ap = v; |
580 | struct vnode *vp = ap->a_vp; |
581 | struct mount *mp = vp->v_mount; |
582 | struct denode *dep = VTODE(vp); |
583 | int error = 0; |
584 | |
585 | #ifdef MSDOSFS_DEBUG |
586 | printf("msdosfs_inactive(): dep %p, de_Name[0] %x\n" , dep, dep->de_Name[0]); |
587 | #endif |
588 | |
589 | fstrans_start(mp, FSTRANS_LAZY); |
590 | /* |
591 | * Get rid of denodes related to stale file handles. |
592 | */ |
593 | if (dep->de_Name[0] == SLOT_DELETED) |
594 | goto out; |
595 | |
596 | /* |
597 | * If the file has been deleted and it is on a read/write |
598 | * filesystem, then truncate the file, and mark the directory slot |
599 | * as empty. (This may not be necessary for the dos filesystem.) |
600 | */ |
601 | #ifdef MSDOSFS_DEBUG |
602 | printf("msdosfs_inactive(): dep %p, refcnt %ld, mntflag %x %s\n" , |
603 | dep, dep->de_refcnt, vp->v_mount->mnt_flag, |
604 | (vp->v_mount->mnt_flag & MNT_RDONLY) ? "MNT_RDONLY" : "" ); |
605 | #endif |
606 | if (dep->de_refcnt <= 0 && (vp->v_mount->mnt_flag & MNT_RDONLY) == 0) { |
607 | if (dep->de_FileSize != 0) { |
608 | error = detrunc(dep, (u_long)0, 0, NOCRED); |
609 | } |
610 | dep->de_Name[0] = SLOT_DELETED; |
611 | msdosfs_fh_remove(dep->de_pmp, |
612 | dep->de_dirclust, dep->de_diroffset); |
613 | } |
614 | deupdat(dep, 0); |
615 | out: |
616 | /* |
617 | * If we are done with the denode, reclaim it |
618 | * so that it can be reused immediately. |
619 | */ |
620 | #ifdef MSDOSFS_DEBUG |
621 | printf("msdosfs_inactive(): v_usecount %d, de_Name[0] %x\n" , |
622 | vp->v_usecount, dep->de_Name[0]); |
623 | #endif |
624 | *ap->a_recycle = (dep->de_Name[0] == SLOT_DELETED); |
625 | VOP_UNLOCK(vp); |
626 | fstrans_done(mp); |
627 | return (error); |
628 | } |
629 | |
630 | int |
631 | msdosfs_gop_alloc(struct vnode *vp, off_t off, |
632 | off_t len, int flags, kauth_cred_t cred) |
633 | { |
634 | return 0; |
635 | } |
636 | |
637 | void |
638 | msdosfs_gop_markupdate(struct vnode *vp, int flags) |
639 | { |
640 | u_long mask = 0; |
641 | |
642 | if ((flags & GOP_UPDATE_ACCESSED) != 0) { |
643 | mask = DE_ACCESS; |
644 | } |
645 | if ((flags & GOP_UPDATE_MODIFIED) != 0) { |
646 | mask |= DE_UPDATE; |
647 | } |
648 | if (mask) { |
649 | struct denode *dep = VTODE(vp); |
650 | |
651 | dep->de_flag |= mask; |
652 | } |
653 | } |
654 | |
655 | int |
656 | msdosfs_fh_enter(struct msdosfsmount *pmp, |
657 | uint32_t dircluster, uint32_t diroffset, uint32_t *genp) |
658 | { |
659 | struct fh_key fhkey; |
660 | struct fh_node *fhp; |
661 | |
662 | fhkey.fhk_mount = pmp; |
663 | fhkey.fhk_dircluster = dircluster; |
664 | fhkey.fhk_diroffset = diroffset; |
665 | |
666 | mutex_enter(&fh_lock); |
667 | fhp = rb_tree_find_node(&fh_rbtree, &fhkey); |
668 | if (fhp == NULL) { |
669 | mutex_exit(&fh_lock); |
670 | fhp = pool_get(&fh_pool, PR_WAITOK); |
671 | mutex_enter(&fh_lock); |
672 | fhp->fh_key = fhkey; |
673 | fhp->fh_gen = fh_generation++; |
674 | rb_tree_insert_node(&fh_rbtree, fhp); |
675 | } |
676 | *genp = fhp->fh_gen; |
677 | mutex_exit(&fh_lock); |
678 | return 0; |
679 | } |
680 | |
681 | int |
682 | msdosfs_fh_remove(struct msdosfsmount *pmp, |
683 | uint32_t dircluster, uint32_t diroffset) |
684 | { |
685 | struct fh_key fhkey; |
686 | struct fh_node *fhp; |
687 | |
688 | fhkey.fhk_mount = pmp; |
689 | fhkey.fhk_dircluster = dircluster; |
690 | fhkey.fhk_diroffset = diroffset; |
691 | |
692 | mutex_enter(&fh_lock); |
693 | fhp = rb_tree_find_node(&fh_rbtree, &fhkey); |
694 | if (fhp == NULL) { |
695 | mutex_exit(&fh_lock); |
696 | return ENOENT; |
697 | } |
698 | rb_tree_remove_node(&fh_rbtree, fhp); |
699 | mutex_exit(&fh_lock); |
700 | pool_put(&fh_pool, fhp); |
701 | return 0; |
702 | } |
703 | |
704 | int |
705 | msdosfs_fh_lookup(struct msdosfsmount *pmp, |
706 | uint32_t dircluster, uint32_t diroffset, uint32_t *genp) |
707 | { |
708 | struct fh_key fhkey; |
709 | struct fh_node *fhp; |
710 | |
711 | fhkey.fhk_mount = pmp; |
712 | fhkey.fhk_dircluster = dircluster; |
713 | fhkey.fhk_diroffset = diroffset; |
714 | |
715 | mutex_enter(&fh_lock); |
716 | fhp = rb_tree_find_node(&fh_rbtree, &fhkey); |
717 | if (fhp == NULL) { |
718 | mutex_exit(&fh_lock); |
719 | return ESTALE; |
720 | } |
721 | *genp = fhp->fh_gen; |
722 | mutex_exit(&fh_lock); |
723 | return 0; |
724 | } |
725 | |
726 | void |
727 | msdosfs_fh_destroy(struct msdosfsmount *pmp) |
728 | { |
729 | struct fh_key fhkey; |
730 | struct fh_node *fhp, *nfhp; |
731 | |
732 | fhkey.fhk_mount = pmp; |
733 | fhkey.fhk_dircluster = 0; |
734 | fhkey.fhk_diroffset = 0; |
735 | |
736 | mutex_enter(&fh_lock); |
737 | for (fhp = rb_tree_find_node_geq(&fh_rbtree, &fhkey); |
738 | fhp != NULL && fhp->fh_mount == pmp; fhp = nfhp) { |
739 | nfhp = rb_tree_iterate(&fh_rbtree, fhp, RB_DIR_RIGHT); |
740 | rb_tree_remove_node(&fh_rbtree, fhp); |
741 | pool_put(&fh_pool, fhp); |
742 | } |
743 | #ifdef DIAGNOSTIC |
744 | RB_TREE_FOREACH(fhp, &fh_rbtree) { |
745 | KASSERT(fhp->fh_mount != pmp); |
746 | } |
747 | #endif |
748 | mutex_exit(&fh_lock); |
749 | } |
750 | |