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