]> git.saurik.com Git - apple/xnu.git/blob - bsd/hfs/hfs_cnode.c
65617595f965aaaac498e34fb142f5f71f4b0e9d
[apple/xnu.git] / bsd / hfs / hfs_cnode.c
1 /*
2 * Copyright (c) 2002 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/ubc.h>
30 #include <sys/quota.h>
31
32 #include <miscfs/specfs/specdev.h>
33 #include <miscfs/fifofs/fifo.h>
34
35 #include <hfs/hfs.h>
36 #include <hfs/hfs_catalog.h>
37 #include <hfs/hfs_cnode.h>
38 #include <hfs/hfs_quota.h>
39
40 extern int prtactive;
41
42
43 extern void hfs_relnamehints(struct cnode *dcp);
44
45
46 /*
47 * Last reference to an cnode. If necessary, write or delete it.
48 */
49 __private_extern__
50 int
51 hfs_inactive(ap)
52 struct vop_inactive_args /* {
53 struct vnode *a_vp;
54 } */ *ap;
55 {
56 struct vnode *vp = ap->a_vp;
57 struct cnode *cp = VTOC(vp);
58 struct hfsmount *hfsmp = VTOHFS(vp);
59 struct proc *p = ap->a_p;
60 struct timeval tv;
61 int error = 0;
62 int recycle = 0;
63 int forkcount = 0;
64 int truncated = 0;
65 int started_tr = 0, grabbed_lock = 0;
66
67 if (prtactive && vp->v_usecount != 0)
68 vprint("hfs_inactive: pushing active", vp);
69
70 /*
71 * Ignore nodes related to stale file handles.
72 */
73 if (cp->c_mode == 0)
74 goto out;
75
76 if (vp->v_mount->mnt_flag & MNT_RDONLY)
77 goto out;
78
79 if (cp->c_datafork)
80 ++forkcount;
81 if (cp->c_rsrcfork)
82 ++forkcount;
83
84 /* If needed, get rid of any fork's data for a deleted file */
85 if ((cp->c_flag & C_DELETED) &&
86 vp->v_type == VREG &&
87 (VTOF(vp)->ff_blocks != 0)) {
88 error = VOP_TRUNCATE(vp, (off_t)0, IO_NDELAY, NOCRED, p);
89 truncated = 1;
90 // have to do this to prevent the lost ubc_info panic
91 SET(cp->c_flag, C_TRANSIT);
92 recycle = 1;
93 if (error) goto out;
94 }
95
96 /*
97 * Check for a postponed deletion.
98 * (only delete cnode when the last fork goes inactive)
99 */
100 if ((cp->c_flag & C_DELETED) && (forkcount <= 1)) {
101 /*
102 * Mark cnode in transit so that one can get this
103 * cnode from cnode hash.
104 */
105 SET(cp->c_flag, C_TRANSIT);
106 cp->c_flag &= ~C_DELETED;
107 cp->c_rdev = 0;
108
109 // XXXdbg
110 hfs_global_shared_lock_acquire(hfsmp);
111 grabbed_lock = 1;
112 if (hfsmp->jnl) {
113 if (journal_start_transaction(hfsmp->jnl) != 0) {
114 error = EINVAL;
115 goto out;
116 }
117 started_tr = 1;
118 }
119
120 /* Lock catalog b-tree */
121 error = hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_EXCLUSIVE, p);
122 if (error) goto out;
123
124 if (cp->c_blocks > 0)
125 printf("hfs_inactive: attempting to delete a non-empty file!");
126
127 /*
128 * The descriptor name may be zero,
129 * in which case the fileid is used.
130 */
131 error = cat_delete(hfsmp, &cp->c_desc, &cp->c_attr);
132
133 if (error && truncated && (error != ENXIO))
134 printf("hfs_inactive: couldn't delete a truncated file!");
135
136 /* Update HFS Private Data dir */
137 if (error == 0) {
138 hfsmp->hfs_privdir_attr.ca_entries--;
139 (void)cat_update(hfsmp, &hfsmp->hfs_privdir_desc,
140 &hfsmp->hfs_privdir_attr, NULL, NULL);
141 }
142
143 /* Unlock catalog b-tree */
144 (void) hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_RELEASE, p);
145 if (error) goto out;
146
147 #if QUOTA
148 if (!hfs_getinoquota(cp))
149 (void)hfs_chkiq(cp, -1, NOCRED, 0);
150 #endif /* QUOTA */
151
152 cp->c_mode = 0;
153 cp->c_flag |= C_NOEXISTS | C_CHANGE | C_UPDATE;
154
155 if (error == 0)
156 hfs_volupdate(hfsmp, VOL_RMFILE, 0);
157 }
158
159 /* Push any defered access times to disk */
160 if (cp->c_flag & C_ATIMEMOD) {
161 cp->c_flag &= ~C_ATIMEMOD;
162 if (HFSTOVCB(hfsmp)->vcbSigWord == kHFSPlusSigWord)
163 cp->c_flag |= C_MODIFIED;
164 }
165
166 if (cp->c_flag & (C_ACCESS | C_CHANGE | C_MODIFIED | C_UPDATE)) {
167 tv = time;
168 VOP_UPDATE(vp, &tv, &tv, 0);
169 }
170 out:
171 // XXXdbg - have to do this because a goto could have come here
172 if (started_tr) {
173 journal_end_transaction(hfsmp->jnl);
174 started_tr = 0;
175 }
176 if (grabbed_lock) {
177 hfs_global_shared_lock_release(hfsmp);
178 }
179
180 VOP_UNLOCK(vp, 0, p);
181 /*
182 * If we are done with the vnode, reclaim it
183 * so that it can be reused immediately.
184 */
185 if (cp->c_mode == 0 || recycle)
186 vrecycle(vp, (struct slock *)0, p);
187
188 return (error);
189 }
190
191
192 /*
193 * Reclaim a cnode so that it can be used for other purposes.
194 */
195 __private_extern__
196 int
197 hfs_reclaim(ap)
198 struct vop_reclaim_args /* {
199 struct vnode *a_vp;
200 } */ *ap;
201 {
202 struct vnode *vp = ap->a_vp;
203 struct cnode *cp = VTOC(vp);
204 struct vnode *devvp = NULL;
205 struct filefork *fp = NULL;
206 struct filefork *altfp = NULL;
207 int i;
208
209 if (prtactive && vp->v_usecount != 0)
210 vprint("hfs_reclaim(): pushing active", vp);
211
212 devvp = cp->c_devvp; /* For later releasing */
213
214 /*
215 * Find file fork for this vnode (if any)
216 * Also check if another fork is active
217 */
218 if ((fp = cp->c_datafork) && (cp->c_vp == vp)) {
219 cp->c_datafork = NULL;
220 cp->c_vp = NULL;
221 altfp = cp->c_rsrcfork;
222 } else if ((fp = cp->c_rsrcfork) && (cp->c_rsrc_vp == vp)) {
223 cp->c_rsrcfork = NULL;
224 cp->c_rsrc_vp = NULL;
225 altfp = cp->c_datafork;
226 } else {
227 cp->c_vp = NULL;
228 fp = NULL;
229 altfp = NULL;
230 }
231
232 /*
233 * On the last fork, remove the cnode from its hash chain.
234 */
235 if (altfp == NULL)
236 hfs_chashremove(cp);
237
238 /* Release the file fork and related data (can block) */
239 if (fp) {
240 fp->ff_cp = NULL;
241 /* Dump cached symlink data */
242 if ((vp->v_type == VLNK) && (fp->ff_symlinkptr != NULL)) {
243 FREE(fp->ff_symlinkptr, M_TEMP);
244 fp->ff_symlinkptr = NULL;
245 }
246 FREE_ZONE(fp, sizeof(struct filefork), M_HFSFORK);
247 fp = NULL;
248 }
249
250 /*
251 * Purge old data structures associated with the cnode.
252 */
253 cache_purge(vp);
254 if (devvp && altfp == NULL) {
255 cp->c_devvp = NULL;
256 vrele(devvp);
257 }
258
259 vp->v_data = NULL;
260
261 /*
262 * If there was only one active fork then we can release the cnode.
263 */
264 if (altfp == NULL) {
265 #if QUOTA
266 for (i = 0; i < MAXQUOTAS; i++) {
267 if (cp->c_dquot[i] != NODQUOT) {
268 dqrele(vp, cp->c_dquot[i]);
269 cp->c_dquot[i] = NODQUOT;
270 }
271 }
272 #endif /* QUOTA */
273 /*
274 * Free any left over directory indices
275 */
276 if (vp->v_type == VDIR)
277 hfs_relnamehints(cp);
278
279 /*
280 * If the descriptor has a name then release it
281 */
282 if (cp->c_desc.cd_flags & CD_HASBUF) {
283 char *nameptr;
284
285 nameptr = cp->c_desc.cd_nameptr;
286 cp->c_desc.cd_nameptr = 0;
287 cp->c_desc.cd_flags &= ~CD_HASBUF;
288 cp->c_desc.cd_namelen = 0;
289 FREE(nameptr, M_TEMP);
290 }
291 CLR(cp->c_flag, (C_ALLOC | C_TRANSIT));
292 if (ISSET(cp->c_flag, C_WALLOC) || ISSET(cp->c_flag, C_WTRANSIT))
293 wakeup(cp);
294 FREE_ZONE(cp, sizeof(struct cnode), M_HFSNODE);
295
296 }
297
298 return (0);
299 }
300
301
302 /*
303 * get a cnode
304 *
305 * called by hfs_lookup and hfs_vget (descp == NULL)
306 *
307 * returns a locked vnode for cnode for given cnid/fileid
308 */
309 __private_extern__
310 int
311 hfs_getcnode(struct hfsmount *hfsmp, cnid_t cnid, struct cat_desc *descp, int wantrsrc,
312 struct cat_attr *attrp, struct cat_fork *forkp, struct vnode **vpp)
313 {
314 dev_t dev = hfsmp->hfs_raw_dev;
315 struct vnode *vp = NULL;
316 struct vnode *rvp = NULL;
317 struct vnode *new_vp = NULL;
318 struct cnode *cp = NULL;
319 struct proc *p = current_proc();
320 int retval = E_NONE;
321
322 /* Check if unmount in progress */
323 if (HFSTOVFS(hfsmp)->mnt_kern_flag & MNTK_UNMOUNT) {
324 *vpp = NULL;
325 return (EPERM);
326 }
327
328 /*
329 * Check the hash for an active cnode
330 */
331 cp = hfs_chashget(dev, cnid, wantrsrc, &vp, &rvp);
332 if (cp != NULL) {
333 /* hide open files that have been deleted */
334 if ((hfsmp->hfs_private_metadata_dir != 0)
335 && (cp->c_parentcnid == hfsmp->hfs_private_metadata_dir)
336 && (cp->c_nlink == 0)) {
337 retval = ENOENT;
338 goto exit;
339 }
340
341 /* Hide private journal files */
342 if (hfsmp->jnl &&
343 (cp->c_parentcnid == kRootDirID) &&
344 ((cp->c_cnid == hfsmp->hfs_jnlfileid) ||
345 (cp->c_cnid == hfsmp->hfs_jnlinfoblkid))) {
346 retval = ENOENT;
347 goto exit;
348 }
349
350 if (wantrsrc && rvp != NULL) {
351 vp = rvp;
352 rvp = NULL;
353 goto done;
354 }
355 if (!wantrsrc && vp != NULL) {
356 /* Hardlinks need an updated catalog descriptor */
357 if (descp && cp->c_flag & C_HARDLINK) {
358 replace_desc(cp, descp);
359 }
360 /* We have a vnode so we're done. */
361 goto done;
362 }
363 }
364
365 /*
366 * There was no active vnode so get a new one.
367 * Use the existing cnode (if any).
368 */
369 if (descp != NULL) {
370 /*
371 * hfs_lookup case, use descp, attrp and forkp
372 */
373 retval = hfs_getnewvnode(hfsmp, cp, descp, wantrsrc, attrp,
374 forkp, &new_vp);
375 } else {
376 struct cat_desc cndesc = {0};
377 struct cat_attr cnattr = {0};
378 struct cat_fork cnfork = {0};
379
380 /*
381 * hfs_vget case, need to lookup entry (by file id)
382 */
383 if (cnid == kRootParID) {
384 static char hfs_rootname[] = "/";
385
386 cndesc.cd_nameptr = &hfs_rootname[0];
387 cndesc.cd_namelen = 1;
388 cndesc.cd_parentcnid = kRootParID;
389 cndesc.cd_cnid = kRootParID;
390 cndesc.cd_flags = CD_ISDIR;
391
392 cnattr.ca_fileid = kRootParID;
393 cnattr.ca_nlink = 2;
394 cnattr.ca_mode = (S_IFDIR | S_IRWXU | S_IRWXG | S_IRWXO);
395 } else {
396 /* Lock catalog b-tree */
397 retval = hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_SHARED, p);
398 if (retval)
399 goto exit;
400
401 retval = cat_idlookup(hfsmp, cnid, &cndesc, &cnattr, &cnfork);
402
403 /* Unlock catalog b-tree */
404 (void) hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_RELEASE, p);
405 if (retval)
406 goto exit;
407
408 /* Hide open files that have been deleted */
409 if ((hfsmp->hfs_private_metadata_dir != 0) &&
410 (cndesc.cd_parentcnid == hfsmp->hfs_private_metadata_dir)) {
411 cat_releasedesc(&cndesc);
412 retval = ENOENT;
413 goto exit;
414 }
415 }
416
417 retval = hfs_getnewvnode(hfsmp, cp, &cndesc, 0, &cnattr, &cnfork, &new_vp);
418
419 /* Hardlinks may need an updated catalog descriptor */
420 if (retval == 0
421 && new_vp
422 && (VTOC(new_vp)->c_flag & C_HARDLINK)
423 && cndesc.cd_nameptr
424 && cndesc.cd_namelen > 0) {
425 replace_desc(VTOC(new_vp), &cndesc);
426 }
427 cat_releasedesc(&cndesc);
428 }
429 exit:
430 /* Release reference taken on opposite vnode (if any). */
431 if (vp)
432 vput(vp);
433 else if (rvp)
434 vput(rvp);
435
436 if (retval) {
437 *vpp = NULL;
438 return (retval);
439 }
440 vp = new_vp;
441 done:
442 /* The cnode's vnode should be in vp. */
443 if (vp == NULL)
444 panic("hfs_getcnode: missing vp!");
445
446 UBCINFOCHECK("hfs_getcnode", vp);
447 *vpp = vp;
448 return (0);
449 }
450
451
452 /*
453 * hfs_getnewvnode - get new default vnode
454 *
455 * the vnode is returned locked
456 */
457 extern int (**hfs_vnodeop_p) (void *);
458 extern int (**hfs_specop_p) (void *);
459 extern int (**hfs_fifoop_p) (void *);
460
461 __private_extern__
462 int
463 hfs_getnewvnode(struct hfsmount *hfsmp, struct cnode *cp,
464 struct cat_desc *descp, int wantrsrc,
465 struct cat_attr *attrp, struct cat_fork *forkp,
466 struct vnode **vpp)
467 {
468 struct mount *mp = HFSTOVFS(hfsmp);
469 struct vnode *vp = NULL;
470 struct vnode *rvp = NULL;
471 struct vnode *new_vp = NULL;
472 struct cnode *cp2 = NULL;
473 struct filefork *fp = NULL;
474 int allocated = 0;
475 int i;
476 int retval;
477 dev_t dev;
478 struct proc *p = current_proc();
479
480 /* Bail when unmount is in progress */
481 if (mp->mnt_kern_flag & MNTK_UNMOUNT) {
482 *vpp = NULL;
483 return (EPERM);
484 }
485
486 #if !FIFO
487 if (IFTOVT(attrp->ca_mode) == VFIFO) {
488 *vpp = NULL;
489 return (EOPNOTSUPP);
490 }
491 #endif
492 dev = hfsmp->hfs_raw_dev;
493
494 /* If no cnode was passed in then create one */
495 if (cp == NULL) {
496 MALLOC_ZONE(cp2, struct cnode *, sizeof(struct cnode),
497 M_HFSNODE, M_WAITOK);
498 bzero(cp2, sizeof(struct cnode));
499 allocated = 1;
500 SET(cp2->c_flag, C_ALLOC);
501 cp2->c_cnid = descp->cd_cnid;
502 cp2->c_fileid = attrp->ca_fileid;
503 cp2->c_dev = dev;
504 lockinit(&cp2->c_lock, PINOD, "cnode", 0, 0);
505 (void) lockmgr(&cp2->c_lock, LK_EXCLUSIVE, (struct slock *)0, p);
506 /*
507 * There were several blocking points since we first
508 * checked the hash. Now that we're through blocking,
509 * check the hash again in case we're racing for the
510 * same cnode.
511 */
512 cp = hfs_chashget(dev, attrp->ca_fileid, wantrsrc, &vp, &rvp);
513 if (cp != NULL) {
514 /* We lost the race - use the winner's cnode */
515 FREE_ZONE(cp2, sizeof(struct cnode), M_HFSNODE);
516 allocated = 0;
517 if (wantrsrc && rvp != NULL) {
518 *vpp = rvp;
519 return (0);
520 }
521 if (!wantrsrc && vp != NULL) {
522 *vpp = vp;
523 return (0);
524 }
525 } else /* allocated */ {
526 cp = cp2;
527 hfs_chashinsert(cp);
528 }
529 }
530
531 /* Allocate a new vnode. If unsuccesful, leave after freeing memory */
532 if ((retval = getnewvnode(VT_HFS, mp, hfs_vnodeop_p, &new_vp))) {
533 if (allocated) {
534 hfs_chashremove(cp);
535 if (ISSET(cp->c_flag, C_WALLOC)) {
536 CLR(cp->c_flag, C_WALLOC);
537 wakeup(cp);
538 }
539 FREE_ZONE(cp2, sizeof(struct cnode), M_HFSNODE);
540 allocated = 0;
541 } else if (rvp) {
542 vput(rvp);
543 } else if (vp) {
544 vput(vp);
545 }
546 *vpp = NULL;
547 return (retval);
548 }
549 if (allocated) {
550 bcopy(attrp, &cp->c_attr, sizeof(struct cat_attr));
551 bcopy(descp, &cp->c_desc, sizeof(struct cat_desc));
552 }
553 new_vp->v_data = cp;
554 if (wantrsrc && S_ISREG(cp->c_mode))
555 cp->c_rsrc_vp = new_vp;
556 else
557 cp->c_vp = new_vp;
558
559 /* Release reference taken on opposite vnode (if any). */
560 if (rvp)
561 vput(rvp);
562 if (vp)
563 vput(vp);
564
565 vp = new_vp;
566 vp->v_ubcinfo = UBC_NOINFO;
567
568 /*
569 * If this is a new cnode then initialize it using descp and attrp...
570 */
571 if (allocated) {
572 /* The name was inherited so clear descriptor state... */
573 descp->cd_namelen = 0;
574 descp->cd_nameptr = NULL;
575 descp->cd_flags &= ~CD_HASBUF;
576
577 /* Tag hardlinks */
578 if (IFTOVT(cp->c_mode) == VREG &&
579 (descp->cd_cnid != attrp->ca_fileid)) {
580 cp->c_flag |= C_HARDLINK;
581 }
582
583 /* Take one dev reference for each non-directory cnode */
584 if (IFTOVT(cp->c_mode) != VDIR) {
585 cp->c_devvp = hfsmp->hfs_devvp;
586 VREF(cp->c_devvp);
587 }
588 #if QUOTA
589 for (i = 0; i < MAXQUOTAS; i++)
590 cp->c_dquot[i] = NODQUOT;
591 #endif /* QUOTA */
592 }
593
594 if (IFTOVT(cp->c_mode) != VDIR) {
595 if (forkp && attrp->ca_blocks < forkp->cf_blocks)
596 panic("hfs_getnewvnode: bad ca_blocks (too small)");
597 /*
598 * Allocate and initialize a file fork...
599 */
600 MALLOC_ZONE(fp, struct filefork *, sizeof(struct filefork),
601 M_HFSFORK, M_WAITOK);
602 bzero(fp, sizeof(struct filefork));
603 fp->ff_cp = cp;
604 if (forkp)
605 bcopy(forkp, &fp->ff_data, sizeof(HFSPlusForkData));
606 if (fp->ff_clumpsize == 0)
607 fp->ff_clumpsize = HFSTOVCB(hfsmp)->vcbClpSiz;
608 rl_init(&fp->ff_invalidranges);
609 if (wantrsrc) {
610 if (cp->c_rsrcfork != NULL)
611 panic("stale rsrc fork");
612 cp->c_rsrcfork = fp;
613 } else {
614 if (cp->c_datafork != NULL)
615 panic("stale data fork");
616 cp->c_datafork = fp;
617 }
618 }
619
620 /*
621 * Finish vnode initialization.
622 * Setting the v_type 'stamps' the vnode as 'complete',
623 * so should be done almost last.
624 *
625 * At this point the vnode should be locked and fully
626 * allocated. And ready to be used or accessed. (though
627 * having it locked prevents most of this, it can still
628 * be accessed through lists and hashes).
629 */
630 vp->v_type = IFTOVT(cp->c_mode);
631
632 /* Tag system files */
633 if ((descp->cd_cnid < kHFSFirstUserCatalogNodeID) && (vp->v_type == VREG))
634 vp->v_flag |= VSYSTEM;
635 /* Tag root directory */
636 if (cp->c_cnid == kRootDirID)
637 vp->v_flag |= VROOT;
638
639 if ((vp->v_type == VREG) && !(vp->v_flag & VSYSTEM)
640 && (UBCINFOMISSING(vp) || UBCINFORECLAIMED(vp))) {
641 ubc_info_init(vp);
642 } else {
643 vp->v_ubcinfo = UBC_NOINFO;
644 }
645
646 if (vp->v_type == VCHR || vp->v_type == VBLK) {
647 struct vnode *nvp;
648
649 vp->v_op = hfs_specop_p;
650 if ((nvp = checkalias(vp, cp->c_rdev, mp))) {
651 /*
652 * Discard unneeded vnode, but save its cnode.
653 * Note that the lock is carried over in the
654 * cnode to the replacement vnode.
655 */
656 nvp->v_data = vp->v_data;
657 vp->v_data = NULL;
658 vp->v_op = spec_vnodeop_p;
659 vrele(vp);
660 vgone(vp);
661 /*
662 * Reinitialize aliased cnode.
663 * Assume its not a resource fork.
664 */
665 cp->c_vp = nvp;
666 vp = nvp;
667 }
668 } else if (vp->v_type == VFIFO) {
669 #if FIFO
670 vp->v_op = hfs_fifoop_p;
671 #endif
672 }
673
674 /* Vnode is now initialized - see if anyone was waiting for it. */
675 CLR(cp->c_flag, C_ALLOC);
676 if (ISSET(cp->c_flag, C_WALLOC)) {
677 CLR(cp->c_flag, C_WALLOC);
678 wakeup((caddr_t)cp);
679 }
680
681 *vpp = vp;
682 return (0);
683 }
684