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