]> git.saurik.com Git - apple/xnu.git/blame_incremental - bsd/hfs/hfs_cnode.c
xnu-792.24.17.tar.gz
[apple/xnu.git] / bsd / hfs / hfs_cnode.c
... / ...
CommitLineData
1/*
2 * Copyright (c) 2002-2005 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License"). You may not use this file except in compliance with the
9 * License. Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
11 *
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
18 * under the License.
19 *
20 * @APPLE_LICENSE_HEADER_END@
21 */
22#include <sys/param.h>
23#include <sys/systm.h>
24#include <sys/proc.h>
25#include <sys/vnode.h>
26#include <sys/mount.h>
27#include <sys/kernel.h>
28#include <sys/malloc.h>
29#include <sys/time.h>
30#include <sys/ubc.h>
31#include <sys/quota.h>
32#include <sys/kdebug.h>
33
34#include <kern/locks.h>
35
36#include <miscfs/specfs/specdev.h>
37#include <miscfs/fifofs/fifo.h>
38
39#include <hfs/hfs.h>
40#include <hfs/hfs_catalog.h>
41#include <hfs/hfs_cnode.h>
42#include <hfs/hfs_quota.h>
43
44extern int prtactive;
45
46extern lck_attr_t * hfs_lock_attr;
47extern lck_grp_t * hfs_mutex_group;
48extern lck_grp_t * hfs_rwlock_group;
49
50static int hfs_filedone(struct vnode *vp, vfs_context_t context);
51
52static void hfs_reclaim_cnode(struct cnode *);
53
54static int hfs_valid_cnode(struct hfsmount *, struct vnode *, struct componentname *, cnid_t);
55
56static int hfs_isordered(struct cnode *, struct cnode *);
57
58int hfs_vnop_inactive(struct vnop_inactive_args *);
59
60int hfs_vnop_reclaim(struct vnop_reclaim_args *);
61
62
63/*
64 * Last reference to an cnode. If necessary, write or delete it.
65 */
66__private_extern__
67int
68hfs_vnop_inactive(struct vnop_inactive_args *ap)
69{
70 struct vnode *vp = ap->a_vp;
71 struct cnode *cp;
72 struct hfsmount *hfsmp = VTOHFS(vp);
73 struct proc *p = vfs_context_proc(ap->a_context);
74 int error = 0;
75 int recycle = 0;
76 int forkcount = 0;
77 int truncated = 0;
78 int started_tr = 0;
79 int took_trunc_lock = 0;
80 cat_cookie_t cookie;
81 int cat_reserve = 0;
82 int lockflags;
83 enum vtype v_type;
84
85 v_type = vnode_vtype(vp);
86 cp = VTOC(vp);
87
88 if ((hfsmp->hfs_flags & HFS_READ_ONLY) || vnode_issystem(vp) ||
89 (hfsmp->hfs_freezing_proc == p)) {
90 return (0);
91 }
92
93 /*
94 * Ignore nodes related to stale file handles.
95 */
96 if (cp->c_mode == 0) {
97 vnode_recycle(vp);
98 return (0);
99 }
100
101 if ((v_type == VREG) &&
102 (ISSET(cp->c_flag, C_DELETED) || VTOF(vp)->ff_blocks)) {
103 hfs_lock_truncate(cp, TRUE);
104 took_trunc_lock = 1;
105 }
106
107 /*
108 * We do the ubc_setsize before we take the cnode
109 * lock and before the hfs_truncate (since we'll
110 * be inside a transaction).
111 */
112 if ((v_type == VREG || v_type == VLNK) &&
113 (cp->c_flag & C_DELETED) &&
114 (VTOF(vp)->ff_blocks != 0)) {
115 ubc_setsize(vp, 0);
116 }
117
118 (void) hfs_lock(cp, HFS_FORCE_LOCK);
119
120 if (v_type == VREG && !ISSET(cp->c_flag, C_DELETED) && VTOF(vp)->ff_blocks) {
121 hfs_filedone(vp, ap->a_context);
122 }
123 /*
124 * Remove any directory hints
125 */
126 if (v_type == VDIR)
127 hfs_reldirhints(cp, 0);
128
129 if (cp->c_datafork)
130 ++forkcount;
131 if (cp->c_rsrcfork)
132 ++forkcount;
133
134 /* If needed, get rid of any fork's data for a deleted file */
135 if ((v_type == VREG || v_type == VLNK) && (cp->c_flag & C_DELETED)) {
136 if (VTOF(vp)->ff_blocks != 0) {
137 // start the transaction out here so that
138 // the truncate and the removal of the file
139 // are all in one transaction. otherwise
140 // because this cnode is marked for deletion
141 // the truncate won't cause the catalog entry
142 // to get updated which means that we could
143 // free blocks but still keep a reference to
144 // them in the catalog entry and then double
145 // free them later.
146 //
147// if (hfs_start_transaction(hfsmp) != 0) {
148// error = EINVAL;
149// goto out;
150// }
151// started_tr = 1;
152
153 /*
154 * Since we're already inside a transaction,
155 * tell hfs_truncate to skip the ubc_setsize.
156 */
157 error = hfs_truncate(vp, (off_t)0, IO_NDELAY, 1, ap->a_context);
158 if (error)
159 goto out;
160 truncated = 1;
161 }
162 recycle = 1;
163 }
164
165 /*
166 * Check for a postponed deletion.
167 * (only delete cnode when the last fork goes inactive)
168 */
169 if ((cp->c_flag & C_DELETED) && (forkcount <= 1)) {
170 /*
171 * Mark cnode in transit so that no one can get this
172 * cnode from cnode hash.
173 */
174 hfs_chash_mark_in_transit(cp);
175
176 cp->c_flag &= ~C_DELETED;
177 cp->c_flag |= C_NOEXISTS; // XXXdbg
178 cp->c_rdev = 0;
179
180 if (started_tr == 0) {
181 if (hfs_start_transaction(hfsmp) != 0) {
182 error = EINVAL;
183 goto out;
184 }
185 started_tr = 1;
186 }
187
188 /*
189 * Reserve some space in the Catalog file.
190 */
191 if ((error = cat_preflight(hfsmp, CAT_DELETE, &cookie, p))) {
192 goto out;
193 }
194 cat_reserve = 1;
195
196 lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG | SFL_ATTRIBUTE, HFS_EXCLUSIVE_LOCK);
197
198 if (cp->c_blocks > 0)
199 printf("hfs_inactive: attempting to delete a non-empty file!");
200
201
202 //
203 // release the name pointer in the descriptor so that
204 // cat_delete() will use the file-id to do the deletion.
205 // in the case of hard links this is imperative (in the
206 // case of regular files the fileid and cnid are the
207 // same so it doesn't matter).
208 //
209 cat_releasedesc(&cp->c_desc);
210
211 /*
212 * The descriptor name may be zero,
213 * in which case the fileid is used.
214 */
215 error = cat_delete(hfsmp, &cp->c_desc, &cp->c_attr);
216
217 if (error && truncated && (error != ENXIO))
218 printf("hfs_inactive: couldn't delete a truncated file!");
219
220 /* Update HFS Private Data dir */
221 if (error == 0) {
222 hfsmp->hfs_privdir_attr.ca_entries--;
223 (void)cat_update(hfsmp, &hfsmp->hfs_privdir_desc,
224 &hfsmp->hfs_privdir_attr, NULL, NULL);
225 }
226
227 if (error == 0) {
228 /* Delete any attributes, ignore errors */
229 (void) hfs_removeallattr(hfsmp, cp->c_fileid);
230 }
231
232 hfs_systemfile_unlock(hfsmp, lockflags);
233
234 if (error)
235 goto out;
236
237#if QUOTA
238 (void)hfs_chkiq(cp, -1, NOCRED, 0);
239#endif /* QUOTA */
240
241 cp->c_mode = 0;
242 cp->c_flag |= C_NOEXISTS;
243 cp->c_touch_chgtime = TRUE;
244 cp->c_touch_modtime = TRUE;
245
246 if (error == 0)
247 hfs_volupdate(hfsmp, VOL_RMFILE, 0);
248 }
249
250 if ((cp->c_flag & C_MODIFIED) ||
251 cp->c_touch_acctime || cp->c_touch_chgtime || cp->c_touch_modtime) {
252 hfs_update(vp, 0);
253 }
254out:
255 if (cat_reserve)
256 cat_postflight(hfsmp, &cookie, p);
257
258 // XXXdbg - have to do this because a goto could have come here
259 if (started_tr) {
260 hfs_end_transaction(hfsmp);
261 started_tr = 0;
262 }
263
264 hfs_unlock(cp);
265
266 if (took_trunc_lock)
267 hfs_unlock_truncate(cp);
268
269 /*
270 * If we are done with the vnode, reclaim it
271 * so that it can be reused immediately.
272 */
273 if (cp->c_mode == 0 || recycle)
274 vnode_recycle(vp);
275
276 return (error);
277}
278
279/*
280 * File clean-up (zero fill and shrink peof).
281 */
282static int
283hfs_filedone(struct vnode *vp, vfs_context_t context)
284{
285 struct cnode *cp;
286 struct filefork *fp;
287 struct hfsmount *hfsmp;
288 off_t leof;
289 u_long blks, blocksize;
290
291 cp = VTOC(vp);
292 fp = VTOF(vp);
293 hfsmp = VTOHFS(vp);
294 leof = fp->ff_size;
295
296 if ((hfsmp->hfs_flags & HFS_READ_ONLY) || (fp->ff_blocks == 0))
297 return (0);
298
299 hfs_unlock(cp);
300 (void) cluster_push(vp, IO_CLOSE);
301 hfs_lock(cp, HFS_FORCE_LOCK);
302
303 /*
304 * Explicitly zero out the areas of file
305 * that are currently marked invalid.
306 */
307 while (!CIRCLEQ_EMPTY(&fp->ff_invalidranges)) {
308 struct rl_entry *invalid_range = CIRCLEQ_FIRST(&fp->ff_invalidranges);
309 off_t start = invalid_range->rl_start;
310 off_t end = invalid_range->rl_end;
311
312 /* The range about to be written must be validated
313 * first, so that VNOP_BLOCKMAP() will return the
314 * appropriate mapping for the cluster code:
315 */
316 rl_remove(start, end, &fp->ff_invalidranges);
317
318 hfs_unlock(cp);
319 (void) cluster_write(vp, (struct uio *) 0,
320 leof, end + 1, start, (off_t)0,
321 IO_HEADZEROFILL | IO_NOZERODIRTY | IO_NOCACHE);
322 hfs_lock(cp, HFS_FORCE_LOCK);
323 cp->c_flag |= C_MODIFIED;
324 }
325 cp->c_flag &= ~C_ZFWANTSYNC;
326 cp->c_zftimeout = 0;
327 blocksize = VTOVCB(vp)->blockSize;
328 blks = leof / blocksize;
329 if (((off_t)blks * (off_t)blocksize) != leof)
330 blks++;
331 /*
332 * Shrink the peof to the smallest size neccessary to contain the leof.
333 */
334 if (blks < fp->ff_blocks)
335 (void) hfs_truncate(vp, leof, IO_NDELAY, 0, context);
336 hfs_unlock(cp);
337 (void) cluster_push(vp, IO_CLOSE);
338 hfs_lock(cp, HFS_FORCE_LOCK);
339
340 /*
341 * If the hfs_truncate didn't happen to flush the vnode's
342 * information out to disk, force it to be updated now that
343 * all invalid ranges have been zero-filled and validated:
344 */
345 if (cp->c_flag & C_MODIFIED) {
346 hfs_update(vp, 0);
347 }
348 return (0);
349}
350
351
352/*
353 * Reclaim a cnode so that it can be used for other purposes.
354 */
355__private_extern__
356int
357hfs_vnop_reclaim(struct vnop_reclaim_args *ap)
358{
359 struct vnode *vp = ap->a_vp;
360 struct cnode *cp;
361 struct filefork *fp = NULL;
362 struct filefork *altfp = NULL;
363 int reclaim_cnode = 0;
364
365 (void) hfs_lock(VTOC(vp), HFS_FORCE_LOCK);
366 cp = VTOC(vp);
367
368 /*
369 * Keep track of an inactive hot file.
370 */
371 if (!vnode_isdir(vp) && !vnode_issystem(vp))
372 (void) hfs_addhotfile(vp);
373
374 vnode_removefsref(vp);
375
376 /*
377 * Find file fork for this vnode (if any)
378 * Also check if another fork is active
379 */
380 if (cp->c_vp == vp) {
381 fp = cp->c_datafork;
382 altfp = cp->c_rsrcfork;
383
384 cp->c_datafork = NULL;
385 cp->c_vp = NULL;
386 } else if (cp->c_rsrc_vp == vp) {
387 fp = cp->c_rsrcfork;
388 altfp = cp->c_datafork;
389
390 cp->c_rsrcfork = NULL;
391 cp->c_rsrc_vp = NULL;
392 } else {
393 panic("hfs_vnop_reclaim: vp points to wrong cnode\n");
394 }
395 /*
396 * On the last fork, remove the cnode from its hash chain.
397 */
398 if (altfp == NULL) {
399 /* If we can't remove it then the cnode must persist! */
400 if (hfs_chashremove(cp) == 0)
401 reclaim_cnode = 1;
402 /*
403 * Remove any directory hints
404 */
405 if (vnode_isdir(vp)) {
406 hfs_reldirhints(cp, 0);
407 }
408 }
409 /* Release the file fork and related data */
410 if (fp) {
411 /* Dump cached symlink data */
412 if (vnode_islnk(vp) && (fp->ff_symlinkptr != NULL)) {
413 FREE(fp->ff_symlinkptr, M_TEMP);
414 }
415 FREE_ZONE(fp, sizeof(struct filefork), M_HFSFORK);
416 }
417
418 /*
419 * If there was only one active fork then we can release the cnode.
420 */
421 if (reclaim_cnode) {
422 hfs_chashwakeup(cp, H_ALLOC | H_TRANSIT);
423 hfs_reclaim_cnode(cp);
424 } else /* cnode in use */ {
425 hfs_unlock(cp);
426 }
427
428 vnode_clearfsnode(vp);
429 return (0);
430}
431
432
433extern int (**hfs_vnodeop_p) (void *);
434extern int (**hfs_specop_p) (void *);
435extern int (**hfs_fifoop_p) (void *);
436
437/*
438 * hfs_getnewvnode - get new default vnode
439 *
440 * The vnode is returned with an iocount and the cnode locked
441 */
442__private_extern__
443int
444hfs_getnewvnode(
445 struct hfsmount *hfsmp,
446 struct vnode *dvp,
447 struct componentname *cnp,
448 struct cat_desc *descp,
449 int wantrsrc,
450 struct cat_attr *attrp,
451 struct cat_fork *forkp,
452 struct vnode **vpp)
453{
454 struct mount *mp = HFSTOVFS(hfsmp);
455 struct vnode *vp = NULL;
456 struct vnode **cvpp;
457 struct vnode *tvp = NULLVP;
458 struct cnode *cp = NULL;
459 struct filefork *fp = NULL;
460 int i;
461 int retval;
462 int issystemfile;
463 struct vnode_fsparam vfsp;
464 enum vtype vtype;
465
466 if (attrp->ca_fileid == 0) {
467 *vpp = NULL;
468 return (ENOENT);
469 }
470
471#if !FIFO
472 if (IFTOVT(attrp->ca_mode) == VFIFO) {
473 *vpp = NULL;
474 return (ENOTSUP);
475 }
476#endif
477 vtype = IFTOVT(attrp->ca_mode);
478 issystemfile = (descp->cd_flags & CD_ISMETA) && (vtype == VREG);
479
480 /*
481 * Get a cnode (new or existing)
482 * skip getting the cnode lock if we are getting resource fork (wantrsrc == 2)
483 */
484 cp = hfs_chash_getcnode(hfsmp->hfs_raw_dev, attrp->ca_fileid, vpp, wantrsrc, (wantrsrc == 2));
485
486 /* Hardlinks may need an updated catalog descriptor */
487 if ((cp->c_flag & C_HARDLINK) && descp->cd_nameptr && descp->cd_namelen > 0) {
488 replace_desc(cp, descp);
489 }
490 /* Check if we found a matching vnode */
491 if (*vpp != NULL)
492 return (0);
493
494 /*
495 * If this is a new cnode then initialize it.
496 */
497 if (ISSET(cp->c_hflag, H_ALLOC)) {
498 lck_rw_init(&cp->c_truncatelock, hfs_rwlock_group, hfs_lock_attr);
499
500 /* Make sure its still valid (ie exists on disk). */
501 if (!hfs_valid_cnode(hfsmp, dvp, (wantrsrc ? NULL : cnp), cp->c_fileid)) {
502 hfs_chash_abort(cp);
503 hfs_reclaim_cnode(cp);
504 *vpp = NULL;
505 return (ENOENT);
506 }
507 bcopy(attrp, &cp->c_attr, sizeof(struct cat_attr));
508 bcopy(descp, &cp->c_desc, sizeof(struct cat_desc));
509
510 /* The name was inherited so clear descriptor state... */
511 descp->cd_namelen = 0;
512 descp->cd_nameptr = NULL;
513 descp->cd_flags &= ~CD_HASBUF;
514
515 /* Tag hardlinks */
516 if (IFTOVT(cp->c_mode) == VREG &&
517 (descp->cd_cnid != attrp->ca_fileid)) {
518 cp->c_flag |= C_HARDLINK;
519 }
520
521 /* Take one dev reference for each non-directory cnode */
522 if (IFTOVT(cp->c_mode) != VDIR) {
523 cp->c_devvp = hfsmp->hfs_devvp;
524 vnode_ref(cp->c_devvp);
525 }
526#if QUOTA
527 for (i = 0; i < MAXQUOTAS; i++)
528 cp->c_dquot[i] = NODQUOT;
529#endif /* QUOTA */
530 }
531
532 if (IFTOVT(cp->c_mode) == VDIR) {
533 if (cp->c_vp != NULL)
534 panic("hfs_getnewvnode: orphaned vnode (data)");
535 cvpp = &cp->c_vp;
536 } else {
537 if (forkp && attrp->ca_blocks < forkp->cf_blocks)
538 panic("hfs_getnewvnode: bad ca_blocks (too small)");
539 /*
540 * Allocate and initialize a file fork...
541 */
542 MALLOC_ZONE(fp, struct filefork *, sizeof(struct filefork),
543 M_HFSFORK, M_WAITOK);
544 fp->ff_cp = cp;
545 if (forkp)
546 bcopy(forkp, &fp->ff_data, sizeof(struct cat_fork));
547 else
548 bzero(&fp->ff_data, sizeof(struct cat_fork));
549 rl_init(&fp->ff_invalidranges);
550 fp->ff_sysfileinfo = 0;
551
552 if (wantrsrc) {
553 if (cp->c_rsrcfork != NULL)
554 panic("hfs_getnewvnode: orphaned rsrc fork");
555 if (cp->c_rsrc_vp != NULL)
556 panic("hfs_getnewvnode: orphaned vnode (rsrc)");
557 cp->c_rsrcfork = fp;
558 cvpp = &cp->c_rsrc_vp;
559 if ( (tvp = cp->c_vp) != NULLVP )
560 cp->c_flag |= C_NEED_DVNODE_PUT;
561 } else {
562 if (cp->c_datafork != NULL)
563 panic("hfs_getnewvnode: orphaned data fork");
564 if (cp->c_vp != NULL)
565 panic("hfs_getnewvnode: orphaned vnode (data)");
566 cp->c_datafork = fp;
567 cvpp = &cp->c_vp;
568 if ( (tvp = cp->c_rsrc_vp) != NULLVP)
569 cp->c_flag |= C_NEED_RVNODE_PUT;
570 }
571 }
572 if (tvp != NULLVP) {
573 /*
574 * grab an iocount on the vnode we weren't
575 * interested in (i.e. we want the resource fork
576 * but the cnode already has the data fork)
577 * to prevent it from being
578 * recycled by us when we call vnode_create
579 * which will result in a deadlock when we
580 * try to take the cnode lock in hfs_vnop_fsync or
581 * hfs_vnop_reclaim... vnode_get can be called here
582 * because we already hold the cnode lock which will
583 * prevent the vnode from changing identity until
584 * we drop it.. vnode_get will not block waiting for
585 * a change of state... however, it will return an
586 * error if the current iocount == 0 and we've already
587 * started to terminate the vnode... we don't need/want to
588 * grab an iocount in the case since we can't cause
589 * the fileystem to be re-entered on this thread for this vp
590 *
591 * the matching vnode_put will happen in hfs_unlock
592 * after we've dropped the cnode lock
593 */
594 if ( vnode_get(tvp) != 0)
595 cp->c_flag &= ~(C_NEED_RVNODE_PUT | C_NEED_DVNODE_PUT);
596 }
597 vfsp.vnfs_mp = mp;
598 vfsp.vnfs_vtype = vtype;
599 vfsp.vnfs_str = "hfs";
600 vfsp.vnfs_dvp = dvp;
601 vfsp.vnfs_fsnode = cp;
602 vfsp.vnfs_cnp = cnp;
603 if (vtype == VFIFO )
604 vfsp.vnfs_vops = hfs_fifoop_p;
605 else if (vtype == VBLK || vtype == VCHR)
606 vfsp.vnfs_vops = hfs_specop_p;
607 else
608 vfsp.vnfs_vops = hfs_vnodeop_p;
609
610 if (vtype == VBLK || vtype == VCHR)
611 vfsp.vnfs_rdev = attrp->ca_rdev;
612 else
613 vfsp.vnfs_rdev = 0;
614
615 if (forkp)
616 vfsp.vnfs_filesize = forkp->cf_size;
617 else
618 vfsp.vnfs_filesize = 0;
619
620 if (dvp && cnp && (cnp->cn_flags & MAKEENTRY))
621 vfsp.vnfs_flags = 0;
622 else
623 vfsp.vnfs_flags = VNFS_NOCACHE;
624
625 /* Tag system files */
626 vfsp.vnfs_marksystem = issystemfile;
627
628 /* Tag root directory */
629 if (descp->cd_cnid == kHFSRootFolderID)
630 vfsp.vnfs_markroot = 1;
631 else
632 vfsp.vnfs_markroot = 0;
633
634 if ((retval = vnode_create(VNCREATE_FLAVOR, VCREATESIZE, &vfsp, cvpp))) {
635 if (fp) {
636 if (fp == cp->c_datafork)
637 cp->c_datafork = NULL;
638 else
639 cp->c_rsrcfork = NULL;
640
641 FREE_ZONE(fp, sizeof(struct filefork), M_HFSFORK);
642 }
643 /*
644 * If this is a newly created cnode or a vnode reclaim
645 * occurred during the attachment, then cleanup the cnode.
646 */
647 if ((cp->c_vp == NULL) && (cp->c_rsrc_vp == NULL)) {
648 hfs_chash_abort(cp);
649 hfs_reclaim_cnode(cp);
650 } else {
651 hfs_chashwakeup(cp, H_ALLOC | H_ATTACH);
652 hfs_unlock(cp);
653 }
654 *vpp = NULL;
655 return (retval);
656 }
657 vp = *cvpp;
658 vnode_addfsref(vp);
659 vnode_settag(vp, VT_HFS);
660 if (cp->c_flag & C_HARDLINK)
661 vnode_set_hard_link(vp);
662 hfs_chashwakeup(cp, H_ALLOC | H_ATTACH);
663
664 /*
665 * Stop tracking an active hot file.
666 */
667 if (!vnode_isdir(vp) && !vnode_issystem(vp))
668 (void) hfs_removehotfile(vp);
669
670 *vpp = vp;
671 return (0);
672}
673
674
675static void
676hfs_reclaim_cnode(struct cnode *cp)
677{
678#if QUOTA
679 int i;
680
681 for (i = 0; i < MAXQUOTAS; i++) {
682 if (cp->c_dquot[i] != NODQUOT) {
683 dqreclaim(cp->c_dquot[i]);
684 cp->c_dquot[i] = NODQUOT;
685 }
686 }
687#endif /* QUOTA */
688
689 if (cp->c_devvp) {
690 struct vnode *tmp_vp = cp->c_devvp;
691
692 cp->c_devvp = NULL;
693 vnode_rele(tmp_vp);
694 }
695
696 /*
697 * If the descriptor has a name then release it
698 */
699 if (cp->c_desc.cd_flags & CD_HASBUF) {
700 char *nameptr;
701
702 nameptr = cp->c_desc.cd_nameptr;
703 cp->c_desc.cd_nameptr = 0;
704 cp->c_desc.cd_flags &= ~CD_HASBUF;
705 cp->c_desc.cd_namelen = 0;
706 vfs_removename(nameptr);
707 }
708
709 lck_rw_destroy(&cp->c_rwlock, hfs_rwlock_group);
710 lck_rw_destroy(&cp->c_truncatelock, hfs_rwlock_group);
711 bzero(cp, sizeof(struct cnode));
712 FREE_ZONE(cp, sizeof(struct cnode), M_HFSNODE);
713}
714
715
716static int
717hfs_valid_cnode(struct hfsmount *hfsmp, struct vnode *dvp, struct componentname *cnp, cnid_t cnid)
718{
719 struct cat_attr attr;
720 struct cat_desc cndesc;
721 int stillvalid = 0;
722 int lockflags;
723
724 /* System files are always valid */
725 if (cnid < kHFSFirstUserCatalogNodeID)
726 return (1);
727
728 /* XXX optimization: check write count in dvp */
729
730 lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_SHARED_LOCK);
731
732 if (dvp && cnp) {
733 bzero(&cndesc, sizeof(cndesc));
734 cndesc.cd_nameptr = cnp->cn_nameptr;
735 cndesc.cd_namelen = cnp->cn_namelen;
736 cndesc.cd_parentcnid = VTOC(dvp)->c_cnid;
737 cndesc.cd_hint = VTOC(dvp)->c_childhint;
738
739 if ((cat_lookup(hfsmp, &cndesc, 0, NULL, &attr, NULL, NULL) == 0) &&
740 (cnid == attr.ca_fileid)) {
741 stillvalid = 1;
742 }
743 } else {
744 if (cat_idlookup(hfsmp, cnid, NULL, NULL, NULL) == 0) {
745 stillvalid = 1;
746 }
747 }
748 hfs_systemfile_unlock(hfsmp, lockflags);
749
750 return (stillvalid);
751}
752
753/*
754 * Touch cnode times based on c_touch_xxx flags
755 *
756 * cnode must be locked exclusive
757 *
758 * This will also update the volume modify time
759 */
760__private_extern__
761void
762hfs_touchtimes(struct hfsmount *hfsmp, struct cnode* cp)
763{
764 /* HFS Standard doesn't support access times */
765 if (hfsmp->hfs_flags & HFS_STANDARD) {
766 cp->c_touch_acctime = FALSE;
767 }
768
769 if (cp->c_touch_acctime || cp->c_touch_chgtime || cp->c_touch_modtime) {
770 struct timeval tv;
771 int touchvol = 0;
772
773 microtime(&tv);
774
775 if (cp->c_touch_acctime) {
776 cp->c_atime = tv.tv_sec;
777 /*
778 * When the access time is the only thing changing
779 * then make sure its sufficiently newer before
780 * committing it to disk.
781 */
782 if ((((u_int32_t)cp->c_atime - (u_int32_t)(cp)->c_attr.ca_atimeondisk) >
783 ATIME_ONDISK_ACCURACY)) {
784 cp->c_flag |= C_MODIFIED;
785 }
786 cp->c_touch_acctime = FALSE;
787 }
788 if (cp->c_touch_modtime) {
789 cp->c_mtime = tv.tv_sec;
790 cp->c_touch_modtime = FALSE;
791 cp->c_flag |= C_MODIFIED;
792 touchvol = 1;
793#if 1
794 /*
795 * HFS dates that WE set must be adjusted for DST
796 */
797 if ((hfsmp->hfs_flags & HFS_STANDARD) && gTimeZone.tz_dsttime) {
798 cp->c_mtime += 3600;
799 }
800#endif
801 }
802 if (cp->c_touch_chgtime) {
803 cp->c_ctime = tv.tv_sec;
804 cp->c_touch_chgtime = FALSE;
805 cp->c_flag |= C_MODIFIED;
806 touchvol = 1;
807 }
808
809 /* Touch the volume modtime if needed */
810 if (touchvol) {
811 HFSTOVCB(hfsmp)->vcbFlags |= 0xFF00;
812 HFSTOVCB(hfsmp)->vcbLsMod = tv.tv_sec;
813 }
814 }
815}
816
817/*
818 * Lock a cnode.
819 */
820__private_extern__
821int
822hfs_lock(struct cnode *cp, enum hfslocktype locktype)
823{
824 void * thread = current_thread();
825
826 /* System files need to keep track of owner */
827 if ((cp->c_fileid < kHFSFirstUserCatalogNodeID) &&
828 (cp->c_fileid > kHFSRootFolderID) &&
829 (locktype != HFS_SHARED_LOCK)) {
830
831 /*
832 * The extents and bitmap file locks support
833 * recursion and are always taken exclusive.
834 */
835 if (cp->c_fileid == kHFSExtentsFileID ||
836 cp->c_fileid == kHFSAllocationFileID) {
837 if (cp->c_lockowner == thread) {
838 cp->c_syslockcount++;
839 } else {
840 lck_rw_lock_exclusive(&cp->c_rwlock);
841 cp->c_lockowner = thread;
842 cp->c_syslockcount = 1;
843 }
844 } else {
845 lck_rw_lock_exclusive(&cp->c_rwlock);
846 cp->c_lockowner = thread;
847 }
848 } else if (locktype == HFS_SHARED_LOCK) {
849 lck_rw_lock_shared(&cp->c_rwlock);
850 cp->c_lockowner = HFS_SHARED_OWNER;
851 } else {
852 lck_rw_lock_exclusive(&cp->c_rwlock);
853 cp->c_lockowner = thread;
854 }
855 /*
856 * Skip cnodes that no longer exist (were deleted).
857 */
858 if ((locktype != HFS_FORCE_LOCK) &&
859 ((cp->c_desc.cd_flags & CD_ISMETA) == 0) &&
860 (cp->c_flag & C_NOEXISTS)) {
861 hfs_unlock(cp);
862 return (ENOENT);
863 }
864 return (0);
865}
866
867/*
868 * Lock a pair of cnodes.
869 */
870__private_extern__
871int
872hfs_lockpair(struct cnode *cp1, struct cnode *cp2, enum hfslocktype locktype)
873{
874 struct cnode *first, *last;
875 int error;
876
877 /*
878 * If cnodes match then just lock one.
879 */
880 if (cp1 == cp2) {
881 return hfs_lock(cp1, locktype);
882 }
883
884 /*
885 * Lock in cnode parent-child order (if there is a relationship);
886 * otherwise lock in cnode address order.
887 */
888 if ((IFTOVT(cp1->c_mode) == VDIR) && (cp1->c_fileid == cp2->c_parentcnid)) {
889 first = cp1;
890 last = cp2;
891 } else if (cp1 < cp2) {
892 first = cp1;
893 last = cp2;
894 } else {
895 first = cp2;
896 last = cp1;
897 }
898
899 if ( (error = hfs_lock(first, locktype))) {
900 return (error);
901 }
902 if ( (error = hfs_lock(last, locktype))) {
903 hfs_unlock(first);
904 return (error);
905 }
906 return (0);
907}
908
909/*
910 * Check ordering of two cnodes. Return true if they are are in-order.
911 */
912static int
913hfs_isordered(struct cnode *cp1, struct cnode *cp2)
914{
915 if (cp1 == cp2)
916 return (0);
917 if (cp1 == NULL || cp2 == (struct cnode *)0xffffffff)
918 return (1);
919 if (cp2 == NULL || cp1 == (struct cnode *)0xffffffff)
920 return (0);
921 if (cp1->c_fileid == cp2->c_parentcnid)
922 return (1); /* cp1 is the parent and should go first */
923 if (cp2->c_fileid == cp1->c_parentcnid)
924 return (0); /* cp1 is the child and should go last */
925
926 return (cp1 < cp2); /* fall-back is to use address order */
927}
928
929/*
930 * Acquire 4 cnode locks.
931 * - locked in cnode parent-child order (if there is a relationship)
932 * otherwise lock in cnode address order (lesser address first).
933 * - all or none of the locks are taken
934 * - only one lock taken per cnode (dup cnodes are skipped)
935 * - some of the cnode pointers may be null
936 */
937__private_extern__
938int
939hfs_lockfour(struct cnode *cp1, struct cnode *cp2, struct cnode *cp3,
940 struct cnode *cp4, enum hfslocktype locktype)
941{
942 struct cnode * a[3];
943 struct cnode * b[3];
944 struct cnode * list[4];
945 struct cnode * tmp;
946 int i, j, k;
947 int error;
948
949 if (hfs_isordered(cp1, cp2)) {
950 a[0] = cp1; a[1] = cp2;
951 } else {
952 a[0] = cp2; a[1] = cp1;
953 }
954 if (hfs_isordered(cp3, cp4)) {
955 b[0] = cp3; b[1] = cp4;
956 } else {
957 b[0] = cp4; b[1] = cp3;
958 }
959 a[2] = (struct cnode *)0xffffffff; /* sentinel value */
960 b[2] = (struct cnode *)0xffffffff; /* sentinel value */
961
962 /*
963 * Build the lock list, skipping over duplicates
964 */
965 for (i = 0, j = 0, k = 0; (i < 2 || j < 2); ) {
966 tmp = hfs_isordered(a[i], b[j]) ? a[i++] : b[j++];
967 if (k == 0 || tmp != list[k-1])
968 list[k++] = tmp;
969 }
970
971 /*
972 * Now we can lock using list[0 - k].
973 * Skip over NULL entries.
974 */
975 for (i = 0; i < k; ++i) {
976 if (list[i])
977 if ((error = hfs_lock(list[i], locktype))) {
978 /* Drop any locks we acquired. */
979 while (--i >= 0) {
980 if (list[i])
981 hfs_unlock(list[i]);
982 }
983 return (error);
984 }
985 }
986 return (0);
987}
988
989
990/*
991 * Unlock a cnode.
992 */
993__private_extern__
994void
995hfs_unlock(struct cnode *cp)
996{
997 vnode_t rvp = NULLVP;
998 vnode_t dvp = NULLVP;
999
1000 /* System files need to keep track of owner */
1001 if ((cp->c_fileid < kHFSFirstUserCatalogNodeID) &&
1002 (cp->c_fileid > kHFSRootFolderID) &&
1003 (cp->c_datafork != NULL)) {
1004 /*
1005 * The extents and bitmap file locks support
1006 * recursion and are always taken exclusive.
1007 */
1008 if (cp->c_fileid == kHFSExtentsFileID ||
1009 cp->c_fileid == kHFSAllocationFileID) {
1010 if (--cp->c_syslockcount > 0) {
1011 return;
1012 }
1013 }
1014 }
1015 if (cp->c_flag & C_NEED_DVNODE_PUT)
1016 dvp = cp->c_vp;
1017
1018 if (cp->c_flag & C_NEED_RVNODE_PUT)
1019 rvp = cp->c_rsrc_vp;
1020
1021 cp->c_flag &= ~(C_NEED_DVNODE_PUT | C_NEED_RVNODE_PUT);
1022
1023 cp-> c_lockowner = NULL;
1024 lck_rw_done(&cp->c_rwlock);
1025
1026 if (dvp)
1027 vnode_put(dvp);
1028 if (rvp)
1029 vnode_put(rvp);
1030}
1031
1032/*
1033 * Unlock a pair of cnodes.
1034 */
1035__private_extern__
1036void
1037hfs_unlockpair(struct cnode *cp1, struct cnode *cp2)
1038{
1039 hfs_unlock(cp1);
1040 if (cp2 != cp1)
1041 hfs_unlock(cp2);
1042}
1043
1044/*
1045 * Unlock a group of cnodes.
1046 */
1047__private_extern__
1048void
1049hfs_unlockfour(struct cnode *cp1, struct cnode *cp2, struct cnode *cp3, struct cnode *cp4)
1050{
1051 struct cnode * list[4];
1052 int i, k = 0;
1053
1054 if (cp1) {
1055 hfs_unlock(cp1);
1056 list[k++] = cp1;
1057 }
1058 if (cp2) {
1059 for (i = 0; i < k; ++i) {
1060 if (list[i] == cp2)
1061 goto skip1;
1062 }
1063 hfs_unlock(cp2);
1064 list[k++] = cp2;
1065 }
1066skip1:
1067 if (cp3) {
1068 for (i = 0; i < k; ++i) {
1069 if (list[i] == cp3)
1070 goto skip2;
1071 }
1072 hfs_unlock(cp3);
1073 list[k++] = cp3;
1074 }
1075skip2:
1076 if (cp4) {
1077 for (i = 0; i < k; ++i) {
1078 if (list[i] == cp4)
1079 return;
1080 }
1081 hfs_unlock(cp4);
1082 }
1083}
1084
1085
1086/*
1087 * Protect a cnode against a truncation.
1088 *
1089 * Used mainly by read/write since they don't hold the
1090 * cnode lock across calls to the cluster layer.
1091 *
1092 * The process doing a truncation must take the lock
1093 * exclusive. The read/write processes can take it
1094 * non-exclusive.
1095 */
1096__private_extern__
1097void
1098hfs_lock_truncate(struct cnode *cp, int exclusive)
1099{
1100 if (cp->c_lockowner == current_thread())
1101 panic("hfs_lock_truncate: cnode 0x%08x locked!", cp);
1102
1103 if (exclusive)
1104 lck_rw_lock_exclusive(&cp->c_truncatelock);
1105 else
1106 lck_rw_lock_shared(&cp->c_truncatelock);
1107}
1108
1109__private_extern__
1110void
1111hfs_unlock_truncate(struct cnode *cp)
1112{
1113 lck_rw_done(&cp->c_truncatelock);
1114}
1115
1116
1117
1118