]> git.saurik.com Git - apple/xnu.git/blame - bsd/hfs/hfs_vnops.c
xnu-517.7.21.tar.gz
[apple/xnu.git] / bsd / hfs / hfs_vnops.c
CommitLineData
9bccf70c 1/*
55e303ae 2 * Copyright (c) 2000-2003 Apple Computer, Inc. All rights reserved.
9bccf70c
A
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
e5568f75
A
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.
9bccf70c 11 *
e5568f75
A
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
9bccf70c
A
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
e5568f75
A
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.
9bccf70c
A
19 *
20 * @APPLE_LICENSE_HEADER_END@
21 */
22
23#include <sys/systm.h>
24#include <sys/kernel.h>
25#include <sys/file.h>
26#include <sys/dirent.h>
27#include <sys/stat.h>
28#include <sys/buf.h>
29#include <sys/mount.h>
30#include <sys/vnode.h>
31#include <sys/malloc.h>
32#include <sys/namei.h>
33#include <sys/ubc.h>
34#include <sys/quota.h>
55e303ae
A
35#include <sys/time.h>
36#include <sys/disk.h>
9bccf70c
A
37
38#include <miscfs/specfs/specdev.h>
39#include <miscfs/fifofs/fifo.h>
40#include <vfs/vfs_support.h>
41#include <machine/spl.h>
42
43#include <sys/kdebug.h>
44
45#include "hfs.h"
46#include "hfs_catalog.h"
47#include "hfs_cnode.h"
48#include "hfs_lockf.h"
49#include "hfs_dbg.h"
50#include "hfs_mount.h"
51#include "hfs_quota.h"
52#include "hfs_endian.h"
53
54#include "hfscommon/headers/BTreesInternal.h"
55#include "hfscommon/headers/FileMgrInternal.h"
56
57#define MAKE_DELETED_NAME(NAME,FID) \
58 (void) sprintf((NAME), "%s%d", HFS_DELETE_PREFIX, (FID))
59
55e303ae 60#define KNDETACH_VNLOCKED 0x00000001
9bccf70c 61
55e303ae 62#define CARBON_TEMP_DIR_NAME "Cleanup At Startup"
9bccf70c 63
90556fb8 64
9bccf70c
A
65/* Global vfs data structures for hfs */
66
67
55e303ae
A
68extern unsigned long strtoul(const char *, char **, int);
69
9bccf70c
A
70extern int groupmember(gid_t gid, struct ucred *cred);
71
72static int hfs_makenode(int mode, struct vnode *dvp, struct vnode **vpp,
73 struct componentname *cnp);
74
75static int hfs_vgetrsrc(struct hfsmount *hfsmp, struct vnode *vp,
76 struct vnode **rvpp, struct proc *p);
77
78static int hfs_metasync(struct hfsmount *hfsmp, daddr_t node, struct proc *p);
79
55e303ae
A
80static int hfs_removedir(struct vnode *, struct vnode *, struct componentname *,
81 int);
82
83static int hfs_removefile(struct vnode *, struct vnode *, struct componentname *,
84 int);
85
86/* Options for hfs_removedir and hfs_removefile */
87#define HFSRM_PARENT_LOCKED 0x01
88#define HFSRM_SKIP_RESERVE 0x02
89#define HFSRM_SAVE_NAME 0x04
90#define HFSRM_RENAMEOPTS 0x07
91
92
9bccf70c
A
93int hfs_write_access(struct vnode *vp, struct ucred *cred, struct proc *p, Boolean considerFlags);
94
95int hfs_chflags(struct vnode *vp, u_long flags, struct ucred *cred,
96 struct proc *p);
97int hfs_chmod(struct vnode *vp, int mode, struct ucred *cred,
98 struct proc *p);
99int hfs_chown(struct vnode *vp, uid_t uid, gid_t gid,
100 struct ucred *cred, struct proc *p);
101
102/*****************************************************************************
103*
104* Common Operations on vnodes
105*
106*****************************************************************************/
107
108/*
109 * Create a regular file
110#% create dvp L U U
111#% create vpp - L -
112#
113 vop_create {
114 IN WILLRELE struct vnode *dvp;
115 OUT struct vnode **vpp;
116 IN struct componentname *cnp;
117 IN struct vattr *vap;
118
119 We are responsible for freeing the namei buffer,
120 it is done in hfs_makenode()
121*/
122
123static int
124hfs_create(ap)
125 struct vop_create_args /* {
126 struct vnode *a_dvp;
127 struct vnode **a_vpp;
128 struct componentname *a_cnp;
129 struct vattr *a_vap;
130 } */ *ap;
131{
132 struct vattr *vap = ap->a_vap;
133
134 return (hfs_makenode(MAKEIMODE(vap->va_type, vap->va_mode),
135 ap->a_dvp, ap->a_vpp, ap->a_cnp));
136}
137
138
139/*
140 * Mknod vnode call
141
142#% mknod dvp L U U
143#% mknod vpp - X -
144#
145 vop_mknod {
146 IN WILLRELE struct vnode *dvp;
147 OUT WILLRELE struct vnode **vpp;
148 IN struct componentname *cnp;
149 IN struct vattr *vap;
150 */
151/* ARGSUSED */
152
153static int
154hfs_mknod(ap)
155 struct vop_mknod_args /* {
156 struct vnode *a_dvp;
157 struct vnode **a_vpp;
158 struct componentname *a_cnp;
159 struct vattr *a_vap;
160 } */ *ap;
161{
162 struct vattr *vap = ap->a_vap;
163 struct vnode **vpp = ap->a_vpp;
164 struct cnode *cp;
165 int error;
166
167 if (VTOVCB(ap->a_dvp)->vcbSigWord != kHFSPlusSigWord) {
168 VOP_ABORTOP(ap->a_dvp, ap->a_cnp);
169 vput(ap->a_dvp);
170 return (EOPNOTSUPP);
171 }
172
173 /* Create the vnode */
174 error = hfs_makenode(MAKEIMODE(vap->va_type, vap->va_mode),
175 ap->a_dvp, vpp, ap->a_cnp);
176 if (error)
177 return (error);
178 cp = VTOC(*vpp);
179 cp->c_flag |= C_ACCESS | C_CHANGE | C_UPDATE;
180 if ((vap->va_rdev != VNOVAL) &&
181 (vap->va_type == VBLK || vap->va_type == VCHR))
182 cp->c_rdev = vap->va_rdev;
183 /*
184 * Remove cnode so that it will be reloaded by lookup and
185 * checked to see if it is an alias of an existing vnode.
186 * Note: unlike UFS, we don't bash v_type here.
187 */
188 vput(*vpp);
189 vgone(*vpp);
190 *vpp = 0;
191 return (0);
192}
193
194
195/*
196 * Open called.
197#% open vp L L L
198#
199 vop_open {
200 IN struct vnode *vp;
201 IN int mode;
202 IN struct ucred *cred;
203 IN struct proc *p;
204 */
205
206
207static int
208hfs_open(ap)
209 struct vop_open_args /* {
210 struct vnode *a_vp;
211 int a_mode;
212 struct ucred *a_cred;
213 struct proc *a_p;
214 } */ *ap;
215{
216 struct vnode *vp = ap->a_vp;
55e303ae
A
217 struct filefork *fp = VTOF(vp);
218 struct timeval tv;
9bccf70c
A
219
220 /*
221 * Files marked append-only must be opened for appending.
222 */
223 if ((vp->v_type != VDIR) && (VTOC(vp)->c_flags & APPEND) &&
224 (ap->a_mode & (FWRITE | O_APPEND)) == FWRITE)
225 return (EPERM);
226
55e303ae
A
227 if (ap->a_mode & O_EVTONLY) {
228 if (vp->v_type == VREG) {
229 ++VTOF(vp)->ff_evtonly_refs;
230 } else {
231 ++VTOC(vp)->c_evtonly_refs;
232 };
233 };
234
235 /*
236 * On the first (non-busy) open of a fragmented
237 * file attempt to de-frag it (if its less than 20MB).
238 */
239 if ((VTOHFS(vp)->hfs_flags & HFS_READ_ONLY) ||
240 !UBCISVALID(vp) || ubc_isinuse(vp, 1)) {
241 return (0);
242 }
243 fp = VTOF(vp);
244 if (fp->ff_blocks &&
245 fp->ff_extents[7].blockCount != 0 &&
246 fp->ff_size <= (20 * 1024 * 1024)) {
247 /*
248 * Wait until system bootup is done (3 min).
249 */
250 microuptime(&tv);
251 if (tv.tv_sec < (60 * 3)) {
252 return (0);
253 }
254 (void) hfs_relocate(vp, VTOVCB(vp)->nextAllocation + 4096, ap->a_cred, ap->a_p);
255 }
256
9bccf70c
A
257 return (0);
258}
259
260/*
261 * Close called.
262 *
263 * Update the times on the cnode.
264#% close vp U U U
265#
266 vop_close {
267 IN struct vnode *vp;
268 IN int fflag;
269 IN struct ucred *cred;
270 IN struct proc *p;
271 */
272
273
274static int
275hfs_close(ap)
276 struct vop_close_args /* {
277 struct vnode *a_vp;
278 int a_fflag;
279 struct ucred *a_cred;
280 struct proc *a_p;
281 } */ *ap;
282{
283 register struct vnode *vp = ap->a_vp;
284 register struct cnode *cp = VTOC(vp);
285 register struct filefork *fp = VTOF(vp);
286 struct proc *p = ap->a_p;
287 struct timeval tv;
288 off_t leof;
289 u_long blks, blocksize;
290 int devBlockSize;
291 int error;
292
293 simple_lock(&vp->v_interlock);
294 if ((!UBCISVALID(vp) && vp->v_usecount > 1)
295 || (UBCISVALID(vp) && ubc_isinuse(vp, 1))) {
296 tv = time;
297 CTIMES(cp, &tv, &tv);
298 }
299 simple_unlock(&vp->v_interlock);
300
55e303ae
A
301 if (ap->a_fflag & O_EVTONLY) {
302 if (vp->v_type == VREG) {
303 --VTOF(vp)->ff_evtonly_refs;
304 } else {
305 --VTOC(vp)->c_evtonly_refs;
306 };
307 };
308
9bccf70c
A
309 /*
310 * VOP_CLOSE can be called with vp locked (from vclean).
311 * We check for this case using VOP_ISLOCKED and bail.
312 *
313 * XXX During a force unmount we won't do the cleanup below!
314 */
315 if (vp->v_type == VDIR || VOP_ISLOCKED(vp))
316 return (0);
317
318 leof = fp->ff_size;
319
55e303ae
A
320 if ((fp->ff_blocks > 0) &&
321 !ISSET(cp->c_flag, C_DELETED) &&
322 ((VTOHFS(vp)->hfs_flags & HFS_READ_ONLY) == 0)) {
9bccf70c
A
323 enum vtype our_type = vp->v_type;
324 u_long our_id = vp->v_id;
325 int was_nocache = ISSET(vp->v_flag, VNOCACHE_DATA);
326
327 error = vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p);
328 if (error)
329 return (0);
330 /*
331 * Since we can context switch in vn_lock our vnode
332 * could get recycled (eg umount -f). Double check
333 * that its still ours.
334 */
335 if (vp->v_type != our_type || vp->v_id != our_id
336 || cp != VTOC(vp) || !UBCINFOEXISTS(vp)) {
337 VOP_UNLOCK(vp, 0, p);
338 return (0);
339 }
340
341 /*
342 * Last chance to explicitly zero out the areas
343 * that are currently marked invalid:
344 */
345 VOP_DEVBLOCKSIZE(cp->c_devvp, &devBlockSize);
346 (void) cluster_push(vp);
347 SET(vp->v_flag, VNOCACHE_DATA); /* Don't cache zeros */
348 while (!CIRCLEQ_EMPTY(&fp->ff_invalidranges)) {
349 struct rl_entry *invalid_range = CIRCLEQ_FIRST(&fp->ff_invalidranges);
350 off_t start = invalid_range->rl_start;
351 off_t end = invalid_range->rl_end;
352
353 /* The range about to be written must be validated
354 * first, so that VOP_CMAP() will return the
355 * appropriate mapping for the cluster code:
356 */
357 rl_remove(start, end, &fp->ff_invalidranges);
358
359 (void) cluster_write(vp, (struct uio *) 0, leof,
360 invalid_range->rl_end + 1, invalid_range->rl_start,
361 (off_t)0, devBlockSize, IO_HEADZEROFILL | IO_NOZERODIRTY);
362
363 if (ISSET(vp->v_flag, VHASDIRTY))
364 (void) cluster_push(vp);
365
366 cp->c_flag |= C_MODIFIED;
367 }
368 cp->c_flag &= ~C_ZFWANTSYNC;
369 cp->c_zftimeout = 0;
370 blocksize = VTOVCB(vp)->blockSize;
371 blks = leof / blocksize;
372 if (((off_t)blks * (off_t)blocksize) != leof)
373 blks++;
374 /*
375 * Shrink the peof to the smallest size neccessary to contain the leof.
376 */
377 if (blks < fp->ff_blocks)
378 (void) VOP_TRUNCATE(vp, leof, IO_NDELAY, ap->a_cred, p);
379 (void) cluster_push(vp);
380
381 if (!was_nocache)
382 CLR(vp->v_flag, VNOCACHE_DATA);
383
384 /*
385 * If the VOP_TRUNCATE didn't happen to flush the vnode's
386 * information out to disk, force it to be updated now that
387 * all invalid ranges have been zero-filled and validated:
388 */
389 if (cp->c_flag & C_MODIFIED) {
390 tv = time;
391 VOP_UPDATE(vp, &tv, &tv, 0);
392 }
393 VOP_UNLOCK(vp, 0, p);
394 }
55e303ae
A
395 if ((vp->v_flag & VSYSTEM) && (vp->v_usecount == 1))
396 vgone(vp);
9bccf70c
A
397 return (0);
398}
399
400/*
401#% access vp L L L
402#
403 vop_access {
404 IN struct vnode *vp;
405 IN int mode;
406 IN struct ucred *cred;
407 IN struct proc *p;
408
409 */
410
411static int
412hfs_access(ap)
413 struct vop_access_args /* {
414 struct vnode *a_vp;
415 int a_mode;
416 struct ucred *a_cred;
417 struct proc *a_p;
418 } */ *ap;
419{
420 struct vnode *vp = ap->a_vp;
421 struct cnode *cp = VTOC(vp);
422 struct ucred *cred = ap->a_cred;
423 register gid_t *gp;
424 mode_t mode = ap->a_mode;
425 mode_t mask = 0;
426 int i;
427 int error;
428
429 /*
430 * Disallow write attempts on read-only file systems;
431 * unless the file is a socket, fifo, or a block or
432 * character device resident on the file system.
433 */
434 if (mode & VWRITE) {
435 switch (vp->v_type) {
436 case VDIR:
437 case VLNK:
438 case VREG:
55e303ae 439 if (VTOHFS(vp)->hfs_flags & HFS_READ_ONLY)
9bccf70c
A
440 return (EROFS);
441#if QUOTA
442 if ((error = hfs_getinoquota(cp)))
443 return (error);
444#endif /* QUOTA */
445 break;
446 }
55e303ae
A
447 /* If immutable bit set, nobody gets to write it. */
448 if (cp->c_flags & IMMUTABLE)
449 return (EPERM);
9bccf70c
A
450 }
451
9bccf70c
A
452
453 /* Otherwise, user id 0 always gets access. */
55e303ae 454 if (cred->cr_uid == 0)
9bccf70c
A
455 return (0);
456
457 mask = 0;
458
459 /* Otherwise, check the owner. */
55e303ae 460 if ( (cp->c_uid == cred->cr_uid) || (cp->c_uid == UNKNOWNUID) ) {
9bccf70c
A
461 if (mode & VEXEC)
462 mask |= S_IXUSR;
463 if (mode & VREAD)
464 mask |= S_IRUSR;
465 if (mode & VWRITE)
466 mask |= S_IWUSR;
467 return ((cp->c_mode & mask) == mask ? 0 : EACCES);
468 }
469
470 /* Otherwise, check the groups. */
471 if (! (VTOVFS(vp)->mnt_flag & MNT_UNKNOWNPERMISSIONS)) {
472 for (i = 0, gp = cred->cr_groups; i < cred->cr_ngroups; i++, gp++)
473 if (cp->c_gid == *gp) {
474 if (mode & VEXEC)
475 mask |= S_IXGRP;
476 if (mode & VREAD)
477 mask |= S_IRGRP;
478 if (mode & VWRITE)
479 mask |= S_IWGRP;
480 return ((cp->c_mode & mask) == mask ? 0 : EACCES);
481 }
482 }
483
484 /* Otherwise, check everyone else. */
485 if (mode & VEXEC)
486 mask |= S_IXOTH;
487 if (mode & VREAD)
488 mask |= S_IROTH;
489 if (mode & VWRITE)
490 mask |= S_IWOTH;
491 return ((cp->c_mode & mask) == mask ? 0 : EACCES);
492}
493
494
495
496/*
497#% getattr vp = = =
498#
499 vop_getattr {
500 IN struct vnode *vp;
501 IN struct vattr *vap;
502 IN struct ucred *cred;
503 IN struct proc *p;
504
505 */
506
507
508/* ARGSUSED */
509static int
510hfs_getattr(ap)
511 struct vop_getattr_args /* {
512 struct vnode *a_vp;
513 struct vattr *a_vap;
514 struct ucred *a_cred;
515 struct proc *a_p;
516 } */ *ap;
517{
518 struct vnode *vp = ap->a_vp;
519 struct cnode *cp = VTOC(vp);
520 struct vattr *vap = ap->a_vap;
521 struct timeval tv;
522
523 tv = time;
524 CTIMES(cp, &tv, &tv);
525
526 vap->va_type = vp->v_type;
55e303ae
A
527 vap->va_mode = cp->c_mode;
528 vap->va_nlink = cp->c_nlink;
9bccf70c
A
529 /*
530 * [2856576] Since we are dynamically changing the owner, also
531 * effectively turn off the set-user-id and set-group-id bits,
532 * just like chmod(2) would when changing ownership. This prevents
533 * a security hole where set-user-id programs run as whoever is
534 * logged on (or root if nobody is logged in yet!)
535 */
55e303ae
A
536 if (cp->c_uid == UNKNOWNUID) {
537 vap->va_mode &= ~(S_ISUID | S_ISGID);
538 vap->va_uid = ap->a_cred->cr_uid;
539 } else {
540 vap->va_uid = cp->c_uid;
541 }
9bccf70c
A
542 vap->va_gid = cp->c_gid;
543 vap->va_fsid = cp->c_dev;
544 /*
545 * Exporting file IDs from HFS Plus:
546 *
547 * For "normal" files the c_fileid is the same value as the
548 * c_cnid. But for hard link files, they are different - the
549 * c_cnid belongs to the active directory entry (ie the link)
550 * and the c_fileid is for the actual inode (ie the data file).
551 *
552 * The stat call (getattr) will always return the c_fileid
553 * and Carbon APIs, which are hardlink-ignorant, will always
554 * receive the c_cnid (from getattrlist).
555 */
556 vap->va_fileid = cp->c_fileid;
557 vap->va_atime.tv_sec = cp->c_atime;
558 vap->va_atime.tv_nsec = 0;
559 vap->va_mtime.tv_sec = cp->c_mtime;
560 vap->va_mtime.tv_nsec = cp->c_mtime_nsec;
561 vap->va_ctime.tv_sec = cp->c_ctime;
562 vap->va_ctime.tv_nsec = 0;
563 vap->va_gen = 0;
564 vap->va_flags = cp->c_flags;
565 vap->va_rdev = 0;
566 vap->va_blocksize = VTOVFS(vp)->mnt_stat.f_iosize;
567 vap->va_filerev = 0;
9bccf70c
A
568 if (vp->v_type == VDIR) {
569 vap->va_size = cp->c_nlink * AVERAGE_HFSDIRENTRY_SIZE;
570 vap->va_bytes = 0;
571 } else {
572 vap->va_size = VTOF(vp)->ff_size;
573 vap->va_bytes = (u_quad_t)cp->c_blocks *
574 (u_quad_t)VTOVCB(vp)->blockSize;
575 if (vp->v_type == VBLK || vp->v_type == VCHR)
576 vap->va_rdev = cp->c_rdev;
577 }
578 return (0);
579}
580
581/*
582 * Set attribute vnode op. called from several syscalls
583#% setattr vp L L L
584#
585 vop_setattr {
586 IN struct vnode *vp;
587 IN struct vattr *vap;
588 IN struct ucred *cred;
589 IN struct proc *p;
590
591 */
592
593static int
594hfs_setattr(ap)
595 struct vop_setattr_args /* {
596 struct vnode *a_vp;
597 struct vattr *a_vap;
598 struct ucred *a_cred;
599 struct proc *a_p;
600 } */ *ap;
601{
602 struct vattr *vap = ap->a_vap;
603 struct vnode *vp = ap->a_vp;
604 struct cnode *cp = VTOC(vp);
605 struct ucred *cred = ap->a_cred;
606 struct proc *p = ap->a_p;
607 struct timeval atimeval, mtimeval;
608 int error;
609
610 /*
611 * Check for unsettable attributes.
612 */
613 if ((vap->va_type != VNON) || (vap->va_nlink != VNOVAL) ||
614 (vap->va_fsid != VNOVAL) || (vap->va_fileid != VNOVAL) ||
615 (vap->va_blocksize != VNOVAL) || (vap->va_rdev != VNOVAL) ||
616 ((int)vap->va_bytes != VNOVAL) || (vap->va_gen != VNOVAL)) {
617 return (EINVAL);
618 }
619
55e303ae
A
620 // XXXdbg
621 // don't allow people to set the attributes of symlinks
622 // (nfs has a bad habit of doing ths and it can cause
623 // problems for journaling).
624 //
625 if (vp->v_type == VLNK) {
626 return 0;
627 }
628
629
630
9bccf70c 631 if (vap->va_flags != VNOVAL) {
55e303ae 632 if (VTOHFS(vp)->hfs_flags & HFS_READ_ONLY)
9bccf70c
A
633 return (EROFS);
634 if ((error = hfs_chflags(vp, vap->va_flags, cred, p)))
635 return (error);
636 if (vap->va_flags & (IMMUTABLE | APPEND))
637 return (0);
638 }
639
640 if (cp->c_flags & (IMMUTABLE | APPEND))
641 return (EPERM);
b4c24cb9
A
642
643 // XXXdbg - don't allow modification of the journal or journal_info_block
644 if (VTOHFS(vp)->jnl && cp->c_datafork) {
645 struct HFSPlusExtentDescriptor *extd;
646
55e303ae 647 extd = &cp->c_datafork->ff_extents[0];
b4c24cb9
A
648 if (extd->startBlock == VTOVCB(vp)->vcbJinfoBlock || extd->startBlock == VTOHFS(vp)->jnl_start) {
649 return EPERM;
650 }
651 }
652
9bccf70c
A
653 /*
654 * Go through the fields and update iff not VNOVAL.
655 */
656 if (vap->va_uid != (uid_t)VNOVAL || vap->va_gid != (gid_t)VNOVAL) {
55e303ae 657 if (VTOHFS(vp)->hfs_flags & HFS_READ_ONLY)
9bccf70c
A
658 return (EROFS);
659 if ((error = hfs_chown(vp, vap->va_uid, vap->va_gid, cred, p)))
660 return (error);
661 }
662 if (vap->va_size != VNOVAL) {
663 /*
664 * Disallow write attempts on read-only file systems;
665 * unless the file is a socket, fifo, or a block or
666 * character device resident on the file system.
667 */
668 switch (vp->v_type) {
669 case VDIR:
670 return (EISDIR);
671 case VLNK:
672 case VREG:
55e303ae 673 if (VTOHFS(vp)->hfs_flags & HFS_READ_ONLY)
9bccf70c
A
674 return (EROFS);
675 break;
676 default:
677 break;
678 }
679 if ((error = VOP_TRUNCATE(vp, vap->va_size, 0, cred, p)))
680 return (error);
681 }
682 cp = VTOC(vp);
683 if (vap->va_atime.tv_sec != VNOVAL || vap->va_mtime.tv_sec != VNOVAL) {
55e303ae 684 if (VTOHFS(vp)->hfs_flags & HFS_READ_ONLY)
9bccf70c
A
685 return (EROFS);
686 if (((error = hfs_owner_rights(VTOHFS(vp), cp->c_uid, cred, p, true)) != 0) &&
687 ((vap->va_vaflags & VA_UTIMES_NULL) == 0 ||
688 (error = VOP_ACCESS(vp, VWRITE, cred, p)))) {
689 return (error);
690 }
691 if (vap->va_atime.tv_sec != VNOVAL)
692 cp->c_flag |= C_ACCESS;
693 if (vap->va_mtime.tv_sec != VNOVAL) {
694 cp->c_flag |= C_CHANGE | C_UPDATE;
695 /*
696 * The utimes system call can reset the modification
697 * time but it doesn't know about HFS create times.
698 * So we need to insure that the creation time is
699 * always at least as old as the modification time.
700 */
701 if ((VTOVCB(vp)->vcbSigWord == kHFSPlusSigWord) &&
702 (cp->c_cnid != kRootDirID) &&
703 (vap->va_mtime.tv_sec < cp->c_itime)) {
704 cp->c_itime = vap->va_mtime.tv_sec;
705 }
706 }
707 atimeval.tv_sec = vap->va_atime.tv_sec;
708 atimeval.tv_usec = 0;
709 mtimeval.tv_sec = vap->va_mtime.tv_sec;
710 mtimeval.tv_usec = 0;
711 if ((error = VOP_UPDATE(vp, &atimeval, &mtimeval, 1)))
712 return (error);
713 }
714 error = 0;
715 if (vap->va_mode != (mode_t)VNOVAL) {
55e303ae 716 if (VTOHFS(vp)->hfs_flags & HFS_READ_ONLY)
9bccf70c
A
717 return (EROFS);
718 error = hfs_chmod(vp, (int)vap->va_mode, cred, p);
719 }
55e303ae 720 HFS_KNOTE(vp, NOTE_ATTRIB);
9bccf70c
A
721 return (error);
722}
723
724
725/*
726 * Change the mode on a file.
727 * cnode must be locked before calling.
728 */
55e303ae 729__private_extern__
9bccf70c
A
730int
731hfs_chmod(vp, mode, cred, p)
732 register struct vnode *vp;
733 register int mode;
734 register struct ucred *cred;
735 struct proc *p;
736{
737 register struct cnode *cp = VTOC(vp);
738 int error;
739
740 if (VTOVCB(vp)->vcbSigWord != kHFSPlusSigWord)
741 return (0);
742
b4c24cb9
A
743 // XXXdbg - don't allow modification of the journal or journal_info_block
744 if (VTOHFS(vp)->jnl && cp && cp->c_datafork) {
745 struct HFSPlusExtentDescriptor *extd;
746
55e303ae 747 extd = &cp->c_datafork->ff_extents[0];
b4c24cb9
A
748 if (extd->startBlock == VTOVCB(vp)->vcbJinfoBlock || extd->startBlock == VTOHFS(vp)->jnl_start) {
749 return EPERM;
750 }
751 }
752
9bccf70c
A
753#if OVERRIDE_UNKNOWN_PERMISSIONS
754 if (VTOVFS(vp)->mnt_flag & MNT_UNKNOWNPERMISSIONS) {
755 return (0);
756 };
757#endif
758 if ((error = hfs_owner_rights(VTOHFS(vp), cp->c_uid, cred, p, true)) != 0)
759 return (error);
760 if (cred->cr_uid) {
761 if (vp->v_type != VDIR && (mode & S_ISTXT))
762 return (EFTYPE);
763 if (!groupmember(cp->c_gid, cred) && (mode & S_ISGID))
764 return (EPERM);
765 }
766 cp->c_mode &= ~ALLPERMS;
767 cp->c_mode |= (mode & ALLPERMS);
768 cp->c_flag |= C_CHANGE;
769 return (0);
770}
771
772
55e303ae 773__private_extern__
9bccf70c
A
774int
775hfs_write_access(struct vnode *vp, struct ucred *cred, struct proc *p, Boolean considerFlags)
776{
777 struct cnode *cp = VTOC(vp);
778 gid_t *gp;
779 int retval = 0;
780 int i;
781
782 /*
783 * Disallow write attempts on read-only file systems;
784 * unless the file is a socket, fifo, or a block or
785 * character device resident on the file system.
786 */
787 switch (vp->v_type) {
788 case VDIR:
789 case VLNK:
790 case VREG:
55e303ae 791 if (VTOHFS(vp)->hfs_flags & HFS_READ_ONLY)
9bccf70c 792 return (EROFS);
55e303ae 793 break;
9bccf70c
A
794 default:
795 break;
796 }
797
798 /* If immutable bit set, nobody gets to write it. */
799 if (considerFlags && (cp->c_flags & IMMUTABLE))
800 return (EPERM);
801
802 /* Otherwise, user id 0 always gets access. */
803 if (cred->cr_uid == 0)
804 return (0);
805
806 /* Otherwise, check the owner. */
807 if ((retval = hfs_owner_rights(VTOHFS(vp), cp->c_uid, cred, p, false)) == 0)
808 return ((cp->c_mode & S_IWUSR) == S_IWUSR ? 0 : EACCES);
809
810 /* Otherwise, check the groups. */
811 for (i = 0, gp = cred->cr_groups; i < cred->cr_ngroups; i++, gp++) {
812 if (cp->c_gid == *gp)
813 return ((cp->c_mode & S_IWGRP) == S_IWGRP ? 0 : EACCES);
814 }
815
816 /* Otherwise, check everyone else. */
817 return ((cp->c_mode & S_IWOTH) == S_IWOTH ? 0 : EACCES);
818}
819
820
821
822/*
823 * Change the flags on a file or directory.
824 * cnode must be locked before calling.
825 */
55e303ae 826__private_extern__
9bccf70c
A
827int
828hfs_chflags(vp, flags, cred, p)
829 register struct vnode *vp;
830 register u_long flags;
831 register struct ucred *cred;
832 struct proc *p;
833{
834 register struct cnode *cp = VTOC(vp);
835 int retval;
836
837 if (VTOVCB(vp)->vcbSigWord == kHFSSigWord) {
838 if ((retval = hfs_write_access(vp, cred, p, false)) != 0) {
839 return retval;
840 };
841 } else if ((retval = hfs_owner_rights(VTOHFS(vp), cp->c_uid, cred, p, true)) != 0) {
842 return retval;
843 };
844
845 if (cred->cr_uid == 0) {
846 if ((cp->c_flags & (SF_IMMUTABLE | SF_APPEND)) &&
847 securelevel > 0) {
848 return EPERM;
849 };
850 cp->c_flags = flags;
851 } else {
852 if (cp->c_flags & (SF_IMMUTABLE | SF_APPEND) ||
853 (flags & UF_SETTABLE) != flags) {
854 return EPERM;
855 };
856 cp->c_flags &= SF_SETTABLE;
857 cp->c_flags |= (flags & UF_SETTABLE);
858 }
859 cp->c_flag |= C_CHANGE;
860
861 return (0);
862}
863
864
865/*
866 * Perform chown operation on cnode cp;
867 * code must be locked prior to call.
868 */
55e303ae 869__private_extern__
9bccf70c
A
870int
871hfs_chown(vp, uid, gid, cred, p)
872 register struct vnode *vp;
873 uid_t uid;
874 gid_t gid;
875 struct ucred *cred;
876 struct proc *p;
877{
878 register struct cnode *cp = VTOC(vp);
879 uid_t ouid;
880 gid_t ogid;
881 int error = 0;
882#if QUOTA
883 register int i;
884 int64_t change;
885#endif /* QUOTA */
886
887 if (VTOVCB(vp)->vcbSigWord != kHFSPlusSigWord)
888 return (EOPNOTSUPP);
889
890 if (VTOVFS(vp)->mnt_flag & MNT_UNKNOWNPERMISSIONS)
891 return (0);
892
893 if (uid == (uid_t)VNOVAL)
894 uid = cp->c_uid;
895 if (gid == (gid_t)VNOVAL)
896 gid = cp->c_gid;
897 /*
898 * If we don't own the file, are trying to change the owner
899 * of the file, or are not a member of the target group,
900 * the caller must be superuser or the call fails.
901 */
902 if ((cred->cr_uid != cp->c_uid || uid != cp->c_uid ||
903 (gid != cp->c_gid && !groupmember((gid_t)gid, cred))) &&
904 (error = suser(cred, &p->p_acflag)))
905 return (error);
906
907 ogid = cp->c_gid;
908 ouid = cp->c_uid;
909#if QUOTA
910 if ((error = hfs_getinoquota(cp)))
911 return (error);
912 if (ouid == uid) {
913 dqrele(vp, cp->c_dquot[USRQUOTA]);
914 cp->c_dquot[USRQUOTA] = NODQUOT;
915 }
916 if (ogid == gid) {
917 dqrele(vp, cp->c_dquot[GRPQUOTA]);
918 cp->c_dquot[GRPQUOTA] = NODQUOT;
919 }
920
921 /*
922 * Eventually need to account for (fake) a block per directory
923 *if (vp->v_type == VDIR)
924 *change = VTOVCB(vp)->blockSize;
925 *else
926 */
927
928 change = (int64_t)(cp->c_blocks) * (int64_t)VTOVCB(vp)->blockSize;
929 (void) hfs_chkdq(cp, -change, cred, CHOWN);
930 (void) hfs_chkiq(cp, -1, cred, CHOWN);
931 for (i = 0; i < MAXQUOTAS; i++) {
932 dqrele(vp, cp->c_dquot[i]);
933 cp->c_dquot[i] = NODQUOT;
934 }
935#endif /* QUOTA */
936 cp->c_gid = gid;
937 cp->c_uid = uid;
938#if QUOTA
939 if ((error = hfs_getinoquota(cp)) == 0) {
940 if (ouid == uid) {
941 dqrele(vp, cp->c_dquot[USRQUOTA]);
942 cp->c_dquot[USRQUOTA] = NODQUOT;
943 }
944 if (ogid == gid) {
945 dqrele(vp, cp->c_dquot[GRPQUOTA]);
946 cp->c_dquot[GRPQUOTA] = NODQUOT;
947 }
948 if ((error = hfs_chkdq(cp, change, cred, CHOWN)) == 0) {
949 if ((error = hfs_chkiq(cp, 1, cred, CHOWN)) == 0)
950 goto good;
951 else
952 (void) hfs_chkdq(cp, -change, cred, CHOWN|FORCE);
953 }
954 for (i = 0; i < MAXQUOTAS; i++) {
955 dqrele(vp, cp->c_dquot[i]);
956 cp->c_dquot[i] = NODQUOT;
957 }
958 }
959 cp->c_gid = ogid;
960 cp->c_uid = ouid;
961 if (hfs_getinoquota(cp) == 0) {
962 if (ouid == uid) {
963 dqrele(vp, cp->c_dquot[USRQUOTA]);
964 cp->c_dquot[USRQUOTA] = NODQUOT;
965 }
966 if (ogid == gid) {
967 dqrele(vp, cp->c_dquot[GRPQUOTA]);
968 cp->c_dquot[GRPQUOTA] = NODQUOT;
969 }
970 (void) hfs_chkdq(cp, change, cred, FORCE|CHOWN);
971 (void) hfs_chkiq(cp, 1, cred, FORCE|CHOWN);
972 (void) hfs_getinoquota(cp);
973 }
974 return (error);
975good:
976 if (hfs_getinoquota(cp))
977 panic("hfs_chown: lost quota");
978#endif /* QUOTA */
979
980 if (ouid != uid || ogid != gid)
981 cp->c_flag |= C_CHANGE;
982 if (ouid != uid && cred->cr_uid != 0)
983 cp->c_mode &= ~S_ISUID;
984 if (ogid != gid && cred->cr_uid != 0)
985 cp->c_mode &= ~S_ISGID;
986 return (0);
987}
988
989
990/*
991#
992#% exchange fvp L L L
993#% exchange tvp L L L
994#
995 */
996 /*
997 * The hfs_exchange routine swaps the fork data in two files by
998 * exchanging some of the information in the cnode. It is used
999 * to preserve the file ID when updating an existing file, in
1000 * case the file is being tracked through its file ID. Typically
1001 * its used after creating a new file during a safe-save.
1002 */
1003
1004static int
1005hfs_exchange(ap)
1006 struct vop_exchange_args /* {
1007 struct vnode *a_fvp;
1008 struct vnode *a_tvp;
1009 struct ucred *a_cred;
1010 struct proc *a_p;
1011 } */ *ap;
1012{
1013 struct vnode *from_vp = ap->a_fvp;
1014 struct vnode *to_vp = ap->a_tvp;
9bccf70c
A
1015 struct cnode *from_cp = VTOC(from_vp);
1016 struct cnode *to_cp = VTOC(to_vp);
1017 struct hfsmount *hfsmp = VTOHFS(from_vp);
1018 struct cat_desc tempdesc;
1019 struct cat_attr tempattr;
b4c24cb9 1020 int error = 0, started_tr = 0, grabbed_lock = 0;
55e303ae 1021 cat_cookie_t cookie = {0};
9bccf70c
A
1022
1023 /* The files must be on the same volume. */
1024 if (from_vp->v_mount != to_vp->v_mount)
1025 return (EXDEV);
1026
1027 /* Only normal files can be exchanged. */
1028 if ((from_vp->v_type != VREG) || (to_vp->v_type != VREG) ||
1029 (from_cp->c_flag & C_HARDLINK) || (to_cp->c_flag & C_HARDLINK) ||
1030 VNODE_IS_RSRC(from_vp) || VNODE_IS_RSRC(to_vp))
1031 return (EINVAL);
1032
b4c24cb9
A
1033 // XXXdbg - don't allow modification of the journal or journal_info_block
1034 if (hfsmp->jnl) {
1035 struct HFSPlusExtentDescriptor *extd;
1036
1037 if (from_cp->c_datafork) {
55e303ae 1038 extd = &from_cp->c_datafork->ff_extents[0];
b4c24cb9
A
1039 if (extd->startBlock == VTOVCB(from_vp)->vcbJinfoBlock || extd->startBlock == hfsmp->jnl_start) {
1040 return EPERM;
1041 }
1042 }
1043
1044 if (to_cp->c_datafork) {
55e303ae 1045 extd = &to_cp->c_datafork->ff_extents[0];
b4c24cb9
A
1046 if (extd->startBlock == VTOVCB(to_vp)->vcbJinfoBlock || extd->startBlock == hfsmp->jnl_start) {
1047 return EPERM;
1048 }
1049 }
1050 }
1051
b4c24cb9
A
1052 // XXXdbg
1053 hfs_global_shared_lock_acquire(hfsmp);
1054 grabbed_lock = 1;
1055 if (hfsmp->jnl) {
1056 if ((error = journal_start_transaction(hfsmp->jnl)) != 0) {
1057 goto Err_Exit;
1058 }
1059 started_tr = 1;
1060 }
1061
55e303ae
A
1062 /*
1063 * Reserve some space in the Catalog file.
1064 */
1065 if ((error = cat_preflight(hfsmp, CAT_EXCHANGE, &cookie, ap->a_p))) {
1066 goto Err_Exit;
1067 }
1068
9bccf70c
A
1069 /* Lock catalog b-tree */
1070 error = hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_EXCLUSIVE, ap->a_p);
1071 if (error) goto Err_Exit;
1072
1073 /* The backend code always tries to delete the virtual
1074 * extent id for exchanging files so we neeed to lock
1075 * the extents b-tree.
1076 */
1077 error = hfs_metafilelocking(hfsmp, kHFSExtentsFileID, LK_EXCLUSIVE, ap->a_p);
1078 if (error) {
1079 (void) hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_RELEASE, ap->a_p);
1080 goto Err_Exit;
1081 }
1082
1083 /* Do the exchange */
1084 error = MacToVFSError(ExchangeFileIDs(HFSTOVCB(hfsmp),
1085 from_cp->c_desc.cd_nameptr, to_cp->c_desc.cd_nameptr,
1086 from_cp->c_parentcnid, to_cp->c_parentcnid,
1087 from_cp->c_hint, to_cp->c_hint));
1088
1089 (void) hfs_metafilelocking(hfsmp, kHFSExtentsFileID, LK_RELEASE, ap->a_p);
1090 (void) hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_RELEASE, ap->a_p);
1091
1092 if (error != E_NONE) {
1093 goto Err_Exit;
1094 }
1095
1096 /* Purge the vnodes from the name cache */
1097 if (from_vp)
1098 cache_purge(from_vp);
1099 if (to_vp)
1100 cache_purge(to_vp);
1101
1102 /* Save a copy of from attributes before swapping. */
1103 bcopy(&from_cp->c_desc, &tempdesc, sizeof(struct cat_desc));
1104 bcopy(&from_cp->c_attr, &tempattr, sizeof(struct cat_attr));
1105
1106 /*
1107 * Swap the descriptors and all non-fork related attributes.
1108 * (except the modify date)
1109 */
1110 bcopy(&to_cp->c_desc, &from_cp->c_desc, sizeof(struct cat_desc));
b4c24cb9 1111
9bccf70c
A
1112 from_cp->c_hint = 0;
1113 from_cp->c_fileid = from_cp->c_cnid;
1114 from_cp->c_itime = to_cp->c_itime;
1115 from_cp->c_btime = to_cp->c_btime;
1116 from_cp->c_atime = to_cp->c_atime;
1117 from_cp->c_ctime = to_cp->c_ctime;
1118 from_cp->c_gid = to_cp->c_gid;
1119 from_cp->c_uid = to_cp->c_uid;
1120 from_cp->c_flags = to_cp->c_flags;
1121 from_cp->c_mode = to_cp->c_mode;
1122 bcopy(to_cp->c_finderinfo, from_cp->c_finderinfo, 32);
1123
1124 bcopy(&tempdesc, &to_cp->c_desc, sizeof(struct cat_desc));
1125 to_cp->c_hint = 0;
1126 to_cp->c_fileid = to_cp->c_cnid;
1127 to_cp->c_itime = tempattr.ca_itime;
1128 to_cp->c_btime = tempattr.ca_btime;
1129 to_cp->c_atime = tempattr.ca_atime;
1130 to_cp->c_ctime = tempattr.ca_ctime;
1131 to_cp->c_gid = tempattr.ca_gid;
1132 to_cp->c_uid = tempattr.ca_uid;
1133 to_cp->c_flags = tempattr.ca_flags;
1134 to_cp->c_mode = tempattr.ca_mode;
1135 bcopy(tempattr.ca_finderinfo, to_cp->c_finderinfo, 32);
1136
1137 /* Reinsert into the cnode hash under new file IDs*/
1138 hfs_chashremove(from_cp);
1139 hfs_chashremove(to_cp);
1140
1141 hfs_chashinsert(from_cp);
1142 hfs_chashinsert(to_cp);
90556fb8
A
1143
1144 /*
1145 * When a file moves out of "Cleanup At Startup"
1146 * we can drop its NODUMP status.
1147 */
1148 if ((from_cp->c_flags & UF_NODUMP) &&
1149 (from_cp->c_parentcnid != to_cp->c_parentcnid)) {
1150 from_cp->c_flags &= ~UF_NODUMP;
1151 from_cp->c_flag |= C_CHANGE;
1152 }
90556fb8
A
1153 if ((to_cp->c_flags & UF_NODUMP) &&
1154 (to_cp->c_parentcnid != from_cp->c_parentcnid)) {
1155 to_cp->c_flags &= ~UF_NODUMP;
1156 to_cp->c_flag |= C_CHANGE;
1157 }
1158
55e303ae
A
1159 HFS_KNOTE(from_vp, NOTE_ATTRIB);
1160 HFS_KNOTE(to_vp, NOTE_ATTRIB);
1161
9bccf70c 1162Err_Exit:
55e303ae 1163 cat_postflight(hfsmp, &cookie, ap->a_p);
9bccf70c 1164
b4c24cb9
A
1165 // XXXdbg
1166 if (started_tr) {
1167 journal_end_transaction(hfsmp->jnl);
1168 }
1169 if (grabbed_lock) {
1170 hfs_global_shared_lock_release(hfsmp);
1171 }
1172
9bccf70c
A
1173 return (error);
1174}
1175
1176
1177/*
1178
1179#% fsync vp L L L
1180#
1181 vop_fsync {
1182 IN struct vnode *vp;
1183 IN struct ucred *cred;
1184 IN int waitfor;
1185 IN struct proc *p;
1186
1187 */
9bccf70c
A
1188static int
1189hfs_fsync(ap)
1190 struct vop_fsync_args /* {
1191 struct vnode *a_vp;
1192 struct ucred *a_cred;
1193 int a_waitfor;
1194 struct proc *a_p;
1195 } */ *ap;
1196{
1197 struct vnode *vp = ap->a_vp;
1198 struct cnode *cp = VTOC(vp);
1199 struct filefork *fp = NULL;
1200 int retval = 0;
1201 register struct buf *bp;
1202 struct timeval tv;
1203 struct buf *nbp;
b4c24cb9 1204 struct hfsmount *hfsmp = VTOHFS(ap->a_vp);
9bccf70c
A
1205 int s;
1206 int wait;
1207 int retry = 0;
1208
1209 wait = (ap->a_waitfor == MNT_WAIT);
1210
1211 /* HFS directories don't have any data blocks. */
1212 if (vp->v_type == VDIR)
1213 goto metasync;
1214
1215 /*
1216 * For system files flush the B-tree header and
1217 * for regular files write out any clusters
1218 */
1219 if (vp->v_flag & VSYSTEM) {
b4c24cb9
A
1220 if (VTOF(vp)->fcbBTCBPtr != NULL) {
1221 // XXXdbg
55e303ae 1222 if (hfsmp->jnl == NULL) {
b4c24cb9
A
1223 BTFlushPath(VTOF(vp));
1224 }
1225 }
9bccf70c
A
1226 } else if (UBCINFOEXISTS(vp))
1227 (void) cluster_push(vp);
1228
1229 /*
1230 * When MNT_WAIT is requested and the zero fill timeout
1231 * has expired then we must explicitly zero out any areas
1232 * that are currently marked invalid (holes).
90556fb8
A
1233 *
1234 * Files with NODUMP can bypass zero filling here.
9bccf70c
A
1235 */
1236 if ((wait || (cp->c_flag & C_ZFWANTSYNC)) &&
90556fb8 1237 ((cp->c_flags & UF_NODUMP) == 0) &&
9bccf70c
A
1238 UBCINFOEXISTS(vp) && (fp = VTOF(vp)) &&
1239 cp->c_zftimeout != 0) {
1240 int devblksize;
1241 int was_nocache;
1242
1243 if (time.tv_sec < cp->c_zftimeout) {
1244 /* Remember that a force sync was requested. */
1245 cp->c_flag |= C_ZFWANTSYNC;
1246 goto loop;
1247 }
1248 VOP_DEVBLOCKSIZE(cp->c_devvp, &devblksize);
1249 was_nocache = ISSET(vp->v_flag, VNOCACHE_DATA);
1250 SET(vp->v_flag, VNOCACHE_DATA); /* Don't cache zeros */
1251
1252 while (!CIRCLEQ_EMPTY(&fp->ff_invalidranges)) {
1253 struct rl_entry *invalid_range = CIRCLEQ_FIRST(&fp->ff_invalidranges);
1254 off_t start = invalid_range->rl_start;
1255 off_t end = invalid_range->rl_end;
1256
1257 /* The range about to be written must be validated
1258 * first, so that VOP_CMAP() will return the
1259 * appropriate mapping for the cluster code:
1260 */
1261 rl_remove(start, end, &fp->ff_invalidranges);
1262
1263 (void) cluster_write(vp, (struct uio *) 0,
1264 fp->ff_size,
1265 invalid_range->rl_end + 1,
1266 invalid_range->rl_start,
1267 (off_t)0, devblksize,
1268 IO_HEADZEROFILL | IO_NOZERODIRTY);
1269 cp->c_flag |= C_MODIFIED;
1270 }
1271 (void) cluster_push(vp);
1272 if (!was_nocache)
1273 CLR(vp->v_flag, VNOCACHE_DATA);
1274 cp->c_flag &= ~C_ZFWANTSYNC;
1275 cp->c_zftimeout = 0;
1276 }
1277
1278 /*
1279 * Flush all dirty buffers associated with a vnode.
1280 */
1281loop:
1282 s = splbio();
1283 for (bp = vp->v_dirtyblkhd.lh_first; bp; bp = nbp) {
1284 nbp = bp->b_vnbufs.le_next;
1285 if ((bp->b_flags & B_BUSY))
1286 continue;
1287 if ((bp->b_flags & B_DELWRI) == 0)
b4c24cb9
A
1288 panic("hfs_fsync: bp 0x% not dirty (hfsmp 0x%x)", bp, hfsmp);
1289 // XXXdbg
1290 if (hfsmp->jnl && (bp->b_flags & B_LOCKED)) {
1291 if ((bp->b_flags & B_META) == 0) {
1292 panic("hfs: bp @ 0x%x is locked but not meta! jnl 0x%x\n",
1293 bp, hfsmp->jnl);
1294 }
1295 // if journal_active() returns >= 0 then the journal is ok and we
1296 // shouldn't do anything to this locked block (because it is part
1297 // of a transaction). otherwise we'll just go through the normal
1298 // code path and flush the buffer.
1299 if (journal_active(hfsmp->jnl) >= 0) {
1300 continue;
1301 }
1302 }
1303
9bccf70c
A
1304 bremfree(bp);
1305 bp->b_flags |= B_BUSY;
1306 /* Clear B_LOCKED, should only be set on meta files */
1307 bp->b_flags &= ~B_LOCKED;
b4c24cb9 1308
9bccf70c
A
1309 splx(s);
1310 /*
1311 * Wait for I/O associated with indirect blocks to complete,
1312 * since there is no way to quickly wait for them below.
1313 */
1314 if (bp->b_vp == vp || ap->a_waitfor == MNT_NOWAIT)
1315 (void) bawrite(bp);
1316 else
1317 (void) VOP_BWRITE(bp);
1318 goto loop;
1319 }
1320
1321 if (wait) {
1322 while (vp->v_numoutput) {
1323 vp->v_flag |= VBWAIT;
1324 tsleep((caddr_t)&vp->v_numoutput, PRIBIO + 1, "hfs_fsync", 0);
1325 }
1326
b4c24cb9
A
1327 // XXXdbg -- is checking for hfsmp->jnl == NULL the right
1328 // thing to do?
1329 if (hfsmp->jnl == NULL && vp->v_dirtyblkhd.lh_first) {
9bccf70c
A
1330 /* still have some dirty buffers */
1331 if (retry++ > 10) {
1332 vprint("hfs_fsync: dirty", vp);
1333 splx(s);
1334 /*
1335 * Looks like the requests are not
1336 * getting queued to the driver.
1337 * Retrying here causes a cpu bound loop.
1338 * Yield to the other threads and hope
1339 * for the best.
1340 */
1341 (void)tsleep((caddr_t)&vp->v_numoutput,
1342 PRIBIO + 1, "hfs_fsync", hz/10);
1343 retry = 0;
1344 } else {
1345 splx(s);
1346 }
1347 /* try again */
1348 goto loop;
1349 }
1350 }
1351 splx(s);
1352
1353metasync:
1354 tv = time;
1355 if (vp->v_flag & VSYSTEM) {
1356 if (VTOF(vp)->fcbBTCBPtr != NULL)
1357 BTSetLastSync(VTOF(vp), tv.tv_sec);
1358 cp->c_flag &= ~(C_ACCESS | C_CHANGE | C_MODIFIED | C_UPDATE);
1359 } else /* User file */ {
1360 retval = VOP_UPDATE(ap->a_vp, &tv, &tv, wait);
1361
1362 /* When MNT_WAIT is requested push out any delayed meta data */
1363 if ((retval == 0) && wait && cp->c_hint &&
1364 !ISSET(cp->c_flag, C_DELETED | C_NOEXISTS)) {
1365 hfs_metasync(VTOHFS(vp), cp->c_hint, ap->a_p);
1366 }
55e303ae
A
1367
1368 // make sure that we've really been called from the user
1369 // fsync() and if so push out any pending transactions
1370 // that this file might is a part of (and get them on
1371 // stable storage).
1372 if (vp->v_flag & VFULLFSYNC) {
1373 if (hfsmp->jnl) {
1374 journal_flush(hfsmp->jnl);
1375 } else {
1376 VOP_IOCTL(hfsmp->hfs_devvp, DKIOCSYNCHRONIZECACHE, NULL, FWRITE, NOCRED, ap->a_p);
1377 }
1378 }
9bccf70c
A
1379 }
1380
1381 return (retval);
1382}
1383
1384/* Sync an hfs catalog b-tree node */
1385static int
1386hfs_metasync(struct hfsmount *hfsmp, daddr_t node, struct proc *p)
1387{
1388 struct vnode *vp;
1389 struct buf *bp;
1390 struct buf *nbp;
1391 int s;
1392
1393 vp = HFSTOVCB(hfsmp)->catalogRefNum;
1394
b4c24cb9
A
1395 // XXXdbg - don't need to do this on a journaled volume
1396 if (hfsmp->jnl) {
1397 return 0;
1398 }
1399
9bccf70c
A
1400 if (hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_EXCLUSIVE, p) != 0)
1401 return (0);
1402
1403 /*
1404 * Look for a matching node that has been delayed
1405 * but is not part of a set (B_LOCKED).
1406 */
1407 s = splbio();
1408 for (bp = vp->v_dirtyblkhd.lh_first; bp; bp = nbp) {
1409 nbp = bp->b_vnbufs.le_next;
1410 if (bp->b_flags & B_BUSY)
1411 continue;
1412 if (bp->b_lblkno == node) {
1413 if (bp->b_flags & B_LOCKED)
1414 break;
1415
1416 bremfree(bp);
1417 bp->b_flags |= B_BUSY;
1418 splx(s);
1419 (void) VOP_BWRITE(bp);
1420 goto exit;
1421 }
1422 }
1423 splx(s);
1424exit:
1425 (void) hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_RELEASE, p);
1426
1427 return (0);
1428}
1429
1430__private_extern__
1431int
1432hfs_btsync(struct vnode *vp, int sync_transaction)
1433{
1434 struct cnode *cp = VTOC(vp);
1435 register struct buf *bp;
1436 struct timeval tv;
1437 struct buf *nbp;
b4c24cb9 1438 struct hfsmount *hfsmp = VTOHFS(vp);
9bccf70c
A
1439 int s;
1440
1441 /*
1442 * Flush all dirty buffers associated with b-tree.
1443 */
1444loop:
1445 s = splbio();
1446
1447 for (bp = vp->v_dirtyblkhd.lh_first; bp; bp = nbp) {
1448 nbp = bp->b_vnbufs.le_next;
1449 if ((bp->b_flags & B_BUSY))
1450 continue;
1451 if ((bp->b_flags & B_DELWRI) == 0)
b4c24cb9
A
1452 panic("hfs_btsync: not dirty (bp 0x%x hfsmp 0x%x)", bp, hfsmp);
1453
1454 // XXXdbg
1455 if (hfsmp->jnl && (bp->b_flags & B_LOCKED)) {
1456 if ((bp->b_flags & B_META) == 0) {
1457 panic("hfs: bp @ 0x%x is locked but not meta! jnl 0x%x\n",
1458 bp, hfsmp->jnl);
1459 }
1460 // if journal_active() returns >= 0 then the journal is ok and we
1461 // shouldn't do anything to this locked block (because it is part
1462 // of a transaction). otherwise we'll just go through the normal
1463 // code path and flush the buffer.
1464 if (journal_active(hfsmp->jnl) >= 0) {
1465 continue;
1466 }
1467 }
1468
9bccf70c
A
1469 if (sync_transaction && !(bp->b_flags & B_LOCKED))
1470 continue;
1471
1472 bremfree(bp);
1473 bp->b_flags |= B_BUSY;
1474 bp->b_flags &= ~B_LOCKED;
b4c24cb9 1475
9bccf70c
A
1476 splx(s);
1477
1478 (void) bawrite(bp);
1479
1480 goto loop;
1481 }
1482 splx(s);
1483
1484 tv = time;
1485 if ((vp->v_flag & VSYSTEM) && (VTOF(vp)->fcbBTCBPtr != NULL))
1486 (void) BTSetLastSync(VTOF(vp), tv.tv_sec);
1487 cp->c_flag &= ~(C_ACCESS | C_CHANGE | C_MODIFIED | C_UPDATE);
1488
1489 return 0;
1490}
1491
1492/*
1493 * Rmdir system call.
1494#% rmdir dvp L U U
1495#% rmdir vp L U U
1496#
1497 vop_rmdir {
1498 IN WILLRELE struct vnode *dvp;
1499 IN WILLRELE struct vnode *vp;
1500 IN struct componentname *cnp;
1501
1502 */
1503static int
1504hfs_rmdir(ap)
1505 struct vop_rmdir_args /* {
1506 struct vnode *a_dvp;
1507 struct vnode *a_vp;
1508 struct componentname *a_cnp;
1509 } */ *ap;
1510{
55e303ae
A
1511 return (hfs_removedir(ap->a_dvp, ap->a_vp, ap->a_cnp, 0));
1512}
1513
1514/*
1515 * hfs_removedir
1516 */
1517static int
1518hfs_removedir(dvp, vp, cnp, options)
1519 struct vnode *dvp;
1520 struct vnode *vp;
1521 struct componentname *cnp;
1522 int options;
1523{
1524 struct proc *p = cnp->cn_proc;
9bccf70c
A
1525 struct cnode *cp;
1526 struct cnode *dcp;
1527 struct hfsmount * hfsmp;
1528 struct timeval tv;
55e303ae 1529 cat_cookie_t cookie = {0};
b4c24cb9 1530 int error = 0, started_tr = 0, grabbed_lock = 0;
9bccf70c
A
1531
1532 cp = VTOC(vp);
1533 dcp = VTOC(dvp);
1534 hfsmp = VTOHFS(vp);
1535
1536 if (dcp == cp) {
1537 vrele(dvp);
1538 vput(vp);
1539 return (EINVAL); /* cannot remove "." */
1540 }
b4c24cb9 1541
d7e50217
A
1542#if QUOTA
1543 (void)hfs_getinoquota(cp);
1544#endif
b4c24cb9
A
1545 // XXXdbg
1546 hfs_global_shared_lock_acquire(hfsmp);
1547 grabbed_lock = 1;
1548 if (hfsmp->jnl) {
1549 if ((error = journal_start_transaction(hfsmp->jnl)) != 0) {
1550 goto out;
1551 }
1552 started_tr = 1;
1553 }
1554
55e303ae
A
1555 if (!(options & HFSRM_SKIP_RESERVE)) {
1556 /*
1557 * Reserve some space in the Catalog file.
1558 */
1559 if ((error = cat_preflight(hfsmp, CAT_DELETE, &cookie, p))) {
1560 goto out;
1561 }
1562 }
1563
9bccf70c
A
1564 /*
1565 * Verify the directory is empty (and valid).
1566 * (Rmdir ".." won't be valid since
1567 * ".." will contain a reference to
1568 * the current directory and thus be
1569 * non-empty.)
1570 */
1571 if (cp->c_entries != 0) {
1572 error = ENOTEMPTY;
1573 goto out;
1574 }
1575 if ((dcp->c_flags & APPEND) || (cp->c_flags & (IMMUTABLE | APPEND))) {
1576 error = EPERM;
1577 goto out;
1578 }
1579
1580 /* Remove the entry from the namei cache: */
1581 cache_purge(vp);
1582
1583 /* Lock catalog b-tree */
1584 error = hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_EXCLUSIVE, p);
1585 if (error) goto out;
1586
1587 if (cp->c_entries > 0)
1588 panic("hfs_rmdir: attempting to delete a non-empty directory!");
1589 /* Remove entry from catalog */
1590 error = cat_delete(hfsmp, &cp->c_desc, &cp->c_attr);
1591
1592 /* Unlock catalog b-tree */
1593 (void) hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_RELEASE, p);
1594 if (error) goto out;
1595
1596#if QUOTA
d7e50217 1597 (void)hfs_chkiq(cp, -1, NOCRED, 0);
9bccf70c
A
1598#endif /* QUOTA */
1599
1600 /* The parent lost a child */
1601 if (dcp->c_entries > 0)
1602 dcp->c_entries--;
1603 if (dcp->c_nlink > 0)
1604 dcp->c_nlink--;
1605 dcp->c_flag |= C_CHANGE | C_UPDATE;
1606 tv = time;
1607 (void) VOP_UPDATE(dvp, &tv, &tv, 0);
55e303ae 1608 HFS_KNOTE(dvp, NOTE_WRITE | NOTE_LINK);
b4c24cb9 1609
9bccf70c
A
1610 hfs_volupdate(hfsmp, VOL_RMDIR, (dcp->c_cnid == kHFSRootFolderID));
1611
1612 cp->c_mode = 0; /* Makes the vnode go away...see inactive */
1613 cp->c_flag |= C_NOEXISTS;
1614out:
55e303ae 1615 if (!(options & HFSRM_PARENT_LOCKED)) {
9bccf70c 1616 vput(dvp);
55e303ae
A
1617 }
1618 HFS_KNOTE(vp, NOTE_DELETE);
9bccf70c 1619 vput(vp);
b4c24cb9 1620
55e303ae
A
1621 if (!(options & HFSRM_SKIP_RESERVE)) {
1622 cat_postflight(hfsmp, &cookie, p);
1623 }
b4c24cb9
A
1624 // XXXdbg
1625 if (started_tr) {
1626 journal_end_transaction(hfsmp->jnl);
1627 }
1628 if (grabbed_lock) {
1629 hfs_global_shared_lock_release(hfsmp);
1630 }
1631
9bccf70c
A
1632 return (error);
1633}
1634
1635/*
1636
1637#% remove dvp L U U
1638#% remove vp L U U
1639#
1640 vop_remove {
1641 IN WILLRELE struct vnode *dvp;
1642 IN WILLRELE struct vnode *vp;
1643 IN struct componentname *cnp;
1644
1645 */
1646
1647static int
1648hfs_remove(ap)
1649 struct vop_remove_args /* {
1650 struct vnode *a_dvp;
1651 struct vnode *a_vp;
1652 struct componentname *a_cnp;
1653 } */ *ap;
1654{
55e303ae
A
1655 return (hfs_removefile(ap->a_dvp, ap->a_vp, ap->a_cnp, 0));
1656}
1657
1658
1659
1660/*
1661 * hfs_removefile
1662 *
1663 * Similar to hfs_remove except there are additional options.
1664 */
1665static int
1666hfs_removefile(dvp, vp, cnp, options)
1667 struct vnode *dvp;
1668 struct vnode *vp;
1669 struct componentname *cnp;
1670 int options;
1671{
9bccf70c
A
1672 struct vnode *rvp = NULL;
1673 struct cnode *cp;
1674 struct cnode *dcp;
1675 struct hfsmount *hfsmp;
55e303ae 1676 struct proc *p = cnp->cn_proc;
9bccf70c
A
1677 int dataforkbusy = 0;
1678 int rsrcforkbusy = 0;
1679 int truncated = 0;
1680 struct timeval tv;
55e303ae 1681 cat_cookie_t cookie = {0};
9bccf70c 1682 int error = 0;
b4c24cb9 1683 int started_tr = 0, grabbed_lock = 0;
55e303ae 1684 int refcount, isbigfile = 0;
9bccf70c 1685
55e303ae
A
1686 /* Directories should call hfs_rmdir! */
1687 if (vp->v_type == VDIR) {
1688 error = EISDIR;
1689 goto out;
1690 }
9bccf70c
A
1691
1692 cp = VTOC(vp);
1693 dcp = VTOC(dvp);
1694 hfsmp = VTOHFS(vp);
1695
1696 if (cp->c_parentcnid != dcp->c_cnid) {
1697 error = EINVAL;
1698 goto out;
1699 }
1700
1701 /* Make sure a remove is permitted */
1702 if ((cp->c_flags & (IMMUTABLE | APPEND)) ||
1703 (VTOC(dvp)->c_flags & APPEND) ||
1704 VNODE_IS_RSRC(vp)) {
1705 error = EPERM;
1706 goto out;
b4c24cb9 1707 }
9bccf70c
A
1708
1709 /*
1710 * Aquire a vnode for a non-empty resource fork.
1711 * (needed for VOP_TRUNCATE)
1712 */
1713 if (cp->c_blocks - VTOF(vp)->ff_blocks) {
1714 error = hfs_vgetrsrc(hfsmp, vp, &rvp, p);
1715 if (error)
1716 goto out;
1717 }
1718
b4c24cb9
A
1719 // XXXdbg - don't allow deleting the journal or journal_info_block
1720 if (hfsmp->jnl && cp->c_datafork) {
1721 struct HFSPlusExtentDescriptor *extd;
1722
55e303ae 1723 extd = &cp->c_datafork->ff_extents[0];
b4c24cb9
A
1724 if (extd->startBlock == HFSTOVCB(hfsmp)->vcbJinfoBlock || extd->startBlock == hfsmp->jnl_start) {
1725 error = EPERM;
1726 goto out;
1727 }
1728 }
1729
9bccf70c
A
1730 /*
1731 * Check if this file is being used.
1732 *
1733 * The namei done for the remove took a reference on the
1734 * vnode (vp). And we took a ref on the resource vnode (rvp).
1735 * Hence set 1 in the tookref parameter of ubc_isinuse().
1736 */
55e303ae
A
1737 if (VTOC(vp)->c_flag & C_VPREFHELD) {
1738 refcount = 2;
1739 } else {
1740 refcount = 1;
1741 }
1742 if (UBCISVALID(vp) && ubc_isinuse(vp, refcount))
9bccf70c
A
1743 dataforkbusy = 1;
1744 if (rvp && UBCISVALID(rvp) && ubc_isinuse(rvp, 1))
1745 rsrcforkbusy = 1;
1746
55e303ae
A
1747 // need this to check if we have to break the deletion
1748 // into multiple pieces
1749 isbigfile = (VTOC(vp)->c_datafork->ff_size >= HFS_BIGFILE_SIZE);
1750
9bccf70c
A
1751 /*
1752 * Carbon semantics prohibit deleting busy files.
1753 * (enforced when NODELETEBUSY is requested)
1754 */
1755 if ((dataforkbusy || rsrcforkbusy) &&
55e303ae
A
1756 ((cnp->cn_flags & NODELETEBUSY) ||
1757 (hfsmp->hfs_privdir_desc.cd_cnid == 0))) {
9bccf70c
A
1758 error = EBUSY;
1759 goto out;
1760 }
1761
d7e50217
A
1762#if QUOTA
1763 (void)hfs_getinoquota(cp);
1764#endif /* QUOTA */
1765
b4c24cb9
A
1766 // XXXdbg
1767 hfs_global_shared_lock_acquire(hfsmp);
1768 grabbed_lock = 1;
1769 if (hfsmp->jnl) {
1770 if ((error = journal_start_transaction(hfsmp->jnl)) != 0) {
1771 goto out;
1772 }
1773 started_tr = 1;
1774 }
1775
55e303ae
A
1776 if (!(options & HFSRM_SKIP_RESERVE)) {
1777 /*
1778 * Reserve some space in the Catalog file.
1779 */
1780 if ((error = cat_preflight(hfsmp, CAT_DELETE, &cookie, p))) {
1781 goto out;
1782 }
1783 }
1784
9bccf70c
A
1785 /* Remove our entry from the namei cache. */
1786 cache_purge(vp);
1787
b4c24cb9
A
1788 // XXXdbg - if we're journaled, kill any dirty symlink buffers
1789 if (hfsmp->jnl && vp->v_type == VLNK && vp->v_dirtyblkhd.lh_first) {
1790 struct buf *bp, *nbp;
1791
1792 recheck:
1793 for (bp=vp->v_dirtyblkhd.lh_first; bp; bp=nbp) {
1794 nbp = bp->b_vnbufs.le_next;
1795
1796 if ((bp->b_flags & B_BUSY)) {
1797 // if it was busy, someone else must be dealing
1798 // with it so just move on.
1799 continue;
1800 }
1801
1802 if (!(bp->b_flags & B_META)) {
1803 panic("hfs: symlink bp @ 0x%x is not marked meta-data!\n", bp);
1804 }
1805
1806 // if it's part of the current transaction, kill it.
1807 if (bp->b_flags & B_LOCKED) {
1808 bremfree(bp);
1809 bp->b_flags |= B_BUSY;
1810 journal_kill_block(hfsmp->jnl, bp);
1811 goto recheck;
1812 }
1813 }
1814 }
1815 // XXXdbg
1816
9bccf70c
A
1817 /*
1818 * Truncate any non-busy forks. Busy forks will
1819 * get trucated when their vnode goes inactive.
1820 *
1821 * (Note: hard links are truncated in VOP_INACTIVE)
1822 */
1823 if ((cp->c_flag & C_HARDLINK) == 0) {
1824 int mode = cp->c_mode;
1825
55e303ae 1826 if (!dataforkbusy && !isbigfile && cp->c_datafork->ff_blocks != 0) {
9bccf70c
A
1827 cp->c_mode = 0; /* Suppress VOP_UPDATES */
1828 error = VOP_TRUNCATE(vp, (off_t)0, IO_NDELAY, NOCRED, p);
1829 cp->c_mode = mode;
1830 if (error)
1831 goto out;
1832 truncated = 1;
1833 }
1834 if (!rsrcforkbusy && rvp) {
1835 cp->c_mode = 0; /* Suppress VOP_UPDATES */
1836 error = VOP_TRUNCATE(rvp, (off_t)0, IO_NDELAY, NOCRED, p);
1837 cp->c_mode = mode;
90556fb8 1838 if (error)
9bccf70c 1839 goto out;
9bccf70c
A
1840 truncated = 1;
1841 }
1842 }
1843 /*
1844 * There are 3 remove cases to consider:
1845 * 1. File is a hardlink ==> remove the link
1846 * 2. File is busy (in use) ==> move/rename the file
1847 * 3. File is not in use ==> remove the file
1848 */
1849
1850 if (cp->c_flag & C_HARDLINK) {
1851 struct cat_desc desc;
1852
55e303ae
A
1853 if ((cnp->cn_flags & HASBUF) == 0 ||
1854 cnp->cn_nameptr[0] == '\0') {
9bccf70c
A
1855 error = ENOENT; /* name missing! */
1856 goto out;
1857 }
1858
1859 /* Setup a descriptor for the link */
1860 bzero(&desc, sizeof(desc));
55e303ae
A
1861 desc.cd_nameptr = cnp->cn_nameptr;
1862 desc.cd_namelen = cnp->cn_namelen;
9bccf70c
A
1863 desc.cd_parentcnid = dcp->c_cnid;
1864 /* XXX - if cnid is out of sync then the wrong thread rec will get deleted. */
1865 desc.cd_cnid = cp->c_cnid;
1866
1867 /* Lock catalog b-tree */
1868 error = hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_EXCLUSIVE, p);
1869 if (error)
1870 goto out;
1871
b4c24cb9 1872 /* Delete the link record */
9bccf70c
A
1873 error = cat_delete(hfsmp, &desc, &cp->c_attr);
1874
b4c24cb9
A
1875 if ((error == 0) && (--cp->c_nlink < 1)) {
1876 char inodename[32];
1877 char delname[32];
1878 struct cat_desc to_desc;
1879 struct cat_desc from_desc;
1880
1881 /*
1882 * This is now esentially an open deleted file.
1883 * Rename it to reflect this state which makes
1884 * orphan file cleanup easier (see hfs_remove_orphans).
1885 * Note: a rename failure here is not fatal.
1886 */
1887 MAKE_INODE_NAME(inodename, cp->c_rdev);
1888 bzero(&from_desc, sizeof(from_desc));
1889 from_desc.cd_nameptr = inodename;
1890 from_desc.cd_namelen = strlen(inodename);
55e303ae 1891 from_desc.cd_parentcnid = hfsmp->hfs_privdir_desc.cd_cnid;
b4c24cb9
A
1892 from_desc.cd_flags = 0;
1893 from_desc.cd_cnid = cp->c_fileid;
1894
1895 MAKE_DELETED_NAME(delname, cp->c_fileid);
1896 bzero(&to_desc, sizeof(to_desc));
1897 to_desc.cd_nameptr = delname;
1898 to_desc.cd_namelen = strlen(delname);
55e303ae 1899 to_desc.cd_parentcnid = hfsmp->hfs_privdir_desc.cd_cnid;
b4c24cb9
A
1900 to_desc.cd_flags = 0;
1901 to_desc.cd_cnid = cp->c_fileid;
1902
1903 (void) cat_rename(hfsmp, &from_desc, &hfsmp->hfs_privdir_desc,
1904 &to_desc, (struct cat_desc *)NULL);
1905 cp->c_flag |= C_DELETED;
1906 }
1907
9bccf70c
A
1908 /* Unlock the Catalog */
1909 (void) hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_RELEASE, p);
1910
9bccf70c
A
1911 if (error != 0)
1912 goto out;
1913
1914 cp->c_flag |= C_CHANGE;
b4c24cb9
A
1915 tv = time;
1916 (void) VOP_UPDATE(vp, &tv, &tv, 0);
1917
9bccf70c
A
1918 hfs_volupdate(hfsmp, VOL_RMFILE, (dcp->c_cnid == kHFSRootFolderID));
1919
55e303ae 1920 } else if (dataforkbusy || rsrcforkbusy || isbigfile) {
9bccf70c
A
1921 char delname[32];
1922 struct cat_desc to_desc;
1923 struct cat_desc todir_desc;
1924
1925 /*
1926 * Orphan this file (move to hidden directory).
1927 */
1928 bzero(&todir_desc, sizeof(todir_desc));
1929 todir_desc.cd_parentcnid = 2;
1930
1931 MAKE_DELETED_NAME(delname, cp->c_fileid);
1932 bzero(&to_desc, sizeof(to_desc));
1933 to_desc.cd_nameptr = delname;
1934 to_desc.cd_namelen = strlen(delname);
55e303ae 1935 to_desc.cd_parentcnid = hfsmp->hfs_privdir_desc.cd_cnid;
9bccf70c
A
1936 to_desc.cd_flags = 0;
1937 to_desc.cd_cnid = cp->c_cnid;
1938
1939 /* Lock catalog b-tree */
1940 error = hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_EXCLUSIVE, p);
b4c24cb9
A
1941 if (error)
1942 goto out;
9bccf70c
A
1943
1944 error = cat_rename(hfsmp, &cp->c_desc, &todir_desc,
1945 &to_desc, (struct cat_desc *)NULL);
1946
b4c24cb9
A
1947 // XXXdbg - only bump this count if we were successful
1948 if (error == 0) {
1949 hfsmp->hfs_privdir_attr.ca_entries++;
1950 }
9bccf70c
A
1951 (void)cat_update(hfsmp, &hfsmp->hfs_privdir_desc,
1952 &hfsmp->hfs_privdir_attr, NULL, NULL);
1953
1954 /* Unlock the Catalog */
1955 (void) hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_RELEASE, p);
1956 if (error) goto out;
1957
1958 cp->c_flag |= C_CHANGE | C_DELETED | C_NOEXISTS;
1959 --cp->c_nlink;
b4c24cb9
A
1960 tv = time;
1961 (void) VOP_UPDATE(vp, &tv, &tv, 0);
9bccf70c
A
1962
1963 } else /* Not busy */ {
1964
90556fb8 1965 if (cp->c_blocks > 0) {
55e303ae
A
1966#if 0
1967 panic("hfs_remove: attempting to delete a non-empty file!");
1968#else
1969 printf("hfs_remove: attempting to delete a non-empty file %s\n",
1970 cp->c_desc.cd_nameptr);
90556fb8
A
1971 error = EBUSY;
1972 goto out;
55e303ae 1973#endif
90556fb8 1974 }
9bccf70c 1975
b4c24cb9
A
1976 /* Lock catalog b-tree */
1977 error = hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_EXCLUSIVE, p);
1978 if (error)
1979 goto out;
1980
9bccf70c
A
1981 error = cat_delete(hfsmp, &cp->c_desc, &cp->c_attr);
1982
90556fb8 1983 if (error && error != ENXIO && error != ENOENT && truncated) {
55e303ae
A
1984 if ((cp->c_datafork && cp->c_datafork->ff_size != 0) ||
1985 (cp->c_rsrcfork && cp->c_rsrcfork->ff_size != 0)) {
b4c24cb9 1986 panic("hfs: remove: couldn't delete a truncated file! (%d, data sz %lld; rsrc sz %lld)",
55e303ae 1987 error, cp->c_datafork->ff_size, cp->c_rsrcfork->ff_size);
b4c24cb9
A
1988 } else {
1989 printf("hfs: remove: strangely enough, deleting truncated file %s (%d) got err %d\n",
1990 cp->c_desc.cd_nameptr, cp->c_attr.ca_fileid, error);
1991 }
1992 }
9bccf70c
A
1993
1994 /* Unlock the Catalog */
1995 (void) hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_RELEASE, p);
1996 if (error) goto out;
1997
1998#if QUOTA
d7e50217 1999 (void)hfs_chkiq(cp, -1, NOCRED, 0);
9bccf70c
A
2000#endif /* QUOTA */
2001
2002 cp->c_mode = 0;
55e303ae 2003 truncated = 0; // because the catalog entry is gone
9bccf70c
A
2004 cp->c_flag |= C_CHANGE | C_NOEXISTS;
2005 --cp->c_nlink;
2006 hfs_volupdate(hfsmp, VOL_RMFILE, (dcp->c_cnid == kHFSRootFolderID));
2007 }
2008
2009 /*
2010 * All done with this cnode's descriptor...
2011 *
2012 * Note: all future catalog calls for this cnode must be
2013 * by fileid only. This is OK for HFS (which doesn't have
2014 * file thread records) since HFS doesn't support hard
2015 * links or the removal of busy files.
2016 */
2017 cat_releasedesc(&cp->c_desc);
2018
2019 /* In all three cases the parent lost a child */
2020 if (dcp->c_entries > 0)
2021 dcp->c_entries--;
2022 if (dcp->c_nlink > 0)
2023 dcp->c_nlink--;
2024 dcp->c_flag |= C_CHANGE | C_UPDATE;
2025 tv = time;
2026 (void) VOP_UPDATE(dvp, &tv, &tv, 0);
55e303ae 2027 HFS_KNOTE(dvp, NOTE_WRITE);
9bccf70c 2028
55e303ae
A
2029out:
2030 /* All done with component name... */
2031 if ((options & HFSRM_SAVE_NAME) == 0 &&
2032 (cnp != 0) &&
2033 (cnp->cn_flags & (HASBUF | SAVENAME)) == (HASBUF | SAVENAME)) {
2034 char *tmp = cnp->cn_pnbuf;
2035 cnp->cn_pnbuf = NULL;
2036 cnp->cn_flags &= ~HASBUF;
2037 FREE_ZONE(tmp, cnp->cn_pnlen, M_NAMEI);
d7e50217
A
2038 }
2039
55e303ae
A
2040 if (!(options & HFSRM_SKIP_RESERVE)) {
2041 cat_postflight(hfsmp, &cookie, p);
b4c24cb9 2042 }
b4c24cb9 2043
9bccf70c
A
2044 /* Commit the truncation to the catalog record */
2045 if (truncated) {
55e303ae
A
2046 cp->c_flag |= C_CHANGE | C_UPDATE | C_FORCEUPDATE;
2047 tv = time;
2048 (void) VOP_UPDATE(vp, &tv, &tv, 0);
9bccf70c 2049 }
b4c24cb9
A
2050
2051 // XXXdbg
2052 if (started_tr) {
2053 journal_end_transaction(hfsmp->jnl);
2054 }
2055 if (grabbed_lock) {
2056 hfs_global_shared_lock_release(hfsmp);
2057 }
2058
55e303ae
A
2059 HFS_KNOTE(vp, NOTE_DELETE);
2060 if (rvp) {
2061 HFS_KNOTE(rvp, NOTE_DELETE);
2062 vrele(rvp);
2063 };
2064
2065 if (error) {
2066 vput(vp);
2067 } else {
2068 VOP_UNLOCK(vp, 0, p);
2069 // XXXdbg - try to prevent the lost ubc_info panic
2070 if ((cp->c_flag & C_HARDLINK) == 0 || cp->c_nlink == 0) {
2071 (void) ubc_uncache(vp);
2072 }
2073 vrele(vp);
2074 }
2075 if (!(options & HFSRM_PARENT_LOCKED)) {
2076 vput(dvp);
2077 }
2078
9bccf70c
A
2079 return (error);
2080}
2081
2082
2083__private_extern__ void
2084replace_desc(struct cnode *cp, struct cat_desc *cdp)
2085{
2086 /* First release allocated name buffer */
2087 if (cp->c_desc.cd_flags & CD_HASBUF && cp->c_desc.cd_nameptr != 0) {
2088 char *name = cp->c_desc.cd_nameptr;
2089
2090 cp->c_desc.cd_nameptr = 0;
2091 cp->c_desc.cd_namelen = 0;
2092 cp->c_desc.cd_flags &= ~CD_HASBUF;
55e303ae 2093 remove_name(name);
9bccf70c
A
2094 }
2095 bcopy(cdp, &cp->c_desc, sizeof(cp->c_desc));
2096
2097 /* Cnode now owns the name buffer */
2098 cdp->cd_nameptr = 0;
2099 cdp->cd_namelen = 0;
2100 cdp->cd_flags &= ~CD_HASBUF;
2101}
2102
2103
2104/*
2105#
55e303ae
A
2106#% rename fdvp U U U
2107#% rename fvp U U U
2108#% rename tdvp L U U
2109#% rename tvp X U U
9bccf70c 2110#
9bccf70c
A
2111*/
2112/*
2113 * Rename a cnode.
2114 *
2115 * The VFS layer guarantees that source and destination will
2116 * either both be directories, or both not be directories.
2117 *
2118 * When the target is a directory, hfs_rename must ensure
2119 * that it is empty.
d7e50217
A
2120 *
2121 * The rename system call is responsible for freeing
2122 * the pathname buffers (ie no need to call VOP_ABORTOP).
9bccf70c
A
2123 */
2124
2125static int
2126hfs_rename(ap)
2127 struct vop_rename_args /* {
2128 struct vnode *a_fdvp;
2129 struct vnode *a_fvp;
2130 struct componentname *a_fcnp;
2131 struct vnode *a_tdvp;
2132 struct vnode *a_tvp;
2133 struct componentname *a_tcnp;
2134 } */ *ap;
2135{
2136 struct vnode *tvp = ap->a_tvp;
2137 struct vnode *tdvp = ap->a_tdvp;
2138 struct vnode *fvp = ap->a_fvp;
2139 struct vnode *fdvp = ap->a_fdvp;
2140 struct componentname *tcnp = ap->a_tcnp;
2141 struct componentname *fcnp = ap->a_fcnp;
d7e50217 2142 struct proc *p = fcnp->cn_proc;
9bccf70c
A
2143 struct cnode *fcp = NULL;
2144 struct cnode *fdcp = NULL;
d7e50217 2145 struct cnode *tdcp = VTOC(tdvp);
9bccf70c
A
2146 struct cat_desc from_desc;
2147 struct cat_desc to_desc;
2148 struct cat_desc out_desc;
55e303ae 2149 struct hfsmount *hfsmp = NULL;
9bccf70c 2150 struct timeval tv;
55e303ae
A
2151 cat_cookie_t cookie = {0};
2152 int fdvp_locked, fvp_locked, tdvp_locked, tvp_locked;
d7e50217
A
2153 int tvp_deleted;
2154 int started_tr = 0, grabbed_lock = 0;
2155 int error = 0;
2156
d7e50217
A
2157
2158 /* Establish our vnode lock state. */
2159 tdvp_locked = 1;
55e303ae 2160 tvp_locked = (tvp != 0);
d7e50217
A
2161 fdvp_locked = 0;
2162 fvp_locked = 0;
2163 tvp_deleted = 0;
9bccf70c 2164
55e303ae
A
2165 /*
2166 * Check for cross-device rename.
2167 */
2168 if ((fvp->v_mount != tdvp->v_mount) ||
2169 (tvp && (fvp->v_mount != tvp->v_mount))) {
2170 error = EXDEV;
2171 goto out;
2172 }
2173
9bccf70c
A
2174 /*
2175 * When fvp matches tvp they must be case variants
d7e50217
A
2176 * or hard links.
2177 *
55e303ae
A
2178 * In some cases tvp will be locked in other cases
2179 * it be unlocked with no reference. Normalize the
2180 * state here (unlocked with a reference) so that
2181 * we can exit in a known state.
9bccf70c
A
2182 */
2183 if (fvp == tvp) {
55e303ae
A
2184 if (VOP_ISLOCKED(tvp) &&
2185 (VTOC(tvp)->c_lock.lk_lockholder == p->p_pid) &&
2186 (VTOC(tvp)->c_lock.lk_lockthread == current_thread())) {
2187 vput(tvp);
9bccf70c
A
2188 }
2189 tvp = NULL;
55e303ae
A
2190 tvp_locked = 0;
2191
d7e50217 2192 /*
55e303ae
A
2193 * If this a hard link with different parents
2194 * and its not a case variant then keep tvp
2195 * around for removal.
d7e50217
A
2196 */
2197 if ((VTOC(fvp)->c_flag & C_HARDLINK) &&
2198 ((fdvp != tdvp) ||
2199 (hfs_namecmp(fcnp->cn_nameptr, fcnp->cn_namelen,
2200 tcnp->cn_nameptr, tcnp->cn_namelen) != 0))) {
2201 tvp = fvp;
55e303ae 2202 vref(tvp);
d7e50217 2203 }
9bccf70c
A
2204 }
2205
2206 /*
55e303ae
A
2207 * The following edge case is caught here:
2208 * (to cannot be a descendent of from)
2209 *
2210 * o fdvp
2211 * /
2212 * /
2213 * o fvp
2214 * \
2215 * \
2216 * o tdvp
2217 * /
2218 * /
2219 * o tvp
9bccf70c 2220 */
55e303ae
A
2221 if (tdcp->c_parentcnid == VTOC(fvp)->c_cnid) {
2222 error = EINVAL;
2223 goto out;
2224 }
2225
2226 /*
2227 * The following two edge cases are caught here:
2228 * (note tvp is not empty)
2229 *
2230 * o tdvp o tdvp
2231 * / /
2232 * / /
2233 * o tvp tvp o fdvp
2234 * \ \
2235 * \ \
2236 * o fdvp o fvp
2237 * /
2238 * /
2239 * o fvp
2240 */
2241 if (tvp && (tvp->v_type == VDIR) && (VTOC(tvp)->c_entries != 0)) {
2242 error = ENOTEMPTY;
2243 goto out;
2244 }
2245
2246 /*
2247 * The following edge case is caught here:
2248 * (the from child and parent are the same)
2249 *
2250 * o tdvp
2251 * /
2252 * /
2253 * fdvp o fvp
2254 */
2255 if (fdvp == fvp) {
2256 error = EINVAL;
d7e50217 2257 goto out;
9bccf70c
A
2258 }
2259
2260 /*
d7e50217 2261 * Make sure "from" vnode and its parent are changeable.
9bccf70c 2262 */
d7e50217
A
2263 if ((VTOC(fvp)->c_flags & (IMMUTABLE | APPEND)) ||
2264 (VTOC(fdvp)->c_flags & APPEND)) {
2265 error = EPERM;
2266 goto out;
9bccf70c
A
2267 }
2268
55e303ae 2269 hfsmp = VTOHFS(tdvp);
9bccf70c
A
2270
2271 /*
d7e50217
A
2272 * If the destination parent directory is "sticky", then the
2273 * user must own the parent directory, or the destination of
2274 * the rename, otherwise the destination may not be changed
2275 * (except by root). This implements append-only directories.
2276 *
55e303ae
A
2277 * Note that checks for immutable and write access are done
2278 * by the call to VOP_REMOVE.
9bccf70c 2279 */
d7e50217
A
2280 if (tvp && (tdcp->c_mode & S_ISTXT) &&
2281 (tcnp->cn_cred->cr_uid != 0) &&
2282 (tcnp->cn_cred->cr_uid != tdcp->c_uid) &&
2283 (hfs_owner_rights(hfsmp, VTOC(tvp)->c_uid, tcnp->cn_cred, p, false)) ) {
55e303ae
A
2284 error = EPERM;
2285 goto out;
9bccf70c
A
2286 }
2287
55e303ae
A
2288#if QUOTA
2289 if (tvp)
2290 (void)hfs_getinoquota(VTOC(tvp));
2291#endif
2292
d7e50217 2293 /*
55e303ae 2294 * Lock all the vnodes before starting a journal transaction.
9bccf70c 2295 */
9bccf70c 2296
d7e50217 2297 /*
55e303ae 2298 * Simple case (same parent) - just lock child (fvp).
d7e50217 2299 */
55e303ae
A
2300 if (fdvp == tdvp) {
2301 if (error = vn_lock(fvp, LK_EXCLUSIVE | LK_RETRY, p))
2302 goto out;
2303 fvp_locked = 1;
2304 goto vnlocked;
9bccf70c 2305 }
d7e50217
A
2306
2307 /*
55e303ae
A
2308 * If fdvp is the parent of tdvp then we'll need to
2309 * drop tdvp's lock before acquiring a lock on fdvp.
2310 *
2311 * fdvp
2312 * o
2313 * / \
2314 * / \
2315 * tdvp o o fvp
2316 * \
2317 * \
2318 * o tvp
2319 *
2320 *
2321 * If the parent directories are unrelated then we'll
2322 * need to aquire their vnode locks in vnode address
2323 * order. Otherwise we can race with another rename
2324 * call that involves the same vnodes except that to
2325 * and from are switched and potentially deadlock.
2326 * [ie rename("a/b", "c/d") vs rename("c/d", "a/b")]
2327 *
2328 * If its not either of the two above cases then we
2329 * can safely lock fdvp and fvp.
b4c24cb9 2330 */
55e303ae
A
2331 if ((VTOC(fdvp)->c_cnid == VTOC(tdvp)->c_parentcnid) ||
2332 ((VTOC(tdvp)->c_cnid != VTOC(fdvp)->c_parentcnid) &&
2333 (fdvp < tdvp))) {
b4c24cb9 2334
55e303ae
A
2335 /* Drop locks on tvp and tdvp */
2336 if (tvp_locked) {
2337 VOP_UNLOCK(tvp, 0, p);
2338 tvp_locked = 0;
9bccf70c 2339 }
55e303ae
A
2340 VOP_UNLOCK(tdvp, 0, p);
2341 tdvp_locked = 0;
2342
2343 /* Aquire locks in correct order */
2344 if ((error = vn_lock(fdvp, LK_EXCLUSIVE | LK_RETRY, p)))
2345 goto out;
2346 fdvp_locked = 1;
2347 if ((error = vn_lock(tdvp, LK_EXCLUSIVE | LK_RETRY, p)))
2348 goto out;
2349 tdvp_locked = 1;
2350
9bccf70c 2351 /*
55e303ae
A
2352 * Now that the parents are locked only one thread
2353 * can continue. So the lock order of the children
2354 * doesn't really matter
9bccf70c 2355 */
55e303ae
A
2356 if (tvp == fvp) {
2357 if ((error = vn_lock(tvp, LK_EXCLUSIVE | LK_RETRY, p)))
d7e50217 2358 goto out;
55e303ae
A
2359 tvp_locked = 1;
2360 } else {
2361 if (tvp) {
2362 if ((error = vn_lock(tvp, LK_EXCLUSIVE | LK_RETRY, p)))
d7e50217 2363 goto out;
55e303ae 2364 tvp_locked = 1;
d7e50217 2365 }
55e303ae 2366 if ((error = vn_lock(fvp, LK_EXCLUSIVE | LK_RETRY, p)))
d7e50217 2367 goto out;
55e303ae 2368 fvp_locked = 1;
d7e50217 2369 }
9bccf70c 2370
55e303ae
A
2371 } else /* OK to lock fdvp and fvp */ {
2372 if ((error = vn_lock(fdvp, LK_EXCLUSIVE | LK_RETRY, p)))
2373 goto out;
2374 fdvp_locked = 1;
d7e50217
A
2375 if (error = vn_lock(fvp, LK_EXCLUSIVE | LK_RETRY, p))
2376 goto out;
55e303ae
A
2377 if (tvp == fvp)
2378 tvp_locked = 1;
2379 else
2380 fvp_locked = 1;
d7e50217 2381 }
9bccf70c 2382
55e303ae 2383vnlocked:
d7e50217
A
2384 fdcp = VTOC(fdvp);
2385 fcp = VTOC(fvp);
9bccf70c 2386
55e303ae
A
2387 /*
2388 * While fvp is still locked, purge it from the name cache and
2389 * grab it's c_cnid value. Note that the removal of tvp (below)
2390 * can drop fvp's lock when fvp == tvp.
2391 */
2392 cache_purge(fvp);
2393
90556fb8
A
2394 /*
2395 * When a file moves out of "Cleanup At Startup"
2396 * we can drop its NODUMP status.
2397 */
2398 if ((fcp->c_flags & UF_NODUMP) &&
2399 (fvp->v_type == VREG) &&
2400 (fdvp != tdvp) &&
2401 (fdcp->c_desc.cd_nameptr != NULL) &&
55e303ae 2402 (strcmp(fdcp->c_desc.cd_nameptr, CARBON_TEMP_DIR_NAME) == 0)) {
90556fb8
A
2403 fcp->c_flags &= ~UF_NODUMP;
2404 fcp->c_flag |= C_CHANGE;
2405 tv = time;
2406 (void) VOP_UPDATE(fvp, &tv, &tv, 0);
2407 }
2408
9bccf70c
A
2409 bzero(&from_desc, sizeof(from_desc));
2410 from_desc.cd_nameptr = fcnp->cn_nameptr;
2411 from_desc.cd_namelen = fcnp->cn_namelen;
2412 from_desc.cd_parentcnid = fdcp->c_cnid;
2413 from_desc.cd_flags = fcp->c_desc.cd_flags & ~(CD_HASBUF | CD_DECOMPOSED);
2414 from_desc.cd_cnid = fcp->c_cnid;
d7e50217 2415
9bccf70c
A
2416 bzero(&to_desc, sizeof(to_desc));
2417 to_desc.cd_nameptr = tcnp->cn_nameptr;
2418 to_desc.cd_namelen = tcnp->cn_namelen;
2419 to_desc.cd_parentcnid = tdcp->c_cnid;
2420 to_desc.cd_flags = fcp->c_desc.cd_flags & ~(CD_HASBUF | CD_DECOMPOSED);
2421 to_desc.cd_cnid = fcp->c_cnid;
2422
55e303ae
A
2423 hfs_global_shared_lock_acquire(hfsmp);
2424 grabbed_lock = 1;
2425 if (hfsmp->jnl) {
2426 if ((error = journal_start_transaction(hfsmp->jnl)) != 0) {
2427 goto out;
2428 }
2429 started_tr = 1;
2430 }
2431
2432 /*
2433 * Reserve some space in the Catalog file.
2434 */
2435 if ((error = cat_preflight(hfsmp, CAT_RENAME + CAT_DELETE, &cookie, p))) {
2436 goto out;
2437 }
2438
2439 /*
2440 * If the destination exists then it needs to be removed.
2441 */
2442
2443 if (tvp) {
2444 if (tvp != fvp)
2445 cache_purge(tvp);
2446 /*
2447 * Note that hfs_removedir and hfs_removefile
2448 * will keep tdvp locked with a reference.
2449 * But tvp will lose its lock and reference.
2450 */
2451 if (tvp->v_type == VDIR)
2452 error = hfs_removedir(tdvp, tvp, tcnp, HFSRM_RENAMEOPTS);
2453 else
2454 error = hfs_removefile(tdvp, tvp, tcnp, HFSRM_RENAMEOPTS);
2455
2456 if (tvp == fvp)
2457 fvp_locked = 0;
2458 tvp = NULL;
2459 tvp_locked = 0;
2460 tvp_deleted = 1;
2461 if (error)
2462 goto out;
2463 }
2464
2465 /*
2466 * All done with tvp and fvp
2467 */
2468
9bccf70c 2469 /* Lock catalog b-tree */
d7e50217
A
2470 error = hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_EXCLUSIVE, p);
2471 if (error)
2472 goto out;
2473
2474 error = cat_rename(hfsmp, &from_desc, &tdcp->c_desc, &to_desc, &out_desc);
9bccf70c
A
2475
2476 /* Unlock catalog b-tree */
2477 (void) hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_RELEASE, p);
55e303ae
A
2478
2479 if (error) {
d7e50217 2480 goto out;
55e303ae 2481 }
9bccf70c 2482
d7e50217 2483 /* Update cnode's catalog descriptor */
55e303ae
A
2484 if (fvp_locked) {
2485 replace_desc(fcp, &out_desc);
2486 fcp->c_parentcnid = tdcp->c_cnid;
2487 fcp->c_hint = 0;
2488 }
9bccf70c 2489
d7e50217
A
2490 hfs_volupdate(hfsmp, fvp->v_type == VDIR ? VOL_RMDIR : VOL_RMFILE,
2491 (fdcp->c_cnid == kHFSRootFolderID));
2492 hfs_volupdate(hfsmp, fvp->v_type == VDIR ? VOL_MKDIR : VOL_MKFILE,
2493 (tdcp->c_cnid == kHFSRootFolderID));
2494
d7e50217 2495 /* Update both parent directories. */
9bccf70c 2496 tv = time;
b4c24cb9 2497 if (fdvp != tdvp) {
d7e50217
A
2498 tdcp->c_nlink++;
2499 tdcp->c_entries++;
2500 if (fdcp->c_nlink > 0)
2501 fdcp->c_nlink--;
2502 if (fdcp->c_entries > 0)
2503 fdcp->c_entries--;
2504 fdcp->c_flag |= C_CHANGE | C_UPDATE;
b4c24cb9
A
2505 (void) VOP_UPDATE(fdvp, &tv, &tv, 0);
2506 }
d7e50217
A
2507 tdcp->c_childhint = out_desc.cd_hint; /* Cache directory's location */
2508 tdcp->c_flag |= C_CHANGE | C_UPDATE;
b4c24cb9
A
2509 (void) VOP_UPDATE(tdvp, &tv, &tv, 0);
2510
d7e50217 2511out:
55e303ae
A
2512 if (hfsmp) {
2513 cat_postflight(hfsmp, &cookie, p);
2514 }
b4c24cb9
A
2515 if (started_tr) {
2516 journal_end_transaction(hfsmp->jnl);
2517 }
2518 if (grabbed_lock) {
2519 hfs_global_shared_lock_release(hfsmp);
2520 }
2521
55e303ae
A
2522 /* Note that if hfs_removedir or hfs_removefile was invoked above they will already have
2523 generated a NOTE_WRITE for tdvp and a NOTE_DELETE for tvp.
2524 */
2525 if (error == 0) {
2526 HFS_KNOTE(fvp, NOTE_RENAME);
2527 HFS_KNOTE(fdvp, NOTE_WRITE);
2528 if (tdvp != fdvp) HFS_KNOTE(tdvp, NOTE_WRITE);
2529 };
d7e50217
A
2530 if (fvp_locked) {
2531 VOP_UNLOCK(fvp, 0, p);
b4c24cb9 2532 }
d7e50217
A
2533 if (fdvp_locked) {
2534 VOP_UNLOCK(fdvp, 0, p);
b4c24cb9 2535 }
d7e50217
A
2536 if (tdvp_locked) {
2537 VOP_UNLOCK(tdvp, 0, p);
2538 }
55e303ae
A
2539 if (tvp_locked) {
2540 VOP_UNLOCK(tvp, 0, p);
b4c24cb9
A
2541 }
2542
9bccf70c 2543 vrele(fvp);
d7e50217 2544 vrele(fdvp);
55e303ae
A
2545 if (tvp)
2546 vrele(tvp);
d7e50217 2547 vrele(tdvp);
b4c24cb9 2548
d7e50217 2549 /* After tvp is removed the only acceptable error is EIO */
55e303ae 2550 if (error && tvp_deleted)
d7e50217
A
2551 error = EIO;
2552
2553 return (error);
9bccf70c
A
2554}
2555
2556
2557
2558/*
2559 * Mkdir system call
2560#% mkdir dvp L U U
2561#% mkdir vpp - L -
2562#
2563 vop_mkdir {
2564 IN WILLRELE struct vnode *dvp;
2565 OUT struct vnode **vpp;
2566 IN struct componentname *cnp;
2567 IN struct vattr *vap;
2568
2569 We are responsible for freeing the namei buffer,
2570 it is done in hfs_makenode()
2571*/
2572
2573static int
2574hfs_mkdir(ap)
2575 struct vop_mkdir_args /* {
2576 struct vnode *a_dvp;
2577 struct vnode **a_vpp;
2578 struct componentname *a_cnp;
2579 struct vattr *a_vap;
2580 } */ *ap;
2581{
2582 struct vattr *vap = ap->a_vap;
2583
2584 return (hfs_makenode(MAKEIMODE(vap->va_type, vap->va_mode),
2585 ap->a_dvp, ap->a_vpp, ap->a_cnp));
2586}
2587
2588
2589/*
2590 * symlink -- make a symbolic link
2591#% symlink dvp L U U
2592#% symlink vpp - U -
2593#
2594# XXX - note that the return vnode has already been VRELE'ed
2595# by the filesystem layer. To use it you must use vget,
2596# possibly with a further namei.
2597#
2598 vop_symlink {
2599 IN WILLRELE struct vnode *dvp;
2600 OUT WILLRELE struct vnode **vpp;
2601 IN struct componentname *cnp;
2602 IN struct vattr *vap;
2603 IN char *target;
2604
2605 We are responsible for freeing the namei buffer,
2606 it is done in hfs_makenode().
2607
2608*/
2609
2610static int
2611hfs_symlink(ap)
2612 struct vop_symlink_args /* {
2613 struct vnode *a_dvp;
2614 struct vnode **a_vpp;
2615 struct componentname *a_cnp;
2616 struct vattr *a_vap;
2617 char *a_target;
2618 } */ *ap;
2619{
2620 register struct vnode *vp, **vpp = ap->a_vpp;
b4c24cb9 2621 struct hfsmount *hfsmp;
9bccf70c
A
2622 struct filefork *fp;
2623 int len, error;
2624 struct buf *bp = NULL;
2625
2626 /* HFS standard disks don't support symbolic links */
2627 if (VTOVCB(ap->a_dvp)->vcbSigWord != kHFSPlusSigWord) {
2628 VOP_ABORTOP(ap->a_dvp, ap->a_cnp);
2629 vput(ap->a_dvp);
2630 return (EOPNOTSUPP);
2631 }
2632
2633 /* Check for empty target name */
2634 if (ap->a_target[0] == 0) {
2635 VOP_ABORTOP(ap->a_dvp, ap->a_cnp);
2636 vput(ap->a_dvp);
2637 return (EINVAL);
2638 }
2639
b4c24cb9
A
2640
2641 hfsmp = VTOHFS(ap->a_dvp);
2642
9bccf70c
A
2643 /* Create the vnode */
2644 if ((error = hfs_makenode(S_IFLNK | ap->a_vap->va_mode,
b4c24cb9 2645 ap->a_dvp, vpp, ap->a_cnp))) {
9bccf70c 2646 return (error);
b4c24cb9 2647 }
9bccf70c
A
2648
2649 vp = *vpp;
2650 len = strlen(ap->a_target);
2651 fp = VTOF(vp);
9bccf70c 2652
d7e50217
A
2653#if QUOTA
2654 (void)hfs_getinoquota(VTOC(vp));
2655#endif /* QUOTA */
2656
b4c24cb9
A
2657 // XXXdbg
2658 hfs_global_shared_lock_acquire(hfsmp);
2659 if (hfsmp->jnl) {
2660 if ((error = journal_start_transaction(hfsmp->jnl)) != 0) {
2661 hfs_global_shared_lock_release(hfsmp);
d7e50217
A
2662 vput(vp);
2663 return error;
b4c24cb9
A
2664 }
2665 }
2666
9bccf70c
A
2667 /* Allocate space for the link */
2668 error = VOP_TRUNCATE(vp, len, IO_NOZEROFILL,
2669 ap->a_cnp->cn_cred, ap->a_cnp->cn_proc);
2670 if (error)
2671 goto out; /* XXX need to remove link */
2672
2673 /* Write the link to disk */
2674 bp = getblk(vp, 0, roundup((int)fp->ff_size, VTOHFS(vp)->hfs_phys_block_size),
2675 0, 0, BLK_META);
b4c24cb9
A
2676 if (hfsmp->jnl) {
2677 journal_modify_block_start(hfsmp->jnl, bp);
2678 }
9bccf70c
A
2679 bzero(bp->b_data, bp->b_bufsize);
2680 bcopy(ap->a_target, bp->b_data, len);
b4c24cb9
A
2681 if (hfsmp->jnl) {
2682 journal_modify_block_end(hfsmp->jnl, bp);
2683 } else {
2684 bawrite(bp);
2685 }
9bccf70c 2686out:
b4c24cb9
A
2687 if (hfsmp->jnl) {
2688 journal_end_transaction(hfsmp->jnl);
2689 }
2690 hfs_global_shared_lock_release(hfsmp);
9bccf70c
A
2691 vput(vp);
2692 return (error);
2693}
2694
2695
2696/*
2697 * Dummy dirents to simulate the "." and ".." entries of the directory
2698 * in a hfs filesystem. HFS doesn't provide these on disk. Note that
2699 * the size of these entries is the smallest needed to represent them
2700 * (only 12 byte each).
2701 */
2702static hfsdotentry rootdots[2] = {
2703 {
2704 1, /* d_fileno */
2705 sizeof(struct hfsdotentry), /* d_reclen */
2706 DT_DIR, /* d_type */
2707 1, /* d_namlen */
2708 "." /* d_name */
2709 },
2710 {
2711 1, /* d_fileno */
2712 sizeof(struct hfsdotentry), /* d_reclen */
2713 DT_DIR, /* d_type */
2714 2, /* d_namlen */
2715 ".." /* d_name */
2716 }
2717};
2718
2719/* 4.3 Note:
2720* There is some confusion as to what the semantics of uio_offset are.
2721* In ufs, it represents the actual byte offset within the directory
2722* "file." HFS, however, just uses it as an entry counter - essentially
2723* assuming that it has no meaning except to the hfs_readdir function.
2724* This approach would be more efficient here, but some callers may
2725* assume the uio_offset acts like a byte offset. NFS in fact
2726* monkeys around with the offset field a lot between readdir calls.
2727*
2728* The use of the resid uiop->uio_resid and uiop->uio_iov->iov_len
2729* fields is a mess as well. The libc function readdir() returns
2730* NULL (indicating the end of a directory) when either
2731* the getdirentries() syscall (which calls this and returns
2732* the size of the buffer passed in less the value of uiop->uio_resid)
2733* returns 0, or a direct record with a d_reclen of zero.
2734* nfs_server.c:rfs_readdir(), on the other hand, checks for the end
2735* of the directory by testing uiop->uio_resid == 0. The solution
2736* is to pad the size of the last struct direct in a given
2737* block to fill the block if we are not at the end of the directory.
2738*/
2739
2740
2741/*
2742 * NOTE: We require a minimal buffer size of DIRBLKSIZ for two reasons. One, it is the same value
2743 * returned be stat() call as the block size. This is mentioned in the man page for getdirentries():
2744 * "Nbytes must be greater than or equal to the block size associated with the file,
2745 * see stat(2)". Might as well settle on the same size of ufs. Second, this makes sure there is enough
2746 * room for the . and .. entries that have to added manually.
2747 */
2748
2749/*
2750#% readdir vp L L L
2751#
2752vop_readdir {
2753 IN struct vnode *vp;
2754 INOUT struct uio *uio;
2755 IN struct ucred *cred;
2756 INOUT int *eofflag;
2757 OUT int *ncookies;
2758 INOUT u_long **cookies;
2759 */
2760static int
2761hfs_readdir(ap)
2762 struct vop_readdir_args /* {
2763 struct vnode *vp;
2764 struct uio *uio;
2765 struct ucred *cred;
2766 int *eofflag;
2767 int *ncookies;
2768 u_long **cookies;
2769 } */ *ap;
2770{
2771 register struct uio *uio = ap->a_uio;
2772 struct cnode *cp = VTOC(ap->a_vp);
2773 struct hfsmount *hfsmp = VTOHFS(ap->a_vp);
2774 struct proc *p = current_proc();
2775 off_t off = uio->uio_offset;
2776 int retval = 0;
2777 int eofflag = 0;
b4c24cb9
A
2778 void *user_start = NULL;
2779 int user_len;
55e303ae
A
2780
2781 int ncookies=0;
2782 u_long *cookies=NULL;
2783 u_long *cookiep=NULL;
b4c24cb9 2784
9bccf70c
A
2785 /* We assume it's all one big buffer... */
2786 if (uio->uio_iovcnt > 1 || uio->uio_resid < AVERAGE_HFSDIRENTRY_SIZE)
2787 return EINVAL;
2788
b4c24cb9
A
2789 // XXXdbg
2790 // We have to lock the user's buffer here so that we won't
2791 // fault on it after we've acquired a shared lock on the
2792 // catalog file. The issue is that you can get a 3-way
2793 // deadlock if someone else starts a transaction and then
2794 // tries to lock the catalog file but can't because we're
2795 // here and we can't service our page fault because VM is
2796 // blocked trying to start a transaction as a result of
2797 // trying to free up pages for our page fault. It's messy
2798 // but it does happen on dual-procesors that are paging
2799 // heavily (see radar 3082639 for more info). By locking
2800 // the buffer up-front we prevent ourselves from faulting
2801 // while holding the shared catalog file lock.
2802 //
2803 // Fortunately this and hfs_search() are the only two places
2804 // currently (10/30/02) that can fault on user data with a
2805 // shared lock on the catalog file.
2806 //
2807 if (hfsmp->jnl && uio->uio_segflg == UIO_USERSPACE) {
2808 user_start = uio->uio_iov->iov_base;
2809 user_len = uio->uio_iov->iov_len;
2810
2811 if ((retval = vslock(user_start, user_len)) != 0) {
2812 return retval;
2813 }
2814 }
2815
9bccf70c
A
2816 /* Create the entries for . and .. */
2817 if (uio->uio_offset < sizeof(rootdots)) {
2818 caddr_t dep;
2819 size_t dotsize;
2820
2821 rootdots[0].d_fileno = cp->c_cnid;
2822 rootdots[1].d_fileno = cp->c_parentcnid;
2823
2824 if (uio->uio_offset == 0) {
2825 dep = (caddr_t) &rootdots[0];
2826 dotsize = 2* sizeof(struct hfsdotentry);
2827 } else if (uio->uio_offset == sizeof(struct hfsdotentry)) {
2828 dep = (caddr_t) &rootdots[1];
2829 dotsize = sizeof(struct hfsdotentry);
2830 } else {
2831 retval = EINVAL;
2832 goto Exit;
2833 }
2834
2835 retval = uiomove(dep, dotsize, uio);
2836 if (retval != 0)
2837 goto Exit;
2838 }
2839
55e303ae
A
2840 if (ap->a_ncookies != NULL) {
2841 /*
2842 * These cookies are handles that allow NFS to restart
2843 * scanning through a directory. If a directory is large
2844 * enough, NFS will issue a successive readdir() with a
2845 * uio->uio_offset that is equal to one of these cookies.
2846 *
2847 * The cookies that we generate are synthesized byte-offsets.
2848 * The offset is where the dirent the dirent would be if the
2849 * directory were an array of packed dirent structs. It is
2850 * synthetic because that's not how directories are stored in
2851 * HFS but other code expects that the cookie is a byte offset.
2852 *
2853 * We have to pre-allocate the cookies because cat_getdirentries()
2854 * is the only one that can properly synthesize the offsets (since
2855 * it may have to skip over entries and only it knows the true
2856 * virtual offset of any particular directory entry). So we allocate
2857 * a cookie table here and pass it in to cat_getdirentries().
2858 *
2859 * Note that the handling of "." and ".." is mostly done here but
2860 * cat_getdirentries() is aware of.
2861 *
2862 * Only the NFS server uses cookies so fortunately this code is
2863 * not executed unless the NFS server is issuing the readdir
2864 * request.
2865 *
2866 * Also note that the NFS server is the one responsible for
2867 * free'ing the cookies even though we allocated them. Ick.
2868 *
2869 * We allocate a reasonable number of entries for the size of
2870 * the buffer that we're going to fill in. cat_getdirentries()
2871 * is smart enough to not overflow if there's more room in the
2872 * buffer but not enough room in the cookie table.
2873 */
2874 if (uio->uio_segflg != UIO_SYSSPACE)
2875 panic("hfs_readdir: unexpected uio from NFS server");
2876
2877 ncookies = uio->uio_iov->iov_len / (AVERAGE_HFSDIRENTRY_SIZE/2);
2878 MALLOC(cookies, u_long *, ncookies * sizeof(u_long), M_TEMP, M_WAITOK);
2879
2880 *ap->a_ncookies = ncookies;
2881 *ap->a_cookies = cookies;
2882 }
2883
9bccf70c
A
2884 /* If there are no children then we're done */
2885 if (cp->c_entries == 0) {
2886 eofflag = 1;
2887 retval = 0;
55e303ae
A
2888 if (cookies) {
2889 cookies[0] = 0;
2890 cookies[1] = sizeof(struct hfsdotentry);
2891 }
9bccf70c
A
2892 goto Exit;
2893 }
2894
2895 /* Lock catalog b-tree */
2896 retval = hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_SHARED, p);
2897 if (retval) goto Exit;
2898
55e303ae 2899 retval = cat_getdirentries(hfsmp, &cp->c_desc, cp->c_entries, uio, &eofflag, cookies, ncookies);
9bccf70c
A
2900
2901 /* Unlock catalog b-tree */
2902 (void) hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_RELEASE, p);
2903
2904 if (retval != E_NONE) {
2905 goto Exit;
2906 }
2907
2908 /* were we already past eof ? */
2909 if (uio->uio_offset == off) {
2910 retval = E_NONE;
2911 goto Exit;
2912 }
2913
2914 cp->c_flag |= C_ACCESS;
9bccf70c
A
2915
2916Exit:;
b4c24cb9
A
2917 if (hfsmp->jnl && user_start) {
2918 vsunlock(user_start, user_len, TRUE);
2919 }
2920
9bccf70c
A
2921 if (ap->a_eofflag)
2922 *ap->a_eofflag = eofflag;
2923
2924 return (retval);
2925}
2926
2927
2928/*
2929 * Return target name of a symbolic link
2930#% readlink vp L L L
2931#
2932 vop_readlink {
2933 IN struct vnode *vp;
2934 INOUT struct uio *uio;
2935 IN struct ucred *cred;
2936 */
2937
2938static int
2939hfs_readlink(ap)
2940 struct vop_readlink_args /* {
2941 struct vnode *a_vp;
2942 struct uio *a_uio;
2943 struct ucred *a_cred;
2944 } */ *ap;
2945{
2946 int retval;
2947 struct vnode *vp = ap->a_vp;
2948 struct cnode *cp;
2949 struct filefork *fp;
2950
2951 if (vp->v_type != VLNK)
2952 return (EINVAL);
2953
2954 cp = VTOC(vp);
2955 fp = VTOF(vp);
2956
2957 /* Zero length sym links are not allowed */
2958 if (fp->ff_size == 0 || fp->ff_size > MAXPATHLEN) {
2959 VTOVCB(vp)->vcbFlags |= kHFS_DamagedVolume;
2960 return (EINVAL);
2961 }
2962
2963 /* Cache the path so we don't waste buffer cache resources */
2964 if (fp->ff_symlinkptr == NULL) {
2965 struct buf *bp = NULL;
2966
2967 MALLOC(fp->ff_symlinkptr, char *, fp->ff_size, M_TEMP, M_WAITOK);
2968 retval = meta_bread(vp, 0,
2969 roundup((int)fp->ff_size,
2970 VTOHFS(vp)->hfs_phys_block_size),
2971 ap->a_cred, &bp);
2972 if (retval) {
2973 if (bp)
2974 brelse(bp);
2975 if (fp->ff_symlinkptr) {
2976 FREE(fp->ff_symlinkptr, M_TEMP);
2977 fp->ff_symlinkptr = NULL;
2978 }
2979 return (retval);
2980 }
2981 bcopy(bp->b_data, fp->ff_symlinkptr, (size_t)fp->ff_size);
2982 if (bp) {
b4c24cb9
A
2983 if (VTOHFS(vp)->jnl && (bp->b_flags & B_LOCKED) == 0) {
2984 bp->b_flags |= B_INVAL; /* data no longer needed */
2985 }
9bccf70c
A
2986 brelse(bp);
2987 }
2988 }
2989 retval = uiomove((caddr_t)fp->ff_symlinkptr, (int)fp->ff_size, ap->a_uio);
55e303ae
A
2990#if 1
2991 /*
2992 * Keep track blocks read
2993 */
2994 if ((VTOHFS(vp)->hfc_stage == HFC_RECORDING) && (retval == 0)) {
2995
2996 /*
2997 * If this file hasn't been seen since the start of
2998 * the current sampling period then start over.
2999 */
3000 if (cp->c_atime < VTOHFS(vp)->hfc_timebase)
3001 VTOF(vp)->ff_bytesread = fp->ff_size;
3002 else
3003 VTOF(vp)->ff_bytesread += fp->ff_size;
3004
3005 // if (VTOF(vp)->ff_bytesread > fp->ff_size)
3006 // cp->c_flag |= C_ACCESS;
d7e50217 3007 }
55e303ae
A
3008#endif
3009 return (retval);
9bccf70c
A
3010}
3011
9bccf70c
A
3012/*
3013 * Lock an cnode. If its already locked, set the WANT bit and sleep.
3014#% lock vp U L U
3015#
3016 vop_lock {
3017 IN struct vnode *vp;
3018 IN int flags;
3019 IN struct proc *p;
3020 */
3021
3022static int
3023hfs_lock(ap)
3024 struct vop_lock_args /* {
3025 struct vnode *a_vp;
3026 int a_flags;
3027 struct proc *a_p;
3028 } */ *ap;
3029{
3030 struct vnode *vp = ap->a_vp;
3031 struct cnode *cp = VTOC(vp);
3032
9bccf70c
A
3033 return (lockmgr(&cp->c_lock, ap->a_flags, &vp->v_interlock, ap->a_p));
3034}
3035
3036/*
3037 * Unlock an cnode.
3038#% unlock vp L U L
3039#
3040 vop_unlock {
3041 IN struct vnode *vp;
3042 IN int flags;
3043 IN struct proc *p;
3044
3045 */
3046static int
3047hfs_unlock(ap)
3048 struct vop_unlock_args /* {
3049 struct vnode *a_vp;
3050 int a_flags;
3051 struct proc *a_p;
3052 } */ *ap;
3053{
3054 struct vnode *vp = ap->a_vp;
3055 struct cnode *cp = VTOC(vp);
55e303ae
A
3056#if 0
3057 if (!lockstatus(&cp->c_lock)) {
3058 printf("hfs_unlock: vnode %s wasn't locked!\n",
3059 cp->c_desc.cd_nameptr ? cp->c_desc.cd_nameptr : "");
3060 }
3061#endif
9bccf70c
A
3062 return (lockmgr(&cp->c_lock, ap->a_flags | LK_RELEASE,
3063 &vp->v_interlock, ap->a_p));
3064}
3065
3066
3067/*
3068 * Print out the contents of a cnode.
3069#% print vp = = =
3070#
3071 vop_print {
3072 IN struct vnode *vp;
3073 */
3074static int
3075hfs_print(ap)
3076 struct vop_print_args /* {
3077 struct vnode *a_vp;
3078 } */ *ap;
3079{
3080 struct vnode * vp = ap->a_vp;
3081 struct cnode *cp = VTOC(vp);
3082
3083 printf("tag VT_HFS, cnid %d, on dev %d, %d", cp->c_cnid,
3084 major(cp->c_dev), minor(cp->c_dev));
3085#if FIFO
3086 if (vp->v_type == VFIFO)
3087 fifo_printinfo(vp);
3088#endif /* FIFO */
3089 lockmgr_printinfo(&cp->c_lock);
3090 printf("\n");
3091 return (0);
3092}
3093
3094
3095/*
3096 * Check for a locked cnode.
3097#% islocked vp = = =
3098#
3099 vop_islocked {
3100 IN struct vnode *vp;
3101
3102 */
3103static int
3104hfs_islocked(ap)
3105 struct vop_islocked_args /* {
3106 struct vnode *a_vp;
3107 } */ *ap;
3108{
3109 return (lockstatus(&VTOC(ap->a_vp)->c_lock));
3110}
3111
3112/*
3113
3114#% pathconf vp L L L
3115#
3116 vop_pathconf {
3117 IN struct vnode *vp;
3118 IN int name;
3119 OUT register_t *retval;
3120
3121 */
3122static int
3123hfs_pathconf(ap)
3124 struct vop_pathconf_args /* {
3125 struct vnode *a_vp;
3126 int a_name;
3127 int *a_retval;
3128 } */ *ap;
3129{
3130 int retval = 0;
3131
3132 switch (ap->a_name) {
3133 case _PC_LINK_MAX:
3134 if (VTOVCB(ap->a_vp)->vcbSigWord == kHFSPlusSigWord)
3135 *ap->a_retval = HFS_LINK_MAX;
3136 else
3137 *ap->a_retval = 1;
3138 break;
3139 case _PC_NAME_MAX:
3140 *ap->a_retval = kHFSPlusMaxFileNameBytes; /* max # of characters x max utf8 representation */
3141 break;
3142 case _PC_PATH_MAX:
3143 *ap->a_retval = PATH_MAX; /* 1024 */
3144 break;
55e303ae
A
3145 case _PC_PIPE_BUF:
3146 *ap->a_retval = PIPE_BUF;
3147 break;
9bccf70c
A
3148 case _PC_CHOWN_RESTRICTED:
3149 *ap->a_retval = 1;
3150 break;
3151 case _PC_NO_TRUNC:
3152 *ap->a_retval = 0;
3153 break;
3154 case _PC_NAME_CHARS_MAX:
3155 *ap->a_retval = kHFSPlusMaxFileNameChars;
3156 break;
3157 case _PC_CASE_SENSITIVE:
55e303ae
A
3158 if (VTOHFS(ap->a_vp)->hfs_flags & HFS_CASE_SENSITIVE)
3159 *ap->a_retval = 1;
3160 else
3161 *ap->a_retval = 0;
9bccf70c
A
3162 break;
3163 case _PC_CASE_PRESERVING:
3164 *ap->a_retval = 1;
3165 break;
3166 default:
3167 retval = EINVAL;
3168 }
3169
3170 return (retval);
3171}
3172
3173
3174/*
3175 * Advisory record locking support
3176#% advlock vp U U U
3177#
3178 vop_advlock {
3179 IN struct vnode *vp;
3180 IN caddr_t id;
3181 IN int op;
3182 IN struct flock *fl;
3183 IN int flags;
3184
3185 */
3186static int
3187hfs_advlock(ap)
3188 struct vop_advlock_args /* {
3189 struct vnode *a_vp;
3190 caddr_t a_id;
3191 int a_op;
3192 struct flock *a_fl;
3193 int a_flags;
3194 } */ *ap;
3195{
3196 struct vnode *vp = ap->a_vp;
3197 struct flock *fl = ap->a_fl;
3198 struct hfslockf *lock;
3199 struct filefork *fork;
3200 off_t start, end;
3201 int retval;
3202
3203 /* Only regular files can have locks */
3204 if (vp->v_type != VREG)
3205 return (EISDIR);
3206
3207 fork = VTOF(ap->a_vp);
3208 /*
3209 * Avoid the common case of unlocking when cnode has no locks.
3210 */
3211 if (fork->ff_lockf == (struct hfslockf *)0) {
3212 if (ap->a_op != F_SETLK) {
3213 fl->l_type = F_UNLCK;
3214 return (0);
3215 }
3216 }
3217 /*
3218 * Convert the flock structure into a start and end.
3219 */
3220 start = 0;
3221 switch (fl->l_whence) {
3222 case SEEK_SET:
3223 case SEEK_CUR:
3224 /*
3225 * Caller is responsible for adding any necessary offset
3226 * when SEEK_CUR is used.
3227 */
3228 start = fl->l_start;
3229 break;
3230 case SEEK_END:
3231 start = fork->ff_size + fl->l_start;
3232 break;
3233 default:
3234 return (EINVAL);
3235 }
3236
9bccf70c
A
3237 if (fl->l_len == 0)
3238 end = -1;
55e303ae 3239 else if (fl->l_len > 0)
9bccf70c 3240 end = start + fl->l_len - 1;
55e303ae
A
3241 else { /* l_len is negative */
3242 end = start - 1;
3243 start += fl->l_len;
3244 }
3245 if (start < 0)
3246 return (EINVAL);
9bccf70c
A
3247
3248 /*
3249 * Create the hfslockf structure
3250 */
3251 MALLOC(lock, struct hfslockf *, sizeof *lock, M_LOCKF, M_WAITOK);
3252 lock->lf_start = start;
3253 lock->lf_end = end;
3254 lock->lf_id = ap->a_id;
3255 lock->lf_fork = fork;
3256 lock->lf_type = fl->l_type;
3257 lock->lf_next = (struct hfslockf *)0;
3258 TAILQ_INIT(&lock->lf_blkhd);
3259 lock->lf_flags = ap->a_flags;
3260 /*
3261 * Do the requested operation.
3262 */
3263 switch(ap->a_op) {
3264 case F_SETLK:
3265 retval = hfs_setlock(lock);
3266 break;
3267 case F_UNLCK:
3268 retval = hfs_clearlock(lock);
3269 FREE(lock, M_LOCKF);
3270 break;
3271 case F_GETLK:
3272 retval = hfs_getlock(lock, fl);
3273 FREE(lock, M_LOCKF);
3274 break;
3275 default:
3276 retval = EINVAL;
3277 _FREE(lock, M_LOCKF);
3278 break;
3279 }
3280
3281 return (retval);
3282}
3283
3284
3285
3286/*
3287 * Update the access, modified, and node change times as specified
3288 * by the C_ACCESS, C_UPDATE, and C_CHANGE flags respectively. The
3289 * C_MODIFIED flag is used to specify that the node needs to be
3290 * updated but that the times have already been set. The access and
3291 * modified times are input parameters but the node change time is
3292 * always taken from the current time. If waitfor is set, then wait
3293 * for the disk write of the node to complete.
3294 */
3295/*
3296#% update vp L L L
3297 IN struct vnode *vp;
3298 IN struct timeval *access;
3299 IN struct timeval *modify;
3300 IN int waitfor;
3301*/
3302static int
3303hfs_update(ap)
3304 struct vop_update_args /* {
3305 struct vnode *a_vp;
3306 struct timeval *a_access;
3307 struct timeval *a_modify;
3308 int a_waitfor;
3309 } */ *ap;
3310{
3311 struct vnode *vp = ap->a_vp;
3312 struct cnode *cp = VTOC(ap->a_vp);
3313 struct proc *p;
3314 struct cat_fork *dataforkp = NULL;
3315 struct cat_fork *rsrcforkp = NULL;
3316 struct cat_fork datafork;
3317 int updateflag;
b4c24cb9 3318 struct hfsmount *hfsmp;
9bccf70c
A
3319 int error;
3320
b4c24cb9
A
3321 hfsmp = VTOHFS(vp);
3322
9bccf70c 3323 /* XXX do we really want to clear the sytem cnode flags here???? */
55e303ae
A
3324 if (((vp->v_flag & VSYSTEM) && (cp->c_cnid < kHFSFirstUserCatalogNodeID))||
3325 (VTOHFS(vp)->hfs_flags & HFS_READ_ONLY) ||
9bccf70c
A
3326 (cp->c_mode == 0)) {
3327 cp->c_flag &= ~(C_ACCESS | C_CHANGE | C_MODIFIED | C_UPDATE);
3328 return (0);
3329 }
3330
55e303ae 3331 updateflag = cp->c_flag & (C_ACCESS | C_CHANGE | C_MODIFIED | C_UPDATE | C_FORCEUPDATE);
9bccf70c
A
3332
3333 /* Nothing to update. */
b4c24cb9 3334 if (updateflag == 0) {
9bccf70c 3335 return (0);
b4c24cb9 3336 }
9bccf70c 3337 /* HFS standard doesn't have access times. */
b4c24cb9 3338 if ((updateflag == C_ACCESS) && (VTOVCB(vp)->vcbSigWord == kHFSSigWord)) {
9bccf70c 3339 return (0);
b4c24cb9 3340 }
9bccf70c
A
3341 if (updateflag & C_ACCESS) {
3342 /*
55e303ae
A
3343 * When the access time is the only thing changing
3344 * then make sure its sufficiently newer before
3345 * committing it to disk.
9bccf70c 3346 */
55e303ae
A
3347 if ((updateflag == C_ACCESS) &&
3348 (ap->a_access->tv_sec < (cp->c_atime + ATIME_ONDISK_ACCURACY))) {
9bccf70c 3349 return (0);
9bccf70c 3350 }
55e303ae 3351 cp->c_atime = ap->a_access->tv_sec;
9bccf70c
A
3352 }
3353 if (updateflag & C_UPDATE) {
3354 cp->c_mtime = ap->a_modify->tv_sec;
3355 cp->c_mtime_nsec = ap->a_modify->tv_usec * 1000;
3356 }
3357 if (updateflag & C_CHANGE) {
3358 cp->c_ctime = time.tv_sec;
3359 /*
3360 * HFS dates that WE set must be adjusted for DST
3361 */
3362 if ((VTOVCB(vp)->vcbSigWord == kHFSSigWord) && gTimeZone.tz_dsttime) {
3363 cp->c_ctime += 3600;
3364 cp->c_mtime = cp->c_ctime;
3365 }
3366 }
3367
3368 if (cp->c_datafork)
3369 dataforkp = &cp->c_datafork->ff_data;
3370 if (cp->c_rsrcfork)
3371 rsrcforkp = &cp->c_rsrcfork->ff_data;
3372
3373 p = current_proc();
3374
3375 /*
3376 * For delayed allocations updates are
3377 * postponed until an fsync or the file
3378 * gets written to disk.
3379 *
3380 * Deleted files can defer meta data updates until inactive.
55e303ae
A
3381 *
3382 * If we're ever called with the C_FORCEUPDATE flag though
3383 * we have to do the update.
9bccf70c 3384 */
55e303ae
A
3385 if (ISSET(cp->c_flag, C_FORCEUPDATE) == 0 &&
3386 (ISSET(cp->c_flag, C_DELETED) ||
9bccf70c 3387 (dataforkp && cp->c_datafork->ff_unallocblocks) ||
55e303ae 3388 (rsrcforkp && cp->c_rsrcfork->ff_unallocblocks))) {
9bccf70c 3389 if (updateflag & (C_CHANGE | C_UPDATE))
b4c24cb9 3390 hfs_volupdate(hfsmp, VOL_UPDATE, 0);
9bccf70c
A
3391 cp->c_flag &= ~(C_ACCESS | C_CHANGE | C_UPDATE);
3392 cp->c_flag |= C_MODIFIED;
b4c24cb9 3393
55e303ae
A
3394 HFS_KNOTE(vp, NOTE_ATTRIB);
3395
9bccf70c
A
3396 return (0);
3397 }
3398
b4c24cb9
A
3399
3400 // XXXdbg
3401 hfs_global_shared_lock_acquire(hfsmp);
3402 if (hfsmp->jnl) {
3403 if ((error = journal_start_transaction(hfsmp->jnl)) != 0) {
3404 hfs_global_shared_lock_release(hfsmp);
3405 return error;
3406 }
3407 }
3408
3409
9bccf70c
A
3410 /*
3411 * For files with invalid ranges (holes) the on-disk
3412 * field representing the size of the file (cf_size)
3413 * must be no larger than the start of the first hole.
3414 */
3415 if (dataforkp && !CIRCLEQ_EMPTY(&cp->c_datafork->ff_invalidranges)) {
3416 bcopy(dataforkp, &datafork, sizeof(datafork));
3417 datafork.cf_size = CIRCLEQ_FIRST(&cp->c_datafork->ff_invalidranges)->rl_start;
3418 dataforkp = &datafork;
55e303ae
A
3419 } else if (dataforkp && (cp->c_datafork->ff_unallocblocks != 0)) {
3420 // always make sure the block count and the size
3421 // of the file match the number of blocks actually
3422 // allocated to the file on disk
3423 bcopy(dataforkp, &datafork, sizeof(datafork));
3424 // make sure that we don't assign a negative block count
3425 if (cp->c_datafork->ff_blocks < cp->c_datafork->ff_unallocblocks) {
3426 panic("hfs: ff_blocks %d is less than unalloc blocks %d\n",
3427 cp->c_datafork->ff_blocks, cp->c_datafork->ff_unallocblocks);
3428 }
3429 datafork.cf_blocks = (cp->c_datafork->ff_blocks - cp->c_datafork->ff_unallocblocks);
3430 datafork.cf_size = datafork.cf_blocks * HFSTOVCB(hfsmp)->blockSize;
3431 dataforkp = &datafork;
9bccf70c
A
3432 }
3433
3434 /*
3435 * Lock the Catalog b-tree file.
3436 * A shared lock is sufficient since an update doesn't change
3437 * the tree and the lock on vp protects the cnode.
3438 */
b4c24cb9
A
3439 error = hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_SHARED, p);
3440 if (error) {
3441 if (hfsmp->jnl) {
3442 journal_end_transaction(hfsmp->jnl);
3443 }
3444 hfs_global_shared_lock_release(hfsmp);
9bccf70c 3445 return (error);
b4c24cb9 3446 }
9bccf70c
A
3447
3448 /* XXX - waitfor is not enforced */
b4c24cb9 3449 error = cat_update(hfsmp, &cp->c_desc, &cp->c_attr, dataforkp, rsrcforkp);
9bccf70c
A
3450
3451 /* Unlock the Catalog b-tree file. */
b4c24cb9 3452 (void) hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_RELEASE, p);
9bccf70c 3453
55e303ae 3454 if (updateflag & (C_CHANGE | C_UPDATE | C_FORCEUPDATE))
b4c24cb9
A
3455 hfs_volupdate(hfsmp, VOL_UPDATE, 0);
3456
55e303ae
A
3457 /* After the updates are finished, clear the flags */
3458 cp->c_flag &= ~(C_ACCESS | C_CHANGE | C_MODIFIED | C_UPDATE | C_FORCEUPDATE);
3459
b4c24cb9
A
3460 // XXXdbg
3461 if (hfsmp->jnl) {
3462 journal_end_transaction(hfsmp->jnl);
3463 }
3464 hfs_global_shared_lock_release(hfsmp);
9bccf70c 3465
55e303ae
A
3466 HFS_KNOTE(vp, NOTE_ATTRIB);
3467
9bccf70c
A
3468 return (error);
3469}
3470
3471/*
3472 * Allocate a new node
3473 *
3474 * Upon leaving, namei buffer must be freed.
3475 *
3476 */
3477static int
3478hfs_makenode(mode, dvp, vpp, cnp)
3479 int mode;
3480 struct vnode *dvp;
3481 struct vnode **vpp;
3482 struct componentname *cnp;
3483{
3484 struct cnode *cp;
3485 struct cnode *dcp;
3486 struct vnode *tvp;
3487 struct hfsmount *hfsmp;
3488 struct timeval tv;
3489 struct proc *p;
3490 struct cat_desc in_desc, out_desc;
3491 struct cat_attr attr;
55e303ae 3492 cat_cookie_t cookie = {0};
b4c24cb9 3493 int error, started_tr = 0, grabbed_lock = 0;
9bccf70c
A
3494 enum vtype vnodetype;
3495
3496 p = cnp->cn_proc;
3497 dcp = VTOC(dvp);
3498 hfsmp = VTOHFS(dvp);
3499 *vpp = NULL;
3500 tvp = NULL;
3501 bzero(&out_desc, sizeof(out_desc));
3502
3503 if ((mode & S_IFMT) == 0)
3504 mode |= S_IFREG;
3505 vnodetype = IFTOVT(mode);
3506
3507 /* Check if unmount in progress */
3508 if (VTOVFS(dvp)->mnt_kern_flag & MNTK_UNMOUNT) {
3509 error = EPERM;
3510 goto exit;
3511 }
3512 /* Check if were out of usable disk space. */
3513 if ((suser(cnp->cn_cred, NULL) != 0) && (hfs_freeblks(hfsmp, 1) <= 0)) {
3514 error = ENOSPC;
3515 goto exit;
3516 }
3517
3518 /* Setup the default attributes */
3519 bzero(&attr, sizeof(attr));
3520 attr.ca_mode = mode;
3521 attr.ca_nlink = vnodetype == VDIR ? 2 : 1;
3522 attr.ca_mtime = time.tv_sec;
3523 attr.ca_mtime_nsec = time.tv_usec * 1000;
3524 if ((VTOVCB(dvp)->vcbSigWord == kHFSSigWord) && gTimeZone.tz_dsttime) {
3525 attr.ca_mtime += 3600; /* Same as what hfs_update does */
3526 }
3527 attr.ca_atime = attr.ca_ctime = attr.ca_itime = attr.ca_mtime;
3528 if (VTOVFS(dvp)->mnt_flag & MNT_UNKNOWNPERMISSIONS) {
3529 attr.ca_uid = hfsmp->hfs_uid;
3530 attr.ca_gid = hfsmp->hfs_gid;
3531 } else {
3532 if (vnodetype == VLNK)
3533 attr.ca_uid = dcp->c_uid;
3534 else
3535 attr.ca_uid = cnp->cn_cred->cr_uid;
3536 attr.ca_gid = dcp->c_gid;
3537 }
3538 /*
3539 * Don't tag as a special file (BLK or CHR) until *after*
3540 * hfs_getnewvnode is called. This insures that any
3541 * alias checking is defered until hfs_mknod completes.
3542 */
3543 if (vnodetype == VBLK || vnodetype == VCHR)
3544 attr.ca_mode = (attr.ca_mode & ~S_IFMT) | S_IFREG;
3545
3546 /* Tag symlinks with a type and creator. */
3547 if (vnodetype == VLNK) {
3548 struct FndrFileInfo *fip;
3549
3550 fip = (struct FndrFileInfo *)&attr.ca_finderinfo;
3551 fip->fdType = SWAP_BE32(kSymLinkFileType);
3552 fip->fdCreator = SWAP_BE32(kSymLinkCreator);
3553 }
3554 if ((attr.ca_mode & S_ISGID) &&
3555 !groupmember(dcp->c_gid, cnp->cn_cred) &&
3556 suser(cnp->cn_cred, NULL)) {
3557 attr.ca_mode &= ~S_ISGID;
3558 }
3559 if (cnp->cn_flags & ISWHITEOUT)
3560 attr.ca_flags |= UF_OPAQUE;
3561
3562 /* Setup the descriptor */
3563 bzero(&in_desc, sizeof(in_desc));
3564 in_desc.cd_nameptr = cnp->cn_nameptr;
3565 in_desc.cd_namelen = cnp->cn_namelen;
3566 in_desc.cd_parentcnid = dcp->c_cnid;
3567 in_desc.cd_flags = S_ISDIR(mode) ? CD_ISDIR : 0;
3568
b4c24cb9
A
3569 // XXXdbg
3570 hfs_global_shared_lock_acquire(hfsmp);
3571 grabbed_lock = 1;
3572 if (hfsmp->jnl) {
3573 if ((error = journal_start_transaction(hfsmp->jnl)) != 0) {
3574 goto exit;
3575 }
3576 started_tr = 1;
3577 }
3578
55e303ae
A
3579 /*
3580 * Reserve some space in the Catalog file.
3581 *
3582 * (we also add CAT_DELETE since our getnewvnode
3583 * request can cause an hfs_inactive call to
3584 * delete an unlinked file)
3585 */
3586 if ((error = cat_preflight(hfsmp, CAT_CREATE | CAT_DELETE, &cookie, p))) {
3587 goto exit;
3588 }
3589
9bccf70c
A
3590 /* Lock catalog b-tree */
3591 error = hfs_metafilelocking(VTOHFS(dvp), kHFSCatalogFileID, LK_EXCLUSIVE, p);
3592 if (error)
3593 goto exit;
3594
3595 error = cat_create(hfsmp, &in_desc, &attr, &out_desc);
3596
3597 /* Unlock catalog b-tree */
3598 (void) hfs_metafilelocking(VTOHFS(dvp), kHFSCatalogFileID, LK_RELEASE, p);
3599 if (error)
3600 goto exit;
3601
3602 /* Update the parent directory */
3603 dcp->c_childhint = out_desc.cd_hint; /* Cache directory's location */
3604 dcp->c_nlink++;
3605 dcp->c_entries++;
3606 dcp->c_flag |= C_CHANGE | C_UPDATE;
3607 tv = time;
3608 (void) VOP_UPDATE(dvp, &tv, &tv, 0);
55e303ae
A
3609 if (vnodetype == VDIR) {
3610 HFS_KNOTE(dvp, NOTE_WRITE | NOTE_LINK);
3611 } else {
3612 HFS_KNOTE(dvp, NOTE_WRITE);
3613 };
b4c24cb9 3614
9bccf70c
A
3615 hfs_volupdate(hfsmp, vnodetype == VDIR ? VOL_MKDIR : VOL_MKFILE,
3616 (dcp->c_cnid == kHFSRootFolderID));
3617
b4c24cb9
A
3618 // XXXdbg
3619 // have to end the transaction here before we call hfs_getnewvnode()
3620 // because that can cause us to try and reclaim a vnode on a different
3621 // file system which could cause us to start a transaction which can
3622 // deadlock with someone on that other file system (since we could be
3623 // holding two transaction locks as well as various vnodes and we did
3624 // not obtain the locks on them in the proper order).
3625 //
3626 // NOTE: this means that if the quota check fails or we have to update
3627 // the change time on a block-special device that those changes
3628 // will happen as part of independent transactions.
3629 //
3630 if (started_tr) {
3631 journal_end_transaction(hfsmp->jnl);
3632 started_tr = 0;
3633 }
3634 if (grabbed_lock) {
3635 hfs_global_shared_lock_release(hfsmp);
3636 grabbed_lock = 0;
3637 }
3638
9bccf70c
A
3639 /* Create a vnode for the object just created: */
3640 error = hfs_getnewvnode(hfsmp, NULL, &out_desc, 0, &attr, NULL, &tvp);
3641 if (error)
3642 goto exit;
3643
55e303ae
A
3644 // XXXdbg
3645 cache_enter(dvp, tvp, cnp);
b4c24cb9 3646
9bccf70c
A
3647#if QUOTA
3648 cp = VTOC(tvp);
3649 /*
3650 * We call hfs_chkiq with FORCE flag so that if we
3651 * fall through to the rmdir we actually have
3652 * accounted for the inode
3653 */
3654 if ((error = hfs_getinoquota(cp)) ||
3655 (error = hfs_chkiq(cp, 1, cnp->cn_cred, FORCE))) {
9bccf70c
A
3656 if (tvp->v_type == VDIR)
3657 VOP_RMDIR(dvp,tvp, cnp);
3658 else
3659 VOP_REMOVE(dvp,tvp, cnp);
b4c24cb9 3660
55e303ae
A
3661 // because VOP_RMDIR and VOP_REMOVE already
3662 // have done the vput()
3663 dvp = NULL;
3664 goto exit;
9bccf70c
A
3665 }
3666#endif /* QUOTA */
3667
3668 /*
3669 * restore vtype and mode for VBLK and VCHR
3670 */
3671 if (vnodetype == VBLK || vnodetype == VCHR) {
3672 struct cnode *cp;
3673
3674 cp = VTOC(tvp);
3675 cp->c_mode = mode;
3676 tvp->v_type = IFTOVT(mode);
3677 cp->c_flag |= C_CHANGE;
3678 tv = time;
b4c24cb9
A
3679 if ((error = VOP_UPDATE(tvp, &tv, &tv, 1))) {
3680 vput(tvp);
9bccf70c
A
3681 goto exit;
3682 }
3683 }
3684
3685 *vpp = tvp;
3686exit:
3687 cat_releasedesc(&out_desc);
3688
55e303ae 3689 cat_postflight(hfsmp, &cookie, p);
90556fb8 3690
55e303ae
A
3691 if ((cnp->cn_flags & (HASBUF | SAVESTART)) == HASBUF) {
3692 char *tmp = cnp->cn_pnbuf;
3693 cnp->cn_pnbuf = NULL;
3694 cnp->cn_flags &= ~HASBUF;
3695 FREE_ZONE(tmp, cnp->cn_pnlen, M_NAMEI);
3696 }
90556fb8
A
3697 /*
3698 * Check if a file is located in the "Cleanup At Startup"
3699 * directory. If it is then tag it as NODUMP so that we
3700 * can be lazy about zero filling data holes.
3701 */
55e303ae 3702 if ((error == 0) && dvp && (vnodetype == VREG) &&
90556fb8 3703 (dcp->c_desc.cd_nameptr != NULL) &&
55e303ae 3704 (strcmp(dcp->c_desc.cd_nameptr, CARBON_TEMP_DIR_NAME) == 0)) {
90556fb8
A
3705 struct vnode *ddvp;
3706 cnid_t parid;
3707
3708 parid = dcp->c_parentcnid;
3709 vput(dvp);
3710 dvp = NULL;
3711
3712 /*
3713 * The parent of "Cleanup At Startup" should
3714 * have the ASCII name of the userid.
3715 */
3716 if (VFS_VGET(HFSTOVFS(hfsmp), &parid, &ddvp) == 0) {
e5568f75
A
3717 if (VTOC(ddvp)->c_desc.cd_nameptr &&
3718 (cp->c_uid == strtoul(VTOC(ddvp)->c_desc.cd_nameptr, 0, 0))) {
3719 cp->c_flags |= UF_NODUMP;
3720 cp->c_flag |= C_CHANGE;
90556fb8
A
3721 }
3722 vput(ddvp);
3723 }
3724 }
90556fb8
A
3725 if (dvp)
3726 vput(dvp);
9bccf70c 3727
b4c24cb9
A
3728 if (started_tr) {
3729 journal_end_transaction(hfsmp->jnl);
3730 started_tr = 0;
3731 }
3732 if (grabbed_lock) {
3733 hfs_global_shared_lock_release(hfsmp);
3734 grabbed_lock = 0;
3735 }
3736
9bccf70c
A
3737 return (error);
3738}
3739
3740
3741static int
3742hfs_vgetrsrc(struct hfsmount *hfsmp, struct vnode *vp, struct vnode **rvpp, struct proc *p)
3743{
3744 struct vnode *rvp;
3745 struct cnode *cp = VTOC(vp);
3746 int error;
3747
3748 if ((rvp = cp->c_rsrc_vp)) {
3749 /* Use exising vnode */
3750 error = vget(rvp, 0, p);
3751 if (error) {
3752 char * name = VTOC(vp)->c_desc.cd_nameptr;
3753
3754 if (name)
3755 printf("hfs_vgetrsrc: couldn't get"
3756 " resource fork for %s\n", name);
3757 return (error);
3758 }
3759 } else {
3760 struct cat_fork rsrcfork;
3761
3762 /* Lock catalog b-tree */
3763 error = hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_SHARED, p);
3764 if (error)
3765 return (error);
3766
3767 /* Get resource fork data */
3768 error = cat_lookup(hfsmp, &cp->c_desc, 1, (struct cat_desc *)0,
3769 (struct cat_attr *)0, &rsrcfork);
3770
3771 /* Unlock the Catalog */
3772 (void) hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_RELEASE, p);
3773 if (error)
3774 return (error);
3775
3776 error = hfs_getnewvnode(hfsmp, cp, &cp->c_desc, 1, &cp->c_attr,
3777 &rsrcfork, &rvp);
3778 if (error)
3779 return (error);
3780 }
3781
3782 *rvpp = rvp;
3783 return (0);
3784}
3785
3786
55e303ae
A
3787static void
3788filt_hfsdetach(struct knote *kn)
3789{
3790 struct vnode *vp;
3791 int result;
3792 struct proc *p = current_proc();
3793
3794 vp = (struct vnode *)kn->kn_hook;
3795 if (1) { /* ! KNDETACH_VNLOCKED */
3796 result = vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p);
3797 if (result) return;
3798 };
3799
3800 result = KNOTE_DETACH(&VTOC(vp)->c_knotes, kn);
3801
3802 if (1) { /* ! KNDETACH_VNLOCKED */
3803 VOP_UNLOCK(vp, 0, p);
3804 };
3805}
3806
3807/*ARGSUSED*/
3808static int
3809filt_hfsread(struct knote *kn, long hint)
3810{
3811 struct vnode *vp = (struct vnode *)kn->kn_fp->f_data;
3812
3813 if (hint == NOTE_REVOKE) {
3814 /*
3815 * filesystem is gone, so set the EOF flag and schedule
3816 * the knote for deletion.
3817 */
3818 kn->kn_flags |= (EV_EOF | EV_ONESHOT);
3819 return (1);
3820 }
3821
3822 kn->kn_data = VTOF(vp)->ff_size - kn->kn_fp->f_offset;
3823 return (kn->kn_data != 0);
3824}
3825
3826/*ARGSUSED*/
3827static int
3828filt_hfswrite(struct knote *kn, long hint)
3829{
3830 if (hint == NOTE_REVOKE) {
3831 /*
3832 * filesystem is gone, so set the EOF flag and schedule
3833 * the knote for deletion.
3834 */
3835 kn->kn_flags |= (EV_EOF | EV_ONESHOT);
3836 }
3837
3838 kn->kn_data = 0;
3839 return (1);
3840}
3841
3842static int
3843filt_hfsvnode(struct knote *kn, long hint)
3844{
3845
3846 if (kn->kn_sfflags & hint)
3847 kn->kn_fflags |= hint;
3848 if (hint == NOTE_REVOKE) {
3849 kn->kn_flags |= EV_EOF;
3850 return (1);
3851 }
3852 return (kn->kn_fflags != 0);
3853}
3854
3855static struct filterops hfsread_filtops =
3856 { 1, NULL, filt_hfsdetach, filt_hfsread };
3857static struct filterops hfswrite_filtops =
3858 { 1, NULL, filt_hfsdetach, filt_hfswrite };
3859static struct filterops hfsvnode_filtops =
3860 { 1, NULL, filt_hfsdetach, filt_hfsvnode };
3861
3862/*
3863 #
3864 #% kqfilt_add vp L L L
3865 #
3866 vop_kqfilt_add
3867 IN struct vnode *vp;
3868 IN struct knote *kn;
3869 IN struct proc *p;
3870 */
3871static int
3872hfs_kqfilt_add(ap)
3873 struct vop_kqfilt_add_args /* {
3874 struct vnode *a_vp;
3875 struct knote *a_kn;
3876 struct proc *p;
3877 } */ *ap;
3878{
3879 struct vnode *vp = ap->a_vp;
3880 struct knote *kn = ap->a_kn;
3881
3882 switch (kn->kn_filter) {
3883 case EVFILT_READ:
3884 if (vp->v_type == VREG) {
3885 kn->kn_fop = &hfsread_filtops;
3886 } else {
3887 return EINVAL;
3888 };
3889 break;
3890 case EVFILT_WRITE:
3891 if (vp->v_type == VREG) {
3892 kn->kn_fop = &hfswrite_filtops;
3893 } else {
3894 return EINVAL;
3895 };
3896 break;
3897 case EVFILT_VNODE:
3898 kn->kn_fop = &hfsvnode_filtops;
3899 break;
3900 default:
3901 return (1);
3902 }
3903
3904 kn->kn_hook = (caddr_t)vp;
3905
3906 /* simple_lock(&vp->v_pollinfo.vpi_lock); */
3907 KNOTE_ATTACH(&VTOC(vp)->c_knotes, kn);
3908 /* simple_unlock(&vp->v_pollinfo.vpi_lock); */
3909
3910 return (0);
3911}
3912
3913/*
3914 #
3915 #% kqfilt_remove vp L L L
3916 #
3917 vop_kqfilt_remove
3918 IN struct vnode *vp;
3919 IN uintptr_t ident;
3920 IN struct proc *p;
3921 */
3922static int
3923hfs_kqfilt_remove(ap)
3924 struct vop_kqfilt_remove_args /* {
3925 struct vnode *a_vp;
3926 uintptr_t ident;
3927 struct proc *p;
3928 } */ *ap;
3929{
3930 struct vnode *vp = ap->a_vp;
3931 uintptr_t ident = ap->a_ident;
3932 int result;
3933
3934 result = ENOTSUP; /* XXX */
3935
3936 return (result);
3937}
3938
9bccf70c
A
3939/*
3940 * Wrapper for special device reads
3941 */
3942static int
3943hfsspec_read(ap)
3944 struct vop_read_args /* {
3945 struct vnode *a_vp;
3946 struct uio *a_uio;
3947 int a_ioflag;
3948 struct ucred *a_cred;
3949 } */ *ap;
3950{
3951 /*
3952 * Set access flag.
3953 */
3954 VTOC(ap->a_vp)->c_flag |= C_ACCESS;
3955 return (VOCALL (spec_vnodeop_p, VOFFSET(vop_read), ap));
3956}
3957
3958/*
3959 * Wrapper for special device writes
3960 */
3961static int
3962hfsspec_write(ap)
3963 struct vop_write_args /* {
3964 struct vnode *a_vp;
3965 struct uio *a_uio;
3966 int a_ioflag;
3967 struct ucred *a_cred;
3968 } */ *ap;
3969{
3970 /*
3971 * Set update and change flags.
3972 */
3973 VTOC(ap->a_vp)->c_flag |= C_CHANGE | C_UPDATE;
3974 return (VOCALL (spec_vnodeop_p, VOFFSET(vop_write), ap));
3975}
3976
3977/*
3978 * Wrapper for special device close
3979 *
3980 * Update the times on the cnode then do device close.
3981 */
3982static int
3983hfsspec_close(ap)
3984 struct vop_close_args /* {
3985 struct vnode *a_vp;
3986 int a_fflag;
3987 struct ucred *a_cred;
3988 struct proc *a_p;
3989 } */ *ap;
3990{
3991 struct vnode *vp = ap->a_vp;
3992 struct cnode *cp = VTOC(vp);
3993
3994 simple_lock(&vp->v_interlock);
3995 if (ap->a_vp->v_usecount > 1)
3996 CTIMES(cp, &time, &time);
3997 simple_unlock(&vp->v_interlock);
3998 return (VOCALL (spec_vnodeop_p, VOFFSET(vop_close), ap));
3999}
4000
4001#if FIFO
4002/*
4003 * Wrapper for fifo reads
4004 */
4005static int
4006hfsfifo_read(ap)
4007 struct vop_read_args /* {
4008 struct vnode *a_vp;
4009 struct uio *a_uio;
4010 int a_ioflag;
4011 struct ucred *a_cred;
4012 } */ *ap;
4013{
4014 extern int (**fifo_vnodeop_p)(void *);
4015
4016 /*
4017 * Set access flag.
4018 */
4019 VTOC(ap->a_vp)->c_flag |= C_ACCESS;
4020 return (VOCALL (fifo_vnodeop_p, VOFFSET(vop_read), ap));
4021}
4022
4023/*
4024 * Wrapper for fifo writes
4025 */
4026static int
4027hfsfifo_write(ap)
4028 struct vop_write_args /* {
4029 struct vnode *a_vp;
4030 struct uio *a_uio;
4031 int a_ioflag;
4032 struct ucred *a_cred;
4033 } */ *ap;
4034{
4035 extern int (**fifo_vnodeop_p)(void *);
4036
4037 /*
4038 * Set update and change flags.
4039 */
4040 VTOC(ap->a_vp)->c_flag |= C_CHANGE | C_UPDATE;
4041 return (VOCALL (fifo_vnodeop_p, VOFFSET(vop_write), ap));
4042}
4043
4044/*
4045 * Wrapper for fifo close
4046 *
4047 * Update the times on the cnode then do device close.
4048 */
4049static int
4050hfsfifo_close(ap)
4051 struct vop_close_args /* {
4052 struct vnode *a_vp;
4053 int a_fflag;
4054 struct ucred *a_cred;
4055 struct proc *a_p;
4056 } */ *ap;
4057{
4058 extern int (**fifo_vnodeop_p)(void *);
4059 struct vnode *vp = ap->a_vp;
4060 struct cnode *cp = VTOC(vp);
4061
4062 simple_lock(&vp->v_interlock);
4063 if (ap->a_vp->v_usecount > 1)
4064 CTIMES(cp, &time, &time);
4065 simple_unlock(&vp->v_interlock);
4066 return (VOCALL (fifo_vnodeop_p, VOFFSET(vop_close), ap));
4067}
55e303ae
A
4068
4069/*
4070 * kqfilt_add wrapper for fifos.
4071 *
4072 * Fall through to hfs kqfilt_add routines if needed
4073 */
4074int
4075hfsfifo_kqfilt_add(ap)
4076 struct vop_kqfilt_add_args *ap;
4077{
4078 extern int (**fifo_vnodeop_p)(void *);
4079 int error;
4080
4081 error = VOCALL(fifo_vnodeop_p, VOFFSET(vop_kqfilt_add), ap);
4082 if (error)
4083 error = hfs_kqfilt_add(ap);
4084 return (error);
4085}
4086
4087/*
4088 * kqfilt_remove wrapper for fifos.
4089 *
4090 * Fall through to hfs kqfilt_remove routines if needed
4091 */
4092int
4093hfsfifo_kqfilt_remove(ap)
4094 struct vop_kqfilt_remove_args *ap;
4095{
4096 extern int (**fifo_vnodeop_p)(void *);
4097 int error;
4098
4099 error = VOCALL(fifo_vnodeop_p, VOFFSET(vop_kqfilt_remove), ap);
4100 if (error)
4101 error = hfs_kqfilt_remove(ap);
4102 return (error);
4103}
4104
9bccf70c
A
4105#endif /* FIFO */
4106
4107
4108/*****************************************************************************
4109*
4110* VOP Tables
4111*
4112*****************************************************************************/
4113int hfs_cache_lookup(); /* in hfs_lookup.c */
4114int hfs_lookup(); /* in hfs_lookup.c */
4115int hfs_read(); /* in hfs_readwrite.c */
4116int hfs_write(); /* in hfs_readwrite.c */
4117int hfs_ioctl(); /* in hfs_readwrite.c */
4118int hfs_select(); /* in hfs_readwrite.c */
4119int hfs_bmap(); /* in hfs_readwrite.c */
4120int hfs_strategy(); /* in hfs_readwrite.c */
4121int hfs_truncate(); /* in hfs_readwrite.c */
4122int hfs_allocate(); /* in hfs_readwrite.c */
4123int hfs_pagein(); /* in hfs_readwrite.c */
4124int hfs_pageout(); /* in hfs_readwrite.c */
4125int hfs_search(); /* in hfs_search.c */
4126int hfs_bwrite(); /* in hfs_readwrite.c */
4127int hfs_link(); /* in hfs_link.c */
4128int hfs_blktooff(); /* in hfs_readwrite.c */
4129int hfs_offtoblk(); /* in hfs_readwrite.c */
4130int hfs_cmap(); /* in hfs_readwrite.c */
4131int hfs_getattrlist(); /* in hfs_attrlist.c */
4132int hfs_setattrlist(); /* in hfs_attrlist.c */
4133int hfs_readdirattr(); /* in hfs_attrlist.c */
4134int hfs_inactive(); /* in hfs_cnode.c */
4135int hfs_reclaim(); /* in hfs_cnode.c */
4136
4137int (**hfs_vnodeop_p)(void *);
4138
4139#define VOPFUNC int (*)(void *)
4140
4141struct vnodeopv_entry_desc hfs_vnodeop_entries[] = {
4142 { &vop_default_desc, (VOPFUNC)vn_default_error },
4143 { &vop_lookup_desc, (VOPFUNC)hfs_cache_lookup }, /* lookup */
4144 { &vop_create_desc, (VOPFUNC)hfs_create }, /* create */
4145 { &vop_mknod_desc, (VOPFUNC)hfs_mknod }, /* mknod */
4146 { &vop_open_desc, (VOPFUNC)hfs_open }, /* open */
4147 { &vop_close_desc, (VOPFUNC)hfs_close }, /* close */
4148 { &vop_access_desc, (VOPFUNC)hfs_access }, /* access */
4149 { &vop_getattr_desc, (VOPFUNC)hfs_getattr }, /* getattr */
4150 { &vop_setattr_desc, (VOPFUNC)hfs_setattr }, /* setattr */
4151 { &vop_read_desc, (VOPFUNC)hfs_read }, /* read */
4152 { &vop_write_desc, (VOPFUNC)hfs_write }, /* write */
4153 { &vop_ioctl_desc, (VOPFUNC)hfs_ioctl }, /* ioctl */
4154 { &vop_select_desc, (VOPFUNC)hfs_select }, /* select */
55e303ae 4155 { &vop_revoke_desc, (VOPFUNC)nop_revoke }, /* revoke */
9bccf70c
A
4156 { &vop_exchange_desc, (VOPFUNC)hfs_exchange }, /* exchange */
4157 { &vop_mmap_desc, (VOPFUNC)err_mmap }, /* mmap */
4158 { &vop_fsync_desc, (VOPFUNC)hfs_fsync }, /* fsync */
4159 { &vop_seek_desc, (VOPFUNC)nop_seek }, /* seek */
4160 { &vop_remove_desc, (VOPFUNC)hfs_remove }, /* remove */
4161 { &vop_link_desc, (VOPFUNC)hfs_link }, /* link */
4162 { &vop_rename_desc, (VOPFUNC)hfs_rename }, /* rename */
4163 { &vop_mkdir_desc, (VOPFUNC)hfs_mkdir }, /* mkdir */
4164 { &vop_rmdir_desc, (VOPFUNC)hfs_rmdir }, /* rmdir */
4165 { &vop_mkcomplex_desc, (VOPFUNC)err_mkcomplex }, /* mkcomplex */
4166 { &vop_getattrlist_desc, (VOPFUNC)hfs_getattrlist }, /* getattrlist */
4167 { &vop_setattrlist_desc, (VOPFUNC)hfs_setattrlist }, /* setattrlist */
4168 { &vop_symlink_desc, (VOPFUNC)hfs_symlink }, /* symlink */
4169 { &vop_readdir_desc, (VOPFUNC)hfs_readdir }, /* readdir */
4170 { &vop_readdirattr_desc, (VOPFUNC)hfs_readdirattr }, /* readdirattr */
4171 { &vop_readlink_desc, (VOPFUNC)hfs_readlink }, /* readlink */
55e303ae 4172 { &vop_abortop_desc, (VOPFUNC)nop_abortop }, /* abortop */
9bccf70c
A
4173 { &vop_inactive_desc, (VOPFUNC)hfs_inactive }, /* inactive */
4174 { &vop_reclaim_desc, (VOPFUNC)hfs_reclaim }, /* reclaim */
4175 { &vop_lock_desc, (VOPFUNC)hfs_lock }, /* lock */
4176 { &vop_unlock_desc, (VOPFUNC)hfs_unlock }, /* unlock */
4177 { &vop_bmap_desc, (VOPFUNC)hfs_bmap }, /* bmap */
4178 { &vop_strategy_desc, (VOPFUNC)hfs_strategy }, /* strategy */
4179 { &vop_print_desc, (VOPFUNC)hfs_print }, /* print */
4180 { &vop_islocked_desc, (VOPFUNC)hfs_islocked }, /* islocked */
4181 { &vop_pathconf_desc, (VOPFUNC)hfs_pathconf }, /* pathconf */
4182 { &vop_advlock_desc, (VOPFUNC)hfs_advlock }, /* advlock */
4183 { &vop_reallocblks_desc, (VOPFUNC)err_reallocblks }, /* reallocblks */
4184 { &vop_truncate_desc, (VOPFUNC)hfs_truncate }, /* truncate */
4185 { &vop_allocate_desc, (VOPFUNC)hfs_allocate }, /* allocate */
4186 { &vop_update_desc, (VOPFUNC)hfs_update }, /* update */
4187 { &vop_searchfs_desc, (VOPFUNC)hfs_search }, /* search fs */
4188 { &vop_bwrite_desc, (VOPFUNC)hfs_bwrite }, /* bwrite */
4189 { &vop_pagein_desc, (VOPFUNC)hfs_pagein }, /* pagein */
4190 { &vop_pageout_desc,(VOPFUNC) hfs_pageout }, /* pageout */
4191 { &vop_copyfile_desc, (VOPFUNC)err_copyfile }, /* copyfile */
4192 { &vop_blktooff_desc, (VOPFUNC)hfs_blktooff }, /* blktooff */
4193 { &vop_offtoblk_desc, (VOPFUNC)hfs_offtoblk }, /* offtoblk */
4194 { &vop_cmap_desc, (VOPFUNC)hfs_cmap }, /* cmap */
55e303ae
A
4195 { &vop_kqfilt_add_desc, (VOPFUNC)hfs_kqfilt_add }, /* kqfilt_add */
4196 { &vop_kqfilt_remove_desc, (VOPFUNC)hfs_kqfilt_remove }, /* kqfilt_remove */
9bccf70c
A
4197 { NULL, (VOPFUNC)NULL }
4198};
4199
4200struct vnodeopv_desc hfs_vnodeop_opv_desc =
4201{ &hfs_vnodeop_p, hfs_vnodeop_entries };
4202
4203int (**hfs_specop_p)(void *);
4204struct vnodeopv_entry_desc hfs_specop_entries[] = {
4205 { &vop_default_desc, (VOPFUNC)vn_default_error },
4206 { &vop_lookup_desc, (VOPFUNC)spec_lookup }, /* lookup */
4207 { &vop_create_desc, (VOPFUNC)spec_create }, /* create */
4208 { &vop_mknod_desc, (VOPFUNC)spec_mknod }, /* mknod */
4209 { &vop_open_desc, (VOPFUNC)spec_open }, /* open */
4210 { &vop_close_desc, (VOPFUNC)hfsspec_close }, /* close */
4211 { &vop_access_desc, (VOPFUNC)hfs_access }, /* access */
4212 { &vop_getattr_desc, (VOPFUNC)hfs_getattr }, /* getattr */
4213 { &vop_setattr_desc, (VOPFUNC)hfs_setattr }, /* setattr */
4214 { &vop_read_desc, (VOPFUNC)hfsspec_read }, /* read */
4215 { &vop_write_desc, (VOPFUNC)hfsspec_write }, /* write */
4216 { &vop_lease_desc, (VOPFUNC)spec_lease_check }, /* lease */
4217 { &vop_ioctl_desc, (VOPFUNC)spec_ioctl }, /* ioctl */
4218 { &vop_select_desc, (VOPFUNC)spec_select }, /* select */
4219 { &vop_revoke_desc, (VOPFUNC)spec_revoke }, /* revoke */
4220 { &vop_mmap_desc, (VOPFUNC)spec_mmap }, /* mmap */
4221 { &vop_fsync_desc, (VOPFUNC)hfs_fsync }, /* fsync */
4222 { &vop_seek_desc, (VOPFUNC)spec_seek }, /* seek */
4223 { &vop_remove_desc, (VOPFUNC)spec_remove }, /* remove */
4224 { &vop_link_desc, (VOPFUNC)spec_link }, /* link */
4225 { &vop_rename_desc, (VOPFUNC)spec_rename }, /* rename */
4226 { &vop_mkdir_desc, (VOPFUNC)spec_mkdir }, /* mkdir */
4227 { &vop_rmdir_desc, (VOPFUNC)spec_rmdir }, /* rmdir */
55e303ae 4228 { &vop_getattrlist_desc, (VOPFUNC)hfs_getattrlist },
9bccf70c
A
4229 { &vop_symlink_desc, (VOPFUNC)spec_symlink }, /* symlink */
4230 { &vop_readdir_desc, (VOPFUNC)spec_readdir }, /* readdir */
4231 { &vop_readlink_desc, (VOPFUNC)spec_readlink }, /* readlink */
4232 { &vop_abortop_desc, (VOPFUNC)spec_abortop }, /* abortop */
4233 { &vop_inactive_desc, (VOPFUNC)hfs_inactive }, /* inactive */
4234 { &vop_reclaim_desc, (VOPFUNC)hfs_reclaim }, /* reclaim */
4235 { &vop_lock_desc, (VOPFUNC)hfs_lock }, /* lock */
4236 { &vop_unlock_desc, (VOPFUNC)hfs_unlock }, /* unlock */
4237 { &vop_bmap_desc, (VOPFUNC)spec_bmap }, /* bmap */
4238 { &vop_strategy_desc, (VOPFUNC)spec_strategy }, /* strategy */
4239 { &vop_print_desc, (VOPFUNC)hfs_print }, /* print */
4240 { &vop_islocked_desc, (VOPFUNC)hfs_islocked }, /* islocked */
4241 { &vop_pathconf_desc, (VOPFUNC)spec_pathconf }, /* pathconf */
4242 { &vop_advlock_desc, (VOPFUNC)spec_advlock }, /* advlock */
4243 { &vop_blkatoff_desc, (VOPFUNC)spec_blkatoff }, /* blkatoff */
4244 { &vop_valloc_desc, (VOPFUNC)spec_valloc }, /* valloc */
4245 { &vop_reallocblks_desc, (VOPFUNC)spec_reallocblks }, /* reallocblks */
4246 { &vop_vfree_desc, (VOPFUNC)err_vfree }, /* vfree */
4247 { &vop_truncate_desc, (VOPFUNC)spec_truncate }, /* truncate */
4248 { &vop_update_desc, (VOPFUNC)hfs_update }, /* update */
4249 { &vop_bwrite_desc, (VOPFUNC)hfs_bwrite },
4250 { &vop_devblocksize_desc, (VOPFUNC)spec_devblocksize }, /* devblocksize */
4251 { &vop_pagein_desc, (VOPFUNC)hfs_pagein }, /* Pagein */
4252 { &vop_pageout_desc, (VOPFUNC)hfs_pageout }, /* Pageout */
4253 { &vop_copyfile_desc, (VOPFUNC)err_copyfile }, /* copyfile */
4254 { &vop_blktooff_desc, (VOPFUNC)hfs_blktooff }, /* blktooff */
4255 { &vop_offtoblk_desc, (VOPFUNC)hfs_offtoblk }, /* offtoblk */
4256 { (struct vnodeop_desc*)NULL, (VOPFUNC)NULL }
4257};
4258struct vnodeopv_desc hfs_specop_opv_desc =
4259 { &hfs_specop_p, hfs_specop_entries };
4260
4261#if FIFO
4262int (**hfs_fifoop_p)(void *);
4263struct vnodeopv_entry_desc hfs_fifoop_entries[] = {
4264 { &vop_default_desc, (VOPFUNC)vn_default_error },
4265 { &vop_lookup_desc, (VOPFUNC)fifo_lookup }, /* lookup */
4266 { &vop_create_desc, (VOPFUNC)fifo_create }, /* create */
4267 { &vop_mknod_desc, (VOPFUNC)fifo_mknod }, /* mknod */
4268 { &vop_open_desc, (VOPFUNC)fifo_open }, /* open */
4269 { &vop_close_desc, (VOPFUNC)hfsfifo_close }, /* close */
4270 { &vop_access_desc, (VOPFUNC)hfs_access }, /* access */
4271 { &vop_getattr_desc, (VOPFUNC)hfs_getattr }, /* getattr */
4272 { &vop_setattr_desc, (VOPFUNC)hfs_setattr }, /* setattr */
4273 { &vop_read_desc, (VOPFUNC)hfsfifo_read }, /* read */
4274 { &vop_write_desc, (VOPFUNC)hfsfifo_write }, /* write */
4275 { &vop_lease_desc, (VOPFUNC)fifo_lease_check }, /* lease */
4276 { &vop_ioctl_desc, (VOPFUNC)fifo_ioctl }, /* ioctl */
4277 { &vop_select_desc, (VOPFUNC)fifo_select }, /* select */
4278 { &vop_revoke_desc, (VOPFUNC)fifo_revoke }, /* revoke */
4279 { &vop_mmap_desc, (VOPFUNC)fifo_mmap }, /* mmap */
4280 { &vop_fsync_desc, (VOPFUNC)hfs_fsync }, /* fsync */
4281 { &vop_seek_desc, (VOPFUNC)fifo_seek }, /* seek */
4282 { &vop_remove_desc, (VOPFUNC)fifo_remove }, /* remove */
4283 { &vop_link_desc, (VOPFUNC)fifo_link }, /* link */
4284 { &vop_rename_desc, (VOPFUNC)fifo_rename }, /* rename */
4285 { &vop_mkdir_desc, (VOPFUNC)fifo_mkdir }, /* mkdir */
4286 { &vop_rmdir_desc, (VOPFUNC)fifo_rmdir }, /* rmdir */
55e303ae 4287 { &vop_getattrlist_desc, (VOPFUNC)hfs_getattrlist },
9bccf70c
A
4288 { &vop_symlink_desc, (VOPFUNC)fifo_symlink }, /* symlink */
4289 { &vop_readdir_desc, (VOPFUNC)fifo_readdir }, /* readdir */
4290 { &vop_readlink_desc, (VOPFUNC)fifo_readlink }, /* readlink */
4291 { &vop_abortop_desc, (VOPFUNC)fifo_abortop }, /* abortop */
4292 { &vop_inactive_desc, (VOPFUNC)hfs_inactive }, /* inactive */
4293 { &vop_reclaim_desc, (VOPFUNC)hfs_reclaim }, /* reclaim */
4294 { &vop_lock_desc, (VOPFUNC)hfs_lock }, /* lock */
4295 { &vop_unlock_desc, (VOPFUNC)hfs_unlock }, /* unlock */
4296 { &vop_bmap_desc, (VOPFUNC)fifo_bmap }, /* bmap */
4297 { &vop_strategy_desc, (VOPFUNC)fifo_strategy }, /* strategy */
4298 { &vop_print_desc, (VOPFUNC)hfs_print }, /* print */
4299 { &vop_islocked_desc, (VOPFUNC)hfs_islocked }, /* islocked */
4300 { &vop_pathconf_desc, (VOPFUNC)fifo_pathconf }, /* pathconf */
4301 { &vop_advlock_desc, (VOPFUNC)fifo_advlock }, /* advlock */
4302 { &vop_blkatoff_desc, (VOPFUNC)fifo_blkatoff }, /* blkatoff */
4303 { &vop_valloc_desc, (VOPFUNC)fifo_valloc }, /* valloc */
4304 { &vop_reallocblks_desc, (VOPFUNC)fifo_reallocblks }, /* reallocblks */
4305 { &vop_vfree_desc, (VOPFUNC)err_vfree }, /* vfree */
4306 { &vop_truncate_desc, (VOPFUNC)fifo_truncate }, /* truncate */
4307 { &vop_update_desc, (VOPFUNC)hfs_update }, /* update */
4308 { &vop_bwrite_desc, (VOPFUNC)hfs_bwrite },
4309 { &vop_pagein_desc, (VOPFUNC)hfs_pagein }, /* Pagein */
4310 { &vop_pageout_desc, (VOPFUNC)hfs_pageout }, /* Pageout */
4311 { &vop_copyfile_desc, (VOPFUNC)err_copyfile }, /* copyfile */
4312 { &vop_blktooff_desc, (VOPFUNC)hfs_blktooff }, /* blktooff */
4313 { &vop_offtoblk_desc, (VOPFUNC)hfs_offtoblk }, /* offtoblk */
4314 { &vop_cmap_desc, (VOPFUNC)hfs_cmap }, /* cmap */
55e303ae
A
4315 { &vop_kqfilt_add_desc, (VOPFUNC)hfsfifo_kqfilt_add }, /* kqfilt_add */
4316 { &vop_kqfilt_remove_desc, (VOPFUNC)hfsfifo_kqfilt_remove }, /* kqfilt_remove */
9bccf70c
A
4317 { (struct vnodeop_desc*)NULL, (VOPFUNC)NULL }
4318};
4319struct vnodeopv_desc hfs_fifoop_opv_desc =
4320 { &hfs_fifoop_p, hfs_fifoop_entries };
4321#endif /* FIFO */
4322
4323
4324