]> git.saurik.com Git - apple/xnu.git/blame - bsd/hfs/hfs_cnode.c
xnu-1504.9.37.tar.gz
[apple/xnu.git] / bsd / hfs / hfs_cnode.c
CommitLineData
9bccf70c 1/*
4a3eedf9 2 * Copyright (c) 2002-2008 Apple Inc. All rights reserved.
9bccf70c 3 *
2d21ac55 4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
9bccf70c 5 *
2d21ac55
A
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
8f6c56a5 14 *
2d21ac55
A
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
8f6c56a5
A
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
2d21ac55
A
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
8f6c56a5 25 *
2d21ac55 26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
9bccf70c
A
27 */
28#include <sys/param.h>
29#include <sys/systm.h>
30#include <sys/proc.h>
31#include <sys/vnode.h>
32#include <sys/mount.h>
33#include <sys/kernel.h>
34#include <sys/malloc.h>
91447636 35#include <sys/time.h>
9bccf70c
A
36#include <sys/ubc.h>
37#include <sys/quota.h>
91447636
A
38#include <sys/kdebug.h>
39
40#include <kern/locks.h>
9bccf70c
A
41
42#include <miscfs/specfs/specdev.h>
43#include <miscfs/fifofs/fifo.h>
44
45#include <hfs/hfs.h>
46#include <hfs/hfs_catalog.h>
47#include <hfs/hfs_cnode.h>
48#include <hfs/hfs_quota.h>
49
50extern int prtactive;
51
91447636
A
52extern lck_attr_t * hfs_lock_attr;
53extern lck_grp_t * hfs_mutex_group;
54extern lck_grp_t * hfs_rwlock_group;
55
56static int hfs_filedone(struct vnode *vp, vfs_context_t context);
57
58static void hfs_reclaim_cnode(struct cnode *);
59
91447636
A
60static int hfs_isordered(struct cnode *, struct cnode *);
61
b7266188
A
62inline int hfs_checkdeleted (struct cnode *cp) {
63 return ((cp->c_flag & (C_DELETED | C_NOEXISTS)) ? ENOENT : 0);
64}
65
9bccf70c
A
66
67/*
68 * Last reference to an cnode. If necessary, write or delete it.
69 */
70__private_extern__
71int
91447636 72hfs_vnop_inactive(struct vnop_inactive_args *ap)
9bccf70c
A
73{
74 struct vnode *vp = ap->a_vp;
91447636 75 struct cnode *cp;
9bccf70c 76 struct hfsmount *hfsmp = VTOHFS(vp);
91447636 77 struct proc *p = vfs_context_proc(ap->a_context);
9bccf70c
A
78 int error = 0;
79 int recycle = 0;
80 int forkcount = 0;
81 int truncated = 0;
91447636
A
82 int started_tr = 0;
83 int took_trunc_lock = 0;
55e303ae
A
84 cat_cookie_t cookie;
85 int cat_reserve = 0;
91447636
A
86 int lockflags;
87 enum vtype v_type;
9bccf70c 88
91447636
A
89 v_type = vnode_vtype(vp);
90 cp = VTOC(vp);
9bccf70c 91
3a60a9f5
A
92 if ((hfsmp->hfs_flags & HFS_READ_ONLY) || vnode_issystem(vp) ||
93 (hfsmp->hfs_freezing_proc == p)) {
91447636
A
94 return (0);
95 }
3a60a9f5 96
9bccf70c
A
97 /*
98 * Ignore nodes related to stale file handles.
b0d623f7
A
99 * We are peeking at the cnode flag without the lock, but if C_NOEXISTS
100 * is set, that means the cnode doesn't have any backing store in the
101 * catalog anymore, and is otherwise safe to force a recycle
9bccf70c 102 */
b0d623f7
A
103
104 if (cp->c_flag & C_NOEXISTS) {
91447636
A
105 vnode_recycle(vp);
106 return (0);
107 }
108
2d21ac55 109 if ((v_type == VREG || v_type == VLNK)) {
91447636
A
110 hfs_lock_truncate(cp, TRUE);
111 took_trunc_lock = 1;
112 }
113
2d21ac55
A
114 (void) hfs_lock(cp, HFS_FORCE_LOCK);
115
b0d623f7
A
116 if (cp->c_datafork)
117 ++forkcount;
118 if (cp->c_rsrcfork)
119 ++forkcount;
120
91447636 121 /*
2d21ac55
A
122 * We should lock cnode before checking the flags in the
123 * condition below and should unlock the cnode before calling
124 * ubc_setsize() as cluster code can call other HFS vnops which
125 * will try to acquire the same cnode lock and cause deadlock.
b0d623f7 126 * Only call ubc_setsize to 0 if we are the last fork.
91447636
A
127 */
128 if ((v_type == VREG || v_type == VLNK) &&
b0d623f7
A
129 (cp->c_flag & C_DELETED) &&
130 (VTOF(vp)->ff_blocks != 0) && (forkcount == 1)) {
131 hfs_unlock(cp);
91447636 132 ubc_setsize(vp, 0);
2d21ac55 133 (void) hfs_lock(cp, HFS_FORCE_LOCK);
91447636
A
134 }
135
91447636
A
136 if (v_type == VREG && !ISSET(cp->c_flag, C_DELETED) && VTOF(vp)->ff_blocks) {
137 hfs_filedone(vp, ap->a_context);
138 }
139 /*
2d21ac55 140 * Remove any directory hints or cached origins
91447636 141 */
2d21ac55 142 if (v_type == VDIR) {
91447636 143 hfs_reldirhints(cp, 0);
593a1d5f 144 }
935ed37a
A
145 if (cp->c_flag & C_HARDLINK) {
146 hfs_relorigins(cp);
2d21ac55 147 }
9bccf70c 148
b0d623f7
A
149 /* Hurry the recycling process along if we're an open-unlinked file */
150 if((v_type == VREG || v_type == VLNK) && (cp->c_flag & C_DELETED)) {
151 recycle = 1;
152 }
153
154 /*
155 * This check is slightly complicated. We should only truncate data
156 * in very specific cases for open-unlinked files. This is because
157 * we want to ensure that the resource fork continues to be available
158 * if the caller has the data fork open. However, this is not symmetric;
159 * someone who has the resource fork open need not be able to access the data
160 * fork once the data fork has gone inactive.
161 *
162 * If we're the last fork, then we have cleaning up to do.
163 *
164 * A) last fork, and vp == c_vp
165 * Truncate away own fork dat. If rsrc fork is not in core, truncate it too.
166 *
167 * B) last fork, and vp == c_rsrc_vp
168 * Truncate ourselves, assume data fork has been cleaned due to C).
169 *
170 * If we're not the last fork, then things are a little different:
171 *
172 * C) not the last fork, vp == c_vp
173 * Truncate ourselves. Once the file has gone out of the namespace,
174 * it cannot be further opened. Further access to the rsrc fork may
175 * continue, however.
176 *
177 * D) not the last fork, vp == c_rsrc_vp
178 * Don't enter the block below, just clean up vnode and push it out of core.
179 */
9bccf70c 180
b0d623f7
A
181 if ((v_type == VREG || v_type == VLNK) && (cp->c_flag & C_DELETED) &&
182 ((forkcount == 1) || (!VNODE_IS_RSRC(vp)))) {
55e303ae 183 if (VTOF(vp)->ff_blocks != 0) {
91447636
A
184 /*
185 * Since we're already inside a transaction,
186 * tell hfs_truncate to skip the ubc_setsize.
187 */
b0d623f7 188 error = hfs_truncate(vp, (off_t)0, IO_NDELAY, 1, 0, ap->a_context);
55e303ae
A
189 if (error)
190 goto out;
191 truncated = 1;
192 }
2d21ac55 193
b0d623f7
A
194 /*
195 * If c_blocks > 0 and we are the last fork (data fork), then
196 * we can go and and truncate away the rsrc fork blocks if
197 * they were not in core.
2d21ac55
A
198 */
199 if ((cp->c_blocks > 0) && (forkcount == 1) && (vp != cp->c_rsrc_vp)) {
200 struct vnode *rvp = NULLVP;
201
b7266188 202 error = hfs_vgetrsrc(hfsmp, vp, &rvp, FALSE, FALSE);
2d21ac55
A
203 if (error)
204 goto out;
205 /*
206 * Defer the vnode_put and ubc_setsize on rvp until hfs_unlock().
207 */
208 cp->c_flag |= C_NEED_RVNODE_PUT | C_NEED_RSRC_SETSIZE;
b0d623f7 209 error = hfs_truncate(rvp, (off_t)0, IO_NDELAY, 1, 0, ap->a_context);
2d21ac55
A
210 if (error)
211 goto out;
212 vnode_recycle(rvp); /* all done with this vnode */
213 }
214 }
215
b0d623f7 216 // If needed, get rid of any xattrs that this file (or directory) may have.
2d21ac55
A
217 // Note that this must happen outside of any other transactions
218 // because it starts/ends its own transactions and grabs its
219 // own locks. This is to prevent a file with a lot of attributes
220 // from creating a transaction that is too large (which panics).
221 //
b0d623f7
A
222 if ((cp->c_attr.ca_recflags & kHFSHasAttributesMask) != 0 &&
223 (cp->c_flag & C_DELETED) && (forkcount <= 1)) {
2d21ac55 224 hfs_removeallattr(hfsmp, cp->c_fileid);
9bccf70c
A
225 }
226
227 /*
228 * Check for a postponed deletion.
229 * (only delete cnode when the last fork goes inactive)
230 */
231 if ((cp->c_flag & C_DELETED) && (forkcount <= 1)) {
232 /*
55e303ae 233 * Mark cnode in transit so that no one can get this
9bccf70c
A
234 * cnode from cnode hash.
235 */
b0d623f7 236 // hfs_chash_mark_in_transit(hfsmp, cp);
2d21ac55
A
237 // XXXdbg - remove the cnode from the hash table since it's deleted
238 // otherwise someone could go to sleep on the cnode and not
239 // be woken up until this vnode gets recycled which could be
240 // a very long time...
b0d623f7 241 hfs_chashremove(hfsmp, cp);
91447636 242
91447636 243 cp->c_flag |= C_NOEXISTS; // XXXdbg
9bccf70c 244 cp->c_rdev = 0;
55e303ae 245
91447636
A
246 if (started_tr == 0) {
247 if (hfs_start_transaction(hfsmp) != 0) {
248 error = EINVAL;
249 goto out;
b4c24cb9
A
250 }
251 started_tr = 1;
252 }
91447636 253
55e303ae
A
254 /*
255 * Reserve some space in the Catalog file.
256 */
257 if ((error = cat_preflight(hfsmp, CAT_DELETE, &cookie, p))) {
258 goto out;
259 }
260 cat_reserve = 1;
261
91447636 262 lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG | SFL_ATTRIBUTE, HFS_EXCLUSIVE_LOCK);
9bccf70c 263
4a3eedf9
A
264 if (cp->c_blocks > 0) {
265 printf("hfs_inactive: deleting non-empty%sfile %d, "
266 "blks %d\n", VNODE_IS_RSRC(vp) ? " rsrc " : " ",
267 (int)cp->c_fileid, (int)cp->c_blocks);
268 }
91447636
A
269
270 //
271 // release the name pointer in the descriptor so that
272 // cat_delete() will use the file-id to do the deletion.
273 // in the case of hard links this is imperative (in the
274 // case of regular files the fileid and cnid are the
275 // same so it doesn't matter).
276 //
277 cat_releasedesc(&cp->c_desc);
278
9bccf70c
A
279 /*
280 * The descriptor name may be zero,
281 * in which case the fileid is used.
282 */
283 error = cat_delete(hfsmp, &cp->c_desc, &cp->c_attr);
284
285 if (error && truncated && (error != ENXIO))
286 printf("hfs_inactive: couldn't delete a truncated file!");
287
288 /* Update HFS Private Data dir */
289 if (error == 0) {
2d21ac55
A
290 hfsmp->hfs_private_attr[FILE_HARDLINKS].ca_entries--;
291 if (vnode_isdir(vp)) {
292 DEC_FOLDERCOUNT(hfsmp, hfsmp->hfs_private_attr[FILE_HARDLINKS]);
293 }
294 (void)cat_update(hfsmp, &hfsmp->hfs_private_desc[FILE_HARDLINKS],
295 &hfsmp->hfs_private_attr[FILE_HARDLINKS], NULL, NULL);
91447636
A
296 }
297
298 hfs_systemfile_unlock(hfsmp, lockflags);
299
300 if (error)
301 goto out;
9bccf70c
A
302
303#if QUOTA
2d21ac55
A
304 if (hfsmp->hfs_flags & HFS_QUOTAS)
305 (void)hfs_chkiq(cp, -1, NOCRED, 0);
9bccf70c 306#endif /* QUOTA */
b0d623f7
A
307
308 /* Already set C_NOEXISTS at the beginning of this block */
2d21ac55 309 cp->c_flag &= ~C_DELETED;
91447636
A
310 cp->c_touch_chgtime = TRUE;
311 cp->c_touch_modtime = TRUE;
9bccf70c
A
312
313 if (error == 0)
2d21ac55 314 hfs_volupdate(hfsmp, (v_type == VDIR) ? VOL_RMDIR : VOL_RMFILE, 0);
9bccf70c
A
315 }
316
4a3eedf9
A
317 /*
318 * A file may have had delayed allocations, in which case hfs_update
319 * would not have updated the catalog record (cat_update). We need
320 * to do that now, before we lose our fork data. We also need to
321 * force the update, or hfs_update will again skip the cat_update.
322 */
91447636
A
323 if ((cp->c_flag & C_MODIFIED) ||
324 cp->c_touch_acctime || cp->c_touch_chgtime || cp->c_touch_modtime) {
b0d623f7 325 if ((cp->c_flag & C_MODIFIED) || cp->c_touch_modtime){
cf7d32b8 326 cp->c_flag |= C_FORCEUPDATE;
b0d623f7 327 }
91447636 328 hfs_update(vp, 0);
b4c24cb9 329 }
9bccf70c 330out:
55e303ae
A
331 if (cat_reserve)
332 cat_postflight(hfsmp, &cookie, p);
333
b4c24cb9
A
334 // XXXdbg - have to do this because a goto could have come here
335 if (started_tr) {
91447636 336 hfs_end_transaction(hfsmp);
b4c24cb9
A
337 started_tr = 0;
338 }
b0d623f7
A
339 /*
340 * This has been removed from the namespace and has no backing store
341 * in the catalog, so we should force a reclaim as soon as possible.
342 * Also, we want to check the flag while we still have the cnode lock.
343 */
344 if (cp->c_flag & C_NOEXISTS)
345 recycle = 1;
b4c24cb9 346
91447636
A
347 hfs_unlock(cp);
348
349 if (took_trunc_lock)
2d21ac55 350 hfs_unlock_truncate(cp, TRUE);
91447636 351
9bccf70c
A
352 /*
353 * If we are done with the vnode, reclaim it
354 * so that it can be reused immediately.
355 */
b0d623f7 356 if (recycle)
91447636 357 vnode_recycle(vp);
9bccf70c
A
358
359 return (error);
360}
361
91447636
A
362/*
363 * File clean-up (zero fill and shrink peof).
364 */
365static int
366hfs_filedone(struct vnode *vp, vfs_context_t context)
367{
368 struct cnode *cp;
369 struct filefork *fp;
370 struct hfsmount *hfsmp;
b0d623f7 371 struct rl_entry *invalid_range;
91447636 372 off_t leof;
b0d623f7 373 u_int32_t blks, blocksize;
91447636
A
374
375 cp = VTOC(vp);
376 fp = VTOF(vp);
377 hfsmp = VTOHFS(vp);
378 leof = fp->ff_size;
379
380 if ((hfsmp->hfs_flags & HFS_READ_ONLY) || (fp->ff_blocks == 0))
381 return (0);
382
383 hfs_unlock(cp);
384 (void) cluster_push(vp, IO_CLOSE);
385 hfs_lock(cp, HFS_FORCE_LOCK);
386
387 /*
388 * Explicitly zero out the areas of file
389 * that are currently marked invalid.
390 */
b0d623f7 391 while ((invalid_range = TAILQ_FIRST(&fp->ff_invalidranges))) {
91447636
A
392 off_t start = invalid_range->rl_start;
393 off_t end = invalid_range->rl_end;
394
395 /* The range about to be written must be validated
396 * first, so that VNOP_BLOCKMAP() will return the
397 * appropriate mapping for the cluster code:
398 */
399 rl_remove(start, end, &fp->ff_invalidranges);
400
401 hfs_unlock(cp);
402 (void) cluster_write(vp, (struct uio *) 0,
403 leof, end + 1, start, (off_t)0,
404 IO_HEADZEROFILL | IO_NOZERODIRTY | IO_NOCACHE);
405 hfs_lock(cp, HFS_FORCE_LOCK);
406 cp->c_flag |= C_MODIFIED;
407 }
408 cp->c_flag &= ~C_ZFWANTSYNC;
409 cp->c_zftimeout = 0;
410 blocksize = VTOVCB(vp)->blockSize;
411 blks = leof / blocksize;
412 if (((off_t)blks * (off_t)blocksize) != leof)
413 blks++;
414 /*
415 * Shrink the peof to the smallest size neccessary to contain the leof.
416 */
417 if (blks < fp->ff_blocks)
b0d623f7 418 (void) hfs_truncate(vp, leof, IO_NDELAY, 0, 0, context);
91447636
A
419 hfs_unlock(cp);
420 (void) cluster_push(vp, IO_CLOSE);
421 hfs_lock(cp, HFS_FORCE_LOCK);
422
423 /*
424 * If the hfs_truncate didn't happen to flush the vnode's
425 * information out to disk, force it to be updated now that
426 * all invalid ranges have been zero-filled and validated:
427 */
428 if (cp->c_flag & C_MODIFIED) {
429 hfs_update(vp, 0);
430 }
431 return (0);
432}
433
9bccf70c
A
434
435/*
436 * Reclaim a cnode so that it can be used for other purposes.
437 */
438__private_extern__
439int
91447636 440hfs_vnop_reclaim(struct vnop_reclaim_args *ap)
9bccf70c
A
441{
442 struct vnode *vp = ap->a_vp;
91447636 443 struct cnode *cp;
9bccf70c
A
444 struct filefork *fp = NULL;
445 struct filefork *altfp = NULL;
b0d623f7 446 struct hfsmount *hfsmp = VTOHFS(vp);
91447636 447 int reclaim_cnode = 0;
9bccf70c 448
91447636
A
449 (void) hfs_lock(VTOC(vp), HFS_FORCE_LOCK);
450 cp = VTOC(vp);
b0d623f7 451
4a3eedf9
A
452 /*
453 * A file may have had delayed allocations, in which case hfs_update
454 * would not have updated the catalog record (cat_update). We need
455 * to do that now, before we lose our fork data. We also need to
456 * force the update, or hfs_update will again skip the cat_update.
457 */
458 if ((cp->c_flag & C_MODIFIED) ||
b0d623f7
A
459 cp->c_touch_acctime || cp->c_touch_chgtime || cp->c_touch_modtime) {
460 if ((cp->c_flag & C_MODIFIED) || cp->c_touch_modtime){
cf7d32b8 461 cp->c_flag |= C_FORCEUPDATE;
b0d623f7 462 }
4a3eedf9
A
463 hfs_update(vp, 0);
464 }
465
55e303ae
A
466 /*
467 * Keep track of an inactive hot file.
468 */
2d21ac55
A
469 if (!vnode_isdir(vp) &&
470 !vnode_issystem(vp) &&
471 !(cp->c_flag & (C_DELETED | C_NOEXISTS)) ) {
91447636 472 (void) hfs_addhotfile(vp);
2d21ac55 473 }
91447636 474 vnode_removefsref(vp);
9bccf70c
A
475
476 /*
477 * Find file fork for this vnode (if any)
478 * Also check if another fork is active
479 */
91447636
A
480 if (cp->c_vp == vp) {
481 fp = cp->c_datafork;
482 altfp = cp->c_rsrcfork;
483
9bccf70c
A
484 cp->c_datafork = NULL;
485 cp->c_vp = NULL;
91447636
A
486 } else if (cp->c_rsrc_vp == vp) {
487 fp = cp->c_rsrcfork;
488 altfp = cp->c_datafork;
489
9bccf70c
A
490 cp->c_rsrcfork = NULL;
491 cp->c_rsrc_vp = NULL;
9bccf70c 492 } else {
b0d623f7 493 panic("hfs_vnop_reclaim: vp points to wrong cnode (vp=%p cp->c_vp=%p cp->c_rsrc_vp=%p)\n", vp, cp->c_vp, cp->c_rsrc_vp);
9bccf70c 494 }
9bccf70c
A
495 /*
496 * On the last fork, remove the cnode from its hash chain.
497 */
91447636
A
498 if (altfp == NULL) {
499 /* If we can't remove it then the cnode must persist! */
b0d623f7 500 if (hfs_chashremove(hfsmp, cp) == 0)
91447636
A
501 reclaim_cnode = 1;
502 /*
503 * Remove any directory hints
504 */
505 if (vnode_isdir(vp)) {
506 hfs_reldirhints(cp, 0);
507 }
b0d623f7
A
508
509 if(cp->c_flag & C_HARDLINK) {
935ed37a
A
510 hfs_relorigins(cp);
511 }
91447636
A
512 }
513 /* Release the file fork and related data */
9bccf70c 514 if (fp) {
9bccf70c 515 /* Dump cached symlink data */
91447636 516 if (vnode_islnk(vp) && (fp->ff_symlinkptr != NULL)) {
9bccf70c 517 FREE(fp->ff_symlinkptr, M_TEMP);
91447636 518 }
9bccf70c 519 FREE_ZONE(fp, sizeof(struct filefork), M_HFSFORK);
9bccf70c
A
520 }
521
9bccf70c
A
522 /*
523 * If there was only one active fork then we can release the cnode.
524 */
91447636 525 if (reclaim_cnode) {
b0d623f7 526 hfs_chashwakeup(hfsmp, cp, H_ALLOC | H_TRANSIT);
91447636
A
527 hfs_reclaim_cnode(cp);
528 } else /* cnode in use */ {
529 hfs_unlock(cp);
9bccf70c
A
530 }
531
91447636 532 vnode_clearfsnode(vp);
9bccf70c
A
533 return (0);
534}
535
536
91447636 537extern int (**hfs_vnodeop_p) (void *);
b0d623f7 538extern int (**hfs_std_vnodeop_p) (void *);
91447636 539extern int (**hfs_specop_p) (void *);
2d21ac55 540#if FIFO
91447636 541extern int (**hfs_fifoop_p) (void *);
2d21ac55 542#endif
9bccf70c
A
543
544/*
545 * hfs_getnewvnode - get new default vnode
546 *
91447636 547 * The vnode is returned with an iocount and the cnode locked
9bccf70c 548 */
9bccf70c
A
549__private_extern__
550int
91447636
A
551hfs_getnewvnode(
552 struct hfsmount *hfsmp,
553 struct vnode *dvp,
554 struct componentname *cnp,
555 struct cat_desc *descp,
2d21ac55 556 int flags,
91447636
A
557 struct cat_attr *attrp,
558 struct cat_fork *forkp,
9bccf70c
A
559 struct vnode **vpp)
560{
561 struct mount *mp = HFSTOVFS(hfsmp);
562 struct vnode *vp = NULL;
91447636
A
563 struct vnode **cvpp;
564 struct vnode *tvp = NULLVP;
565 struct cnode *cp = NULL;
9bccf70c 566 struct filefork *fp = NULL;
b0d623f7 567 int hfs_standard = 0;
9bccf70c 568 int retval;
91447636 569 int issystemfile;
2d21ac55 570 int wantrsrc;
91447636
A
571 struct vnode_fsparam vfsp;
572 enum vtype vtype;
2d21ac55
A
573#if QUOTA
574 int i;
575#endif /* QUOTA */
91447636 576
b0d623f7
A
577 hfs_standard = (hfsmp->hfs_flags & HFS_STANDARD);
578
91447636 579 if (attrp->ca_fileid == 0) {
9bccf70c 580 *vpp = NULL;
91447636 581 return (ENOENT);
9bccf70c
A
582 }
583
584#if !FIFO
585 if (IFTOVT(attrp->ca_mode) == VFIFO) {
586 *vpp = NULL;
91447636 587 return (ENOTSUP);
9bccf70c 588 }
2d21ac55 589#endif /* !FIFO */
91447636
A
590 vtype = IFTOVT(attrp->ca_mode);
591 issystemfile = (descp->cd_flags & CD_ISMETA) && (vtype == VREG);
2d21ac55
A
592 wantrsrc = flags & GNV_WANTRSRC;
593
594#ifdef HFS_CHECK_LOCK_ORDER
595 /*
596 * The only case were its permissible to hold the parent cnode
597 * lock is during a create operation (hfs_makenode) or when
598 * we don't need the cnode lock (GNV_SKIPLOCK).
599 */
600 if ((dvp != NULL) &&
601 (flags & (GNV_CREATE | GNV_SKIPLOCK)) == 0 &&
602 VTOC(dvp)->c_lockowner == current_thread()) {
603 panic("hfs_getnewvnode: unexpected hold of parent cnode %p", VTOC(dvp));
604 }
605#endif /* HFS_CHECK_LOCK_ORDER */
91447636
A
606
607 /*
608 * Get a cnode (new or existing)
91447636 609 */
b0d623f7 610 cp = hfs_chash_getcnode(hfsmp, attrp->ca_fileid, vpp, wantrsrc, (flags & GNV_SKIPLOCK));
2d21ac55
A
611
612 /*
613 * If the id is no longer valid for lookups we'll get back a NULL cp.
614 */
615 if (cp == NULL) {
616 return (ENOENT);
617 }
91447636 618
b7266188
A
619 /*
620 * Hardlinks may need an updated catalog descriptor. However, if
621 * the cnode has already been marked as open-unlinked (C_DELETED), then don't
622 * replace its descriptor.
623 */
624 if (!(hfs_checkdeleted(cp))) {
625 if ((cp->c_flag & C_HARDLINK) && descp->cd_nameptr && descp->cd_namelen > 0) {
626 replace_desc(cp, descp);
627 }
9bccf70c 628 }
91447636
A
629 /* Check if we found a matching vnode */
630 if (*vpp != NULL)
631 return (0);
9bccf70c 632
91447636
A
633 /*
634 * If this is a new cnode then initialize it.
635 */
636 if (ISSET(cp->c_hflag, H_ALLOC)) {
637 lck_rw_init(&cp->c_truncatelock, hfs_rwlock_group, hfs_lock_attr);
b0d623f7
A
638#if HFS_COMPRESSION
639 cp->c_decmp = NULL;
640#endif
91447636
A
641
642 /* Make sure its still valid (ie exists on disk). */
2d21ac55
A
643 if (!(flags & GNV_CREATE) &&
644 !hfs_valid_cnode(hfsmp, dvp, (wantrsrc ? NULL : cnp), cp->c_fileid)) {
b0d623f7 645 hfs_chash_abort(hfsmp, cp);
91447636
A
646 hfs_reclaim_cnode(cp);
647 *vpp = NULL;
648 return (ENOENT);
9bccf70c 649 }
9bccf70c
A
650 bcopy(attrp, &cp->c_attr, sizeof(struct cat_attr));
651 bcopy(descp, &cp->c_desc, sizeof(struct cat_desc));
9bccf70c 652
9bccf70c
A
653 /* The name was inherited so clear descriptor state... */
654 descp->cd_namelen = 0;
655 descp->cd_nameptr = NULL;
656 descp->cd_flags &= ~CD_HASBUF;
657
658 /* Tag hardlinks */
2d21ac55
A
659 if ((vtype == VREG || vtype == VDIR) &&
660 ((descp->cd_cnid != attrp->ca_fileid) ||
661 (attrp->ca_recflags & kHFSHasLinkChainMask))) {
9bccf70c
A
662 cp->c_flag |= C_HARDLINK;
663 }
2d21ac55
A
664 /*
665 * Fix-up dir link counts.
666 *
667 * Earlier versions of Leopard used ca_linkcount for posix
668 * nlink support (effectively the sub-directory count + 2).
669 * That is now accomplished using the ca_dircount field with
670 * the corresponding kHFSHasFolderCountMask flag.
671 *
672 * For directories the ca_linkcount is the true link count,
673 * tracking the number of actual hardlinks to a directory.
674 *
675 * We only do this if the mount has HFS_FOLDERCOUNT set;
676 * at the moment, we only set that for HFSX volumes.
677 */
678 if ((hfsmp->hfs_flags & HFS_FOLDERCOUNT) &&
679 (vtype == VDIR) &&
680 !(attrp->ca_recflags & kHFSHasFolderCountMask) &&
681 (cp->c_attr.ca_linkcount > 1)) {
682 if (cp->c_attr.ca_entries == 0)
683 cp->c_attr.ca_dircount = 0;
684 else
685 cp->c_attr.ca_dircount = cp->c_attr.ca_linkcount - 2;
9bccf70c 686
2d21ac55
A
687 cp->c_attr.ca_linkcount = 1;
688 cp->c_attr.ca_recflags |= kHFSHasFolderCountMask;
689 if ( !(hfsmp->hfs_flags & HFS_READ_ONLY) )
690 cp->c_flag |= C_MODIFIED;
9bccf70c
A
691 }
692#if QUOTA
2d21ac55
A
693 if (hfsmp->hfs_flags & HFS_QUOTAS) {
694 for (i = 0; i < MAXQUOTAS; i++)
695 cp->c_dquot[i] = NODQUOT;
696 }
9bccf70c
A
697#endif /* QUOTA */
698 }
699
2d21ac55 700 if (vtype == VDIR) {
91447636
A
701 if (cp->c_vp != NULL)
702 panic("hfs_getnewvnode: orphaned vnode (data)");
703 cvpp = &cp->c_vp;
704 } else {
9bccf70c
A
705 if (forkp && attrp->ca_blocks < forkp->cf_blocks)
706 panic("hfs_getnewvnode: bad ca_blocks (too small)");
707 /*
708 * Allocate and initialize a file fork...
709 */
710 MALLOC_ZONE(fp, struct filefork *, sizeof(struct filefork),
711 M_HFSFORK, M_WAITOK);
9bccf70c
A
712 fp->ff_cp = cp;
713 if (forkp)
55e303ae 714 bcopy(forkp, &fp->ff_data, sizeof(struct cat_fork));
91447636
A
715 else
716 bzero(&fp->ff_data, sizeof(struct cat_fork));
9bccf70c 717 rl_init(&fp->ff_invalidranges);
91447636
A
718 fp->ff_sysfileinfo = 0;
719
9bccf70c
A
720 if (wantrsrc) {
721 if (cp->c_rsrcfork != NULL)
91447636
A
722 panic("hfs_getnewvnode: orphaned rsrc fork");
723 if (cp->c_rsrc_vp != NULL)
724 panic("hfs_getnewvnode: orphaned vnode (rsrc)");
9bccf70c 725 cp->c_rsrcfork = fp;
91447636
A
726 cvpp = &cp->c_rsrc_vp;
727 if ( (tvp = cp->c_vp) != NULLVP )
728 cp->c_flag |= C_NEED_DVNODE_PUT;
9bccf70c
A
729 } else {
730 if (cp->c_datafork != NULL)
91447636
A
731 panic("hfs_getnewvnode: orphaned data fork");
732 if (cp->c_vp != NULL)
733 panic("hfs_getnewvnode: orphaned vnode (data)");
9bccf70c 734 cp->c_datafork = fp;
91447636
A
735 cvpp = &cp->c_vp;
736 if ( (tvp = cp->c_rsrc_vp) != NULLVP)
737 cp->c_flag |= C_NEED_RVNODE_PUT;
9bccf70c
A
738 }
739 }
91447636
A
740 if (tvp != NULLVP) {
741 /*
742 * grab an iocount on the vnode we weren't
743 * interested in (i.e. we want the resource fork
744 * but the cnode already has the data fork)
745 * to prevent it from being
746 * recycled by us when we call vnode_create
747 * which will result in a deadlock when we
748 * try to take the cnode lock in hfs_vnop_fsync or
749 * hfs_vnop_reclaim... vnode_get can be called here
750 * because we already hold the cnode lock which will
751 * prevent the vnode from changing identity until
752 * we drop it.. vnode_get will not block waiting for
753 * a change of state... however, it will return an
754 * error if the current iocount == 0 and we've already
755 * started to terminate the vnode... we don't need/want to
756 * grab an iocount in the case since we can't cause
757 * the fileystem to be re-entered on this thread for this vp
758 *
759 * the matching vnode_put will happen in hfs_unlock
760 * after we've dropped the cnode lock
761 */
762 if ( vnode_get(tvp) != 0)
763 cp->c_flag &= ~(C_NEED_RVNODE_PUT | C_NEED_DVNODE_PUT);
764 }
765 vfsp.vnfs_mp = mp;
766 vfsp.vnfs_vtype = vtype;
767 vfsp.vnfs_str = "hfs";
2d21ac55
A
768 if ((cp->c_flag & C_HARDLINK) && (vtype == VDIR)) {
769 vfsp.vnfs_dvp = NULL; /* no parent for me! */
770 vfsp.vnfs_cnp = NULL; /* no name for me! */
771 } else {
772 vfsp.vnfs_dvp = dvp;
773 vfsp.vnfs_cnp = cnp;
774 }
91447636 775 vfsp.vnfs_fsnode = cp;
b0d623f7
A
776
777 /*
778 * Special Case HFS Standard VNOPs from HFS+, since
779 * HFS standard is readonly/deprecated as of 10.6
780 */
781
2d21ac55 782#if FIFO
b0d623f7 783 if (vtype == VFIFO )
91447636 784 vfsp.vnfs_vops = hfs_fifoop_p;
2d21ac55
A
785 else
786#endif
787 if (vtype == VBLK || vtype == VCHR)
91447636 788 vfsp.vnfs_vops = hfs_specop_p;
b0d623f7
A
789 else if (hfs_standard)
790 vfsp.vnfs_vops = hfs_std_vnodeop_p;
791 else
91447636 792 vfsp.vnfs_vops = hfs_vnodeop_p;
b0d623f7 793
91447636
A
794 if (vtype == VBLK || vtype == VCHR)
795 vfsp.vnfs_rdev = attrp->ca_rdev;
796 else
797 vfsp.vnfs_rdev = 0;
9bccf70c 798
91447636
A
799 if (forkp)
800 vfsp.vnfs_filesize = forkp->cf_size;
801 else
802 vfsp.vnfs_filesize = 0;
803
2d21ac55
A
804 vfsp.vnfs_flags = VNFS_ADDFSREF;
805 if (dvp == NULLVP || cnp == NULL || !(cnp->cn_flags & MAKEENTRY))
806 vfsp.vnfs_flags |= VNFS_NOCACHE;
9bccf70c
A
807
808 /* Tag system files */
91447636
A
809 vfsp.vnfs_marksystem = issystemfile;
810
9bccf70c 811 /* Tag root directory */
91447636
A
812 if (descp->cd_cnid == kHFSRootFolderID)
813 vfsp.vnfs_markroot = 1;
814 else
815 vfsp.vnfs_markroot = 0;
816
817 if ((retval = vnode_create(VNCREATE_FLAVOR, VCREATESIZE, &vfsp, cvpp))) {
818 if (fp) {
819 if (fp == cp->c_datafork)
820 cp->c_datafork = NULL;
821 else
822 cp->c_rsrcfork = NULL;
823
824 FREE_ZONE(fp, sizeof(struct filefork), M_HFSFORK);
825 }
826 /*
827 * If this is a newly created cnode or a vnode reclaim
828 * occurred during the attachment, then cleanup the cnode.
829 */
830 if ((cp->c_vp == NULL) && (cp->c_rsrc_vp == NULL)) {
b0d623f7 831 hfs_chash_abort(hfsmp, cp);
91447636 832 hfs_reclaim_cnode(cp);
cf7d32b8
A
833 }
834 else {
b0d623f7 835 hfs_chashwakeup(hfsmp, cp, H_ALLOC | H_ATTACH);
cf7d32b8
A
836 if ((flags & GNV_SKIPLOCK) == 0){
837 hfs_unlock(cp);
838 }
91447636
A
839 }
840 *vpp = NULL;
841 return (retval);
842 }
843 vp = *cvpp;
91447636 844 vnode_settag(vp, VT_HFS);
2d21ac55
A
845 if (cp->c_flag & C_HARDLINK) {
846 vnode_setmultipath(vp);
847 }
4a3eedf9
A
848 /*
849 * Tag resource fork vnodes as needing an VNOP_INACTIVE
850 * so that any deferred removes (open unlinked files)
851 * have the chance to process the resource fork.
852 */
853 if (VNODE_IS_RSRC(vp)) {
b0d623f7
A
854 int err;
855 KERNEL_DEBUG_CONSTANT((FSDBG_CODE(DBG_FSRW, 37)), cp->c_vp, cp->c_rsrc_vp, 0, 0, 0);
856
4a3eedf9 857 /* Force VL_NEEDINACTIVE on this vnode */
b0d623f7
A
858 err = vnode_ref(vp);
859 if (err == 0) {
860 vnode_rele(vp);
861 }
4a3eedf9 862 }
b0d623f7 863 hfs_chashwakeup(hfsmp, cp, H_ALLOC | H_ATTACH);
91447636
A
864
865 /*
866 * Stop tracking an active hot file.
867 */
2d21ac55 868 if (!(flags & GNV_CREATE) && (vtype != VDIR) && !issystemfile) {
91447636 869 (void) hfs_removehotfile(vp);
2d21ac55 870 }
91447636
A
871
872 *vpp = vp;
873 return (0);
874}
875
876
877static void
878hfs_reclaim_cnode(struct cnode *cp)
879{
880#if QUOTA
881 int i;
882
883 for (i = 0; i < MAXQUOTAS; i++) {
884 if (cp->c_dquot[i] != NODQUOT) {
885 dqreclaim(cp->c_dquot[i]);
886 cp->c_dquot[i] = NODQUOT;
887 }
888 }
889#endif /* QUOTA */
890
91447636
A
891 /*
892 * If the descriptor has a name then release it
893 */
2d21ac55
A
894 if ((cp->c_desc.cd_flags & CD_HASBUF) && (cp->c_desc.cd_nameptr != 0)) {
895 const char *nameptr;
91447636 896
2d21ac55 897 nameptr = (const char *) cp->c_desc.cd_nameptr;
91447636
A
898 cp->c_desc.cd_nameptr = 0;
899 cp->c_desc.cd_flags &= ~CD_HASBUF;
900 cp->c_desc.cd_namelen = 0;
901 vfs_removename(nameptr);
902 }
903
904 lck_rw_destroy(&cp->c_rwlock, hfs_rwlock_group);
905 lck_rw_destroy(&cp->c_truncatelock, hfs_rwlock_group);
b0d623f7
A
906#if HFS_COMPRESSION
907 if (cp->c_decmp) {
908 decmpfs_cnode_destroy(cp->c_decmp);
909 FREE_ZONE(cp->c_decmp, sizeof(*(cp->c_decmp)), M_DECMPFS_CNODE);
910 }
911#endif
91447636
A
912 bzero(cp, sizeof(struct cnode));
913 FREE_ZONE(cp, sizeof(struct cnode), M_HFSNODE);
914}
915
916
2d21ac55
A
917__private_extern__
918int
91447636
A
919hfs_valid_cnode(struct hfsmount *hfsmp, struct vnode *dvp, struct componentname *cnp, cnid_t cnid)
920{
921 struct cat_attr attr;
922 struct cat_desc cndesc;
923 int stillvalid = 0;
924 int lockflags;
9bccf70c 925
91447636
A
926 /* System files are always valid */
927 if (cnid < kHFSFirstUserCatalogNodeID)
928 return (1);
929
930 /* XXX optimization: check write count in dvp */
931
932 lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_SHARED_LOCK);
933
934 if (dvp && cnp) {
935 bzero(&cndesc, sizeof(cndesc));
2d21ac55 936 cndesc.cd_nameptr = (const u_int8_t *)cnp->cn_nameptr;
91447636 937 cndesc.cd_namelen = cnp->cn_namelen;
2d21ac55 938 cndesc.cd_parentcnid = VTOC(dvp)->c_fileid;
91447636
A
939 cndesc.cd_hint = VTOC(dvp)->c_childhint;
940
941 if ((cat_lookup(hfsmp, &cndesc, 0, NULL, &attr, NULL, NULL) == 0) &&
942 (cnid == attr.ca_fileid)) {
943 stillvalid = 1;
944 }
9bccf70c 945 } else {
2d21ac55 946 if (cat_idlookup(hfsmp, cnid, 0, NULL, NULL, NULL) == 0) {
91447636
A
947 stillvalid = 1;
948 }
949 }
950 hfs_systemfile_unlock(hfsmp, lockflags);
951
952 return (stillvalid);
953}
954
955/*
956 * Touch cnode times based on c_touch_xxx flags
957 *
958 * cnode must be locked exclusive
959 *
960 * This will also update the volume modify time
961 */
962__private_extern__
963void
964hfs_touchtimes(struct hfsmount *hfsmp, struct cnode* cp)
965{
2d21ac55
A
966 /* don't modify times if volume is read-only */
967 if (hfsmp->hfs_flags & HFS_READ_ONLY) {
968 cp->c_touch_acctime = FALSE;
969 cp->c_touch_chgtime = FALSE;
970 cp->c_touch_modtime = FALSE;
971 }
972 else if (hfsmp->hfs_flags & HFS_STANDARD) {
91447636 973 /* HFS Standard doesn't support access times */
91447636 974 cp->c_touch_acctime = FALSE;
9bccf70c
A
975 }
976
2d21ac55
A
977 /*
978 * Skip access time updates if:
979 * . MNT_NOATIME is set
980 * . a file system freeze is in progress
981 * . a file system resize is in progress
b0d623f7 982 * . the vnode associated with this cnode is marked for rapid aging
2d21ac55
A
983 */
984 if (cp->c_touch_acctime) {
985 if ((vfs_flags(hfsmp->hfs_mp) & MNT_NOATIME) ||
986 (hfsmp->hfs_freezing_proc != NULL) ||
b0d623f7
A
987 (hfsmp->hfs_flags & HFS_RESIZE_IN_PROGRESS) ||
988 (cp->c_vp && vnode_israge(cp->c_vp)))
2d21ac55
A
989 cp->c_touch_acctime = FALSE;
990 }
91447636
A
991 if (cp->c_touch_acctime || cp->c_touch_chgtime || cp->c_touch_modtime) {
992 struct timeval tv;
993 int touchvol = 0;
9bccf70c 994
91447636
A
995 microtime(&tv);
996
997 if (cp->c_touch_acctime) {
998 cp->c_atime = tv.tv_sec;
9bccf70c 999 /*
91447636
A
1000 * When the access time is the only thing changing
1001 * then make sure its sufficiently newer before
1002 * committing it to disk.
9bccf70c 1003 */
91447636
A
1004 if ((((u_int32_t)cp->c_atime - (u_int32_t)(cp)->c_attr.ca_atimeondisk) >
1005 ATIME_ONDISK_ACCURACY)) {
1006 cp->c_flag |= C_MODIFIED;
1007 }
1008 cp->c_touch_acctime = FALSE;
1009 }
1010 if (cp->c_touch_modtime) {
1011 cp->c_mtime = tv.tv_sec;
1012 cp->c_touch_modtime = FALSE;
1013 cp->c_flag |= C_MODIFIED;
1014 touchvol = 1;
1015#if 1
9bccf70c 1016 /*
91447636 1017 * HFS dates that WE set must be adjusted for DST
9bccf70c 1018 */
91447636
A
1019 if ((hfsmp->hfs_flags & HFS_STANDARD) && gTimeZone.tz_dsttime) {
1020 cp->c_mtime += 3600;
1021 }
9bccf70c 1022#endif
91447636
A
1023 }
1024 if (cp->c_touch_chgtime) {
1025 cp->c_ctime = tv.tv_sec;
1026 cp->c_touch_chgtime = FALSE;
1027 cp->c_flag |= C_MODIFIED;
1028 touchvol = 1;
1029 }
1030
1031 /* Touch the volume modtime if needed */
1032 if (touchvol) {
2d21ac55 1033 MarkVCBDirty(hfsmp);
91447636
A
1034 HFSTOVCB(hfsmp)->vcbLsMod = tv.tv_sec;
1035 }
9bccf70c 1036 }
91447636
A
1037}
1038
1039/*
1040 * Lock a cnode.
1041 */
1042__private_extern__
1043int
1044hfs_lock(struct cnode *cp, enum hfslocktype locktype)
1045{
1046 void * thread = current_thread();
1047
2d21ac55 1048 if (cp->c_lockowner == thread) {
91447636 1049 /*
2d21ac55 1050 * Only the extents and bitmap file's support lock recursion.
91447636 1051 */
2d21ac55
A
1052 if ((cp->c_fileid == kHFSExtentsFileID) ||
1053 (cp->c_fileid == kHFSAllocationFileID)) {
1054 cp->c_syslockcount++;
91447636 1055 } else {
2d21ac55 1056 panic("hfs_lock: locking against myself!");
91447636
A
1057 }
1058 } else if (locktype == HFS_SHARED_LOCK) {
1059 lck_rw_lock_shared(&cp->c_rwlock);
1060 cp->c_lockowner = HFS_SHARED_OWNER;
2d21ac55
A
1061
1062 } else /* HFS_EXCLUSIVE_LOCK */ {
91447636
A
1063 lck_rw_lock_exclusive(&cp->c_rwlock);
1064 cp->c_lockowner = thread;
2d21ac55
A
1065
1066 /*
1067 * Only the extents and bitmap file's support lock recursion.
1068 */
1069 if ((cp->c_fileid == kHFSExtentsFileID) ||
1070 (cp->c_fileid == kHFSAllocationFileID)) {
1071 cp->c_syslockcount = 1;
1072 }
1073 }
1074
1075#ifdef HFS_CHECK_LOCK_ORDER
1076 /*
1077 * Regular cnodes (non-system files) cannot be locked
1078 * while holding the journal lock or a system file lock.
1079 */
1080 if (!(cp->c_desc.cd_flags & CD_ISMETA) &&
1081 ((cp->c_fileid > kHFSFirstUserCatalogNodeID) || (cp->c_fileid == kHFSRootFolderID))) {
1082 vnode_t vp = NULLVP;
1083
1084 /* Find corresponding vnode. */
1085 if (cp->c_vp != NULLVP && VTOC(cp->c_vp) == cp) {
1086 vp = cp->c_vp;
1087 } else if (cp->c_rsrc_vp != NULLVP && VTOC(cp->c_rsrc_vp) == cp) {
1088 vp = cp->c_rsrc_vp;
1089 }
1090 if (vp != NULLVP) {
1091 struct hfsmount *hfsmp = VTOHFS(vp);
1092
1093 if (hfsmp->jnl && (journal_owner(hfsmp->jnl) == thread)) {
1094 /* This will eventually be a panic here. */
1095 printf("hfs_lock: bad lock order (cnode after journal)\n");
1096 }
1097 if (hfsmp->hfs_catalog_cp && hfsmp->hfs_catalog_cp->c_lockowner == thread) {
1098 panic("hfs_lock: bad lock order (cnode after catalog)");
1099 }
1100 if (hfsmp->hfs_attribute_cp && hfsmp->hfs_attribute_cp->c_lockowner == thread) {
1101 panic("hfs_lock: bad lock order (cnode after attribute)");
1102 }
1103 if (hfsmp->hfs_extents_cp && hfsmp->hfs_extents_cp->c_lockowner == thread) {
1104 panic("hfs_lock: bad lock order (cnode after extents)");
1105 }
1106 }
91447636 1107 }
2d21ac55
A
1108#endif /* HFS_CHECK_LOCK_ORDER */
1109
55e303ae 1110 /*
91447636 1111 * Skip cnodes that no longer exist (were deleted).
55e303ae 1112 */
91447636
A
1113 if ((locktype != HFS_FORCE_LOCK) &&
1114 ((cp->c_desc.cd_flags & CD_ISMETA) == 0) &&
1115 (cp->c_flag & C_NOEXISTS)) {
1116 hfs_unlock(cp);
1117 return (ENOENT);
1118 }
1119 return (0);
1120}
55e303ae 1121
91447636
A
1122/*
1123 * Lock a pair of cnodes.
1124 */
1125__private_extern__
1126int
1127hfs_lockpair(struct cnode *cp1, struct cnode *cp2, enum hfslocktype locktype)
1128{
1129 struct cnode *first, *last;
1130 int error;
1131
1132 /*
1133 * If cnodes match then just lock one.
1134 */
1135 if (cp1 == cp2) {
1136 return hfs_lock(cp1, locktype);
9bccf70c
A
1137 }
1138
91447636 1139 /*
2d21ac55 1140 * Lock in cnode address order.
91447636 1141 */
2d21ac55 1142 if (cp1 < cp2) {
91447636
A
1143 first = cp1;
1144 last = cp2;
1145 } else {
1146 first = cp2;
1147 last = cp1;
1148 }
1149
1150 if ( (error = hfs_lock(first, locktype))) {
1151 return (error);
1152 }
1153 if ( (error = hfs_lock(last, locktype))) {
1154 hfs_unlock(first);
1155 return (error);
1156 }
9bccf70c
A
1157 return (0);
1158}
1159
91447636
A
1160/*
1161 * Check ordering of two cnodes. Return true if they are are in-order.
1162 */
1163static int
1164hfs_isordered(struct cnode *cp1, struct cnode *cp2)
1165{
1166 if (cp1 == cp2)
1167 return (0);
1168 if (cp1 == NULL || cp2 == (struct cnode *)0xffffffff)
1169 return (1);
1170 if (cp2 == NULL || cp1 == (struct cnode *)0xffffffff)
1171 return (0);
2d21ac55
A
1172 /*
1173 * Locking order is cnode address order.
1174 */
1175 return (cp1 < cp2);
91447636
A
1176}
1177
1178/*
1179 * Acquire 4 cnode locks.
2d21ac55 1180 * - locked in cnode address order (lesser address first).
91447636
A
1181 * - all or none of the locks are taken
1182 * - only one lock taken per cnode (dup cnodes are skipped)
1183 * - some of the cnode pointers may be null
1184 */
1185__private_extern__
1186int
1187hfs_lockfour(struct cnode *cp1, struct cnode *cp2, struct cnode *cp3,
b0d623f7 1188 struct cnode *cp4, enum hfslocktype locktype, struct cnode **error_cnode)
91447636
A
1189{
1190 struct cnode * a[3];
1191 struct cnode * b[3];
1192 struct cnode * list[4];
1193 struct cnode * tmp;
1194 int i, j, k;
1195 int error;
b0d623f7
A
1196 if (error_cnode) {
1197 *error_cnode = NULL;
1198 }
91447636
A
1199
1200 if (hfs_isordered(cp1, cp2)) {
1201 a[0] = cp1; a[1] = cp2;
1202 } else {
1203 a[0] = cp2; a[1] = cp1;
1204 }
1205 if (hfs_isordered(cp3, cp4)) {
1206 b[0] = cp3; b[1] = cp4;
1207 } else {
1208 b[0] = cp4; b[1] = cp3;
1209 }
1210 a[2] = (struct cnode *)0xffffffff; /* sentinel value */
1211 b[2] = (struct cnode *)0xffffffff; /* sentinel value */
1212
1213 /*
1214 * Build the lock list, skipping over duplicates
1215 */
1216 for (i = 0, j = 0, k = 0; (i < 2 || j < 2); ) {
1217 tmp = hfs_isordered(a[i], b[j]) ? a[i++] : b[j++];
1218 if (k == 0 || tmp != list[k-1])
1219 list[k++] = tmp;
1220 }
1221
1222 /*
1223 * Now we can lock using list[0 - k].
1224 * Skip over NULL entries.
1225 */
1226 for (i = 0; i < k; ++i) {
1227 if (list[i])
1228 if ((error = hfs_lock(list[i], locktype))) {
b0d623f7
A
1229 /* Only stuff error_cnode if requested */
1230 if (error_cnode) {
1231 *error_cnode = list[i];
1232 }
91447636
A
1233 /* Drop any locks we acquired. */
1234 while (--i >= 0) {
1235 if (list[i])
1236 hfs_unlock(list[i]);
1237 }
1238 return (error);
1239 }
1240 }
1241 return (0);
1242}
1243
1244
1245/*
1246 * Unlock a cnode.
1247 */
1248__private_extern__
1249void
1250hfs_unlock(struct cnode *cp)
1251{
1252 vnode_t rvp = NULLVP;
0c530ab8 1253 vnode_t vp = NULLVP;
2d21ac55
A
1254 u_int32_t c_flag;
1255 void *lockowner;
91447636 1256
2d21ac55
A
1257 /*
1258 * Only the extents and bitmap file's support lock recursion.
1259 */
1260 if ((cp->c_fileid == kHFSExtentsFileID) ||
1261 (cp->c_fileid == kHFSAllocationFileID)) {
1262 if (--cp->c_syslockcount > 0) {
1263 return;
91447636
A
1264 }
1265 }
0c530ab8
A
1266 c_flag = cp->c_flag;
1267 cp->c_flag &= ~(C_NEED_DVNODE_PUT | C_NEED_RVNODE_PUT | C_NEED_DATA_SETSIZE | C_NEED_RSRC_SETSIZE);
2d21ac55 1268
0c530ab8 1269 if (c_flag & (C_NEED_DVNODE_PUT | C_NEED_DATA_SETSIZE)) {
2d21ac55 1270 vp = cp->c_vp;
0c530ab8
A
1271 }
1272 if (c_flag & (C_NEED_RVNODE_PUT | C_NEED_RSRC_SETSIZE)) {
2d21ac55 1273 rvp = cp->c_rsrc_vp;
0c530ab8 1274 }
6601e61a 1275
2d21ac55
A
1276 lockowner = cp->c_lockowner;
1277 if (lockowner == current_thread()) {
1278 cp->c_lockowner = NULL;
1279 lck_rw_unlock_exclusive(&cp->c_rwlock);
1280 } else {
1281 lck_rw_unlock_shared(&cp->c_rwlock);
1282 }
91447636 1283
0c530ab8
A
1284 /* Perform any vnode post processing after cnode lock is dropped. */
1285 if (vp) {
1286 if (c_flag & C_NEED_DATA_SETSIZE)
1287 ubc_setsize(vp, 0);
1288 if (c_flag & C_NEED_DVNODE_PUT)
1289 vnode_put(vp);
1290 }
1291 if (rvp) {
1292 if (c_flag & C_NEED_RSRC_SETSIZE)
1293 ubc_setsize(rvp, 0);
1294 if (c_flag & C_NEED_RVNODE_PUT)
2d21ac55 1295 vnode_put(rvp);
0c530ab8 1296 }
91447636
A
1297}
1298
1299/*
1300 * Unlock a pair of cnodes.
1301 */
1302__private_extern__
1303void
1304hfs_unlockpair(struct cnode *cp1, struct cnode *cp2)
1305{
1306 hfs_unlock(cp1);
1307 if (cp2 != cp1)
1308 hfs_unlock(cp2);
1309}
1310
1311/*
1312 * Unlock a group of cnodes.
1313 */
1314__private_extern__
1315void
1316hfs_unlockfour(struct cnode *cp1, struct cnode *cp2, struct cnode *cp3, struct cnode *cp4)
1317{
1318 struct cnode * list[4];
1319 int i, k = 0;
1320
1321 if (cp1) {
1322 hfs_unlock(cp1);
1323 list[k++] = cp1;
1324 }
1325 if (cp2) {
1326 for (i = 0; i < k; ++i) {
1327 if (list[i] == cp2)
1328 goto skip1;
1329 }
1330 hfs_unlock(cp2);
1331 list[k++] = cp2;
1332 }
1333skip1:
1334 if (cp3) {
1335 for (i = 0; i < k; ++i) {
1336 if (list[i] == cp3)
1337 goto skip2;
1338 }
1339 hfs_unlock(cp3);
1340 list[k++] = cp3;
1341 }
1342skip2:
1343 if (cp4) {
1344 for (i = 0; i < k; ++i) {
1345 if (list[i] == cp4)
1346 return;
1347 }
1348 hfs_unlock(cp4);
1349 }
1350}
1351
1352
1353/*
1354 * Protect a cnode against a truncation.
1355 *
1356 * Used mainly by read/write since they don't hold the
1357 * cnode lock across calls to the cluster layer.
1358 *
1359 * The process doing a truncation must take the lock
1360 * exclusive. The read/write processes can take it
1361 * non-exclusive.
1362 */
1363__private_extern__
1364void
1365hfs_lock_truncate(struct cnode *cp, int exclusive)
1366{
2d21ac55 1367#ifdef HFS_CHECK_LOCK_ORDER
91447636 1368 if (cp->c_lockowner == current_thread())
2d21ac55
A
1369 panic("hfs_lock_truncate: cnode %p locked!", cp);
1370#endif /* HFS_CHECK_LOCK_ORDER */
91447636
A
1371
1372 if (exclusive)
1373 lck_rw_lock_exclusive(&cp->c_truncatelock);
1374 else
1375 lck_rw_lock_shared(&cp->c_truncatelock);
1376}
1377
1378__private_extern__
1379void
2d21ac55 1380hfs_unlock_truncate(struct cnode *cp, int exclusive)
91447636 1381{
2d21ac55
A
1382 if (exclusive) {
1383 lck_rw_unlock_exclusive(&cp->c_truncatelock);
1384 } else {
1385 lck_rw_unlock_shared(&cp->c_truncatelock);
1386 }
91447636
A
1387}
1388
1389
1390
1391