1/*
2 * Copyright (c) 2000-2013 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28
29#include <sys/systm.h>
30#include <sys/param.h>
31#include <sys/kernel.h>
32#include <sys/file_internal.h>
33#include <sys/dirent.h>
34#include <sys/stat.h>
35#include <sys/buf.h>
36#include <sys/buf_internal.h>
37#include <sys/mount.h>
38#include <sys/vnode_if.h>
39#include <sys/vnode_internal.h>
40#include <sys/malloc.h>
41#include <sys/ubc.h>
42#include <sys/ubc_internal.h>
43#include <sys/paths.h>
44#include <sys/quota.h>
45#include <sys/time.h>
46#include <sys/disk.h>
47#include <sys/kauth.h>
48#include <sys/uio_internal.h>
49#include <sys/fsctl.h>
50#include <sys/cprotect.h>
51#include <sys/xattr.h>
52#include <string.h>
53#include <sys/fsevents.h>
54#include <kern/kalloc.h>
55
56#include <miscfs/specfs/specdev.h>
57#include <miscfs/fifofs/fifo.h>
58#include <vfs/vfs_support.h>
59#include <machine/spl.h>
60
61#include <sys/kdebug.h>
62#include <sys/sysctl.h>
63
64#include "hfs.h"
65#include "hfs_catalog.h"
66#include "hfs_cnode.h"
67#include "hfs_dbg.h"
68#include "hfs_mount.h"
69#include "hfs_quota.h"
70#include "hfs_endian.h"
71
72#include "hfscommon/headers/BTreesInternal.h"
73#include "hfscommon/headers/FileMgrInternal.h"
74
75#define KNDETACH_VNLOCKED 0x00000001
76
77/* Global vfs data structures for hfs */
78
79/* Always F_FULLFSYNC? 1=yes,0=no (default due to "various" reasons is 'no') */
80int always_do_fullfsync = 0;
81SYSCTL_DECL(_vfs_generic);
82SYSCTL_INT (_vfs_generic, OID_AUTO, always_do_fullfsync, CTLFLAG_RW | CTLFLAG_LOCKED, &always_do_fullfsync, 0, "always F_FULLFSYNC when fsync is called");
83
84int hfs_makenode(struct vnode *dvp, struct vnode **vpp,
85                        struct componentname *cnp, struct vnode_attr *vap,
86                        vfs_context_t ctx);
87int hfs_metasync(struct hfsmount *hfsmp, daddr64_t node, __unused struct proc *p);
88int hfs_metasync_all(struct hfsmount *hfsmp);
89
90int hfs_removedir(struct vnode *, struct vnode *, struct componentname *,
91                         int, int);
92int hfs_removefile(struct vnode *, struct vnode *, struct componentname *,
93                          int, int, int, struct vnode *, int);
94
95/* Used here and in cnode teardown -- for symlinks */
96int hfs_removefile_callback(struct buf *bp, void *hfsmp);
97
98int hfs_movedata (struct vnode *, struct vnode*);
99static int hfs_move_fork (struct filefork *srcfork, struct cnode *src,
100						  struct filefork *dstfork, struct cnode *dst);
101
102decmpfs_cnode* hfs_lazy_init_decmpfs_cnode (struct cnode *cp);
103
104#if FIFO
105static int hfsfifo_read(struct vnop_read_args *);
106static int hfsfifo_write(struct vnop_write_args *);
107static int hfsfifo_close(struct vnop_close_args *);
108
109extern int (**fifo_vnodeop_p)(void *);
110#endif /* FIFO */
111
112int hfs_vnop_close(struct vnop_close_args*);
113int hfs_vnop_create(struct vnop_create_args*);
114int hfs_vnop_exchange(struct vnop_exchange_args*);
115int hfs_vnop_fsync(struct vnop_fsync_args*);
116int hfs_vnop_mkdir(struct vnop_mkdir_args*);
117int hfs_vnop_mknod(struct vnop_mknod_args*);
118int hfs_vnop_getattr(struct vnop_getattr_args*);
119int hfs_vnop_open(struct vnop_open_args*);
120int hfs_vnop_readdir(struct vnop_readdir_args*);
121int hfs_vnop_remove(struct vnop_remove_args*);
122int hfs_vnop_rename(struct vnop_rename_args*);
123int hfs_vnop_rmdir(struct vnop_rmdir_args*);
124int hfs_vnop_symlink(struct vnop_symlink_args*);
125int hfs_vnop_setattr(struct vnop_setattr_args*);
126int hfs_vnop_readlink(struct vnop_readlink_args *);
127int hfs_vnop_pathconf(struct vnop_pathconf_args *);
128int hfs_vnop_whiteout(struct vnop_whiteout_args *);
129int hfs_vnop_mmap(struct vnop_mmap_args *ap);
130int hfsspec_read(struct vnop_read_args *);
131int hfsspec_write(struct vnop_write_args *);
132int hfsspec_close(struct vnop_close_args *);
133
134/* Options for hfs_removedir and hfs_removefile */
135#define HFSRM_SKIP_RESERVE  0x01
136
137
138
139/*****************************************************************************
140*
141* Common Operations on vnodes
142*
143*****************************************************************************/
144
145/*
146 * Is the given cnode either the .journal or .journal_info_block file on
147 * a volume with an active journal?  Many VNOPs use this to deny access
148 * to those files.
149 *
150 * Note: the .journal file on a volume with an external journal still
151 * returns true here, even though it does not actually hold the contents
152 * of the volume's journal.
153 */
154static _Bool
155hfs_is_journal_file(struct hfsmount *hfsmp, struct cnode *cp)
156{
157	if (hfsmp->jnl != NULL &&
158	    (cp->c_fileid == hfsmp->hfs_jnlinfoblkid ||
159	     cp->c_fileid == hfsmp->hfs_jnlfileid)) {
160		return true;
161	} else {
162		return false;
163	}
164}
165
166/*
167 * Create a regular file.
168 */
169int
170hfs_vnop_create(struct vnop_create_args *ap)
171{
172	int error;
173
174again:
175	error = hfs_makenode(ap->a_dvp, ap->a_vpp, ap->a_cnp, ap->a_vap, ap->a_context);
176
177	/*
178	 * We speculatively skipped the original lookup of the leaf
179	 * for CREATE.  Since it exists, go get it as long as they
180	 * didn't want an exclusive create.
181	 */
182	if ((error == EEXIST) && !(ap->a_vap->va_vaflags & VA_EXCLUSIVE)) {
183		struct vnop_lookup_args args;
184
185		args.a_desc = &vnop_lookup_desc;
186		args.a_dvp = ap->a_dvp;
187		args.a_vpp = ap->a_vpp;
188		args.a_cnp = ap->a_cnp;
189		args.a_context = ap->a_context;
190		args.a_cnp->cn_nameiop = LOOKUP;
191		error = hfs_vnop_lookup(&args);
192		/*
193		 * We can also race with remove for this file.
194		 */
195		if (error == ENOENT) {
196			goto again;
197		}
198
199		/* Make sure it was file. */
200		if ((error == 0) && !vnode_isreg(*args.a_vpp)) {
201			vnode_put(*args.a_vpp);
202			*args.a_vpp = NULLVP;
203			error = EEXIST;
204		}
205		args.a_cnp->cn_nameiop = CREATE;
206	}
207	return (error);
208}
209
210/*
211 * Make device special file.
212 */
213int
214hfs_vnop_mknod(struct vnop_mknod_args *ap)
215{
216	struct vnode_attr *vap = ap->a_vap;
217	struct vnode *dvp = ap->a_dvp;
218	struct vnode **vpp = ap->a_vpp;
219	struct cnode *cp;
220	int error;
221
222	if (VTOVCB(dvp)->vcbSigWord != kHFSPlusSigWord) {
223		return (ENOTSUP);
224	}
225
226	/* Create the vnode */
227	error = hfs_makenode(dvp, vpp, ap->a_cnp, vap, ap->a_context);
228	if (error)
229		return (error);
230
231	cp = VTOC(*vpp);
232	cp->c_touch_acctime = TRUE;
233	cp->c_touch_chgtime = TRUE;
234	cp->c_touch_modtime = TRUE;
235
236	if ((vap->va_rdev != VNOVAL) &&
237	    (vap->va_type == VBLK || vap->va_type == VCHR))
238		cp->c_rdev = vap->va_rdev;
239
240	return (0);
241}
242
243#if HFS_COMPRESSION
244/*
245 *	hfs_ref_data_vp(): returns the data fork vnode for a given cnode.
246 *	In the (hopefully rare) case where the data fork vnode is not
247 *	present, it will use hfs_vget() to create a new vnode for the
248 *	data fork.
249 *
250 *	NOTE: If successful and a vnode is returned, the caller is responsible
251 *	for releasing the returned vnode with vnode_rele().
252 */
253static int
254hfs_ref_data_vp(struct cnode *cp, struct vnode **data_vp, int skiplock)
255{
256	int vref = 0;
257
258	if (!data_vp || !cp) /* sanity check incoming parameters */
259		return EINVAL;
260
261	/* maybe we should take the hfs cnode lock here, and if so, use the skiplock parameter to tell us not to */
262
263	if (!skiplock) hfs_lock(cp, HFS_SHARED_LOCK, HFS_LOCK_DEFAULT);
264	struct vnode *c_vp = cp->c_vp;
265	if (c_vp) {
266		/* we already have a data vnode */
267		*data_vp = c_vp;
268		vref = vnode_ref(*data_vp);
269		if (!skiplock) hfs_unlock(cp);
270		if (vref == 0) {
271			return 0;
272		}
273		return EINVAL;
274	}
275	/* no data fork vnode in the cnode, so ask hfs for one. */
276
277	if (!cp->c_rsrc_vp) {
278		/* if we don't have either a c_vp or c_rsrc_vp, we can't really do anything useful */
279		*data_vp = NULL;
280		if (!skiplock) hfs_unlock(cp);
281		return EINVAL;
282	}
283
284	if (0 == hfs_vget(VTOHFS(cp->c_rsrc_vp), cp->c_cnid, data_vp, 1, 0) &&
285		0 != data_vp) {
286		vref = vnode_ref(*data_vp);
287		vnode_put(*data_vp);
288		if (!skiplock) hfs_unlock(cp);
289		if (vref == 0) {
290			return 0;
291		}
292		return EINVAL;
293	}
294	/* there was an error getting the vnode */
295	*data_vp = NULL;
296	if (!skiplock) hfs_unlock(cp);
297	return EINVAL;
298}
299
300/*
301 *	hfs_lazy_init_decmpfs_cnode(): returns the decmpfs_cnode for a cnode,
302 *	allocating it if necessary; returns NULL if there was an allocation error.
303 *  function is non-static so that it can be used from the FCNTL handler.
304 */
305decmpfs_cnode *
306hfs_lazy_init_decmpfs_cnode(struct cnode *cp)
307{
308	if (!cp->c_decmp) {
309		decmpfs_cnode *dp = NULL;
310		MALLOC_ZONE(dp, decmpfs_cnode *, sizeof(decmpfs_cnode), M_DECMPFS_CNODE, M_WAITOK);
311		if (!dp) {
312			/* error allocating a decmpfs cnode */
313			return NULL;
314		}
315		decmpfs_cnode_init(dp);
316		if (!OSCompareAndSwapPtr(NULL, dp, (void * volatile *)&cp->c_decmp)) {
317			/* another thread got here first, so free the decmpfs_cnode we allocated */
318			decmpfs_cnode_destroy(dp);
319			FREE_ZONE(dp, sizeof(*dp), M_DECMPFS_CNODE);
320		}
321	}
322
323	return cp->c_decmp;
324}
325
326/*
327 *	hfs_file_is_compressed(): returns 1 if the file is compressed, and 0 (zero) if not.
328 *	if the file's compressed flag is set, makes sure that the decmpfs_cnode field
329 *	is allocated by calling hfs_lazy_init_decmpfs_cnode(), then makes sure it is populated,
330 *	or else fills it in via the decmpfs_file_is_compressed() function.
331 */
332int
333hfs_file_is_compressed(struct cnode *cp, int skiplock)
334{
335	int ret = 0;
336
337	/* fast check to see if file is compressed. If flag is clear, just answer no */
338	if (!(cp->c_bsdflags & UF_COMPRESSED)) {
339		return 0;
340	}
341
342	decmpfs_cnode *dp = hfs_lazy_init_decmpfs_cnode(cp);
343	if (!dp) {
344		/* error allocating a decmpfs cnode, treat the file as uncompressed */
345		return 0;
346	}
347
348	/* flag was set, see if the decmpfs_cnode state is valid (zero == invalid) */
349	uint32_t decmpfs_state = decmpfs_cnode_get_vnode_state(dp);
350	switch(decmpfs_state) {
351		case FILE_IS_COMPRESSED:
352		case FILE_IS_CONVERTING: /* treat decompressing files as if they are compressed */
353			return 1;
354		case FILE_IS_NOT_COMPRESSED:
355			return 0;
356		/* otherwise the state is not cached yet */
357	}
358
359	/* decmpfs hasn't seen this file yet, so call decmpfs_file_is_compressed() to init the decmpfs_cnode struct */
360	struct vnode *data_vp = NULL;
361	if (0 == hfs_ref_data_vp(cp, &data_vp, skiplock)) {
362		if (data_vp) {
363			ret = decmpfs_file_is_compressed(data_vp, VTOCMP(data_vp)); // fill in decmpfs_cnode
364			vnode_rele(data_vp);
365		}
366	}
367	return ret;
368}
369
370/*	hfs_uncompressed_size_of_compressed_file() - get the uncompressed size of the file.
371 *	if the caller has passed a valid vnode (has a ref count > 0), then hfsmp and fid are not required.
372 *	if the caller doesn't have a vnode, pass NULL in vp, and pass valid hfsmp and fid.
373 *	files size is returned in size (required)
374 *	if the indicated file is a directory (or something that doesn't have a data fork), then this call
375 *	will return an error and the caller should fall back to treating the item as an uncompressed file
376 */
377int
378hfs_uncompressed_size_of_compressed_file(struct hfsmount *hfsmp, struct vnode *vp, cnid_t fid, off_t *size, int skiplock)
379{
380	int ret = 0;
381	int putaway = 0;									/* flag to remember if we used hfs_vget() */
382
383	if (!size) {
384		return EINVAL;									/* no place to put the file size */
385	}
386
387	if (NULL == vp) {
388		if (!hfsmp || !fid) {							/* make sure we have the required parameters */
389			return EINVAL;
390		}
391		if (0 != hfs_vget(hfsmp, fid, &vp, skiplock, 0)) {		/* vnode is null, use hfs_vget() to get it */
392			vp = NULL;
393		} else {
394			putaway = 1;								/* note that hfs_vget() was used to aquire the vnode */
395		}
396	}
397	/* this double check for compression (hfs_file_is_compressed)
398	 * ensures the cached size is present in case decmpfs hasn't
399	 * encountered this node yet.
400	 */
401	if (vp) {
402		if (hfs_file_is_compressed(VTOC(vp), skiplock) ) {
403			*size = decmpfs_cnode_get_vnode_cached_size(VTOCMP(vp));	/* file info will be cached now, so get size */
404		} else {
405			if (VTOCMP(vp) && VTOCMP(vp)->cmp_type >= CMP_MAX) {
406				if (VTOCMP(vp)->cmp_type != DATALESS_CMPFS_TYPE) {
407					// if we don't recognize this type, just use the real data fork size
408					if (VTOC(vp)->c_datafork) {
409						*size = VTOC(vp)->c_datafork->ff_size;
410						ret = 0;
411					} else {
412						ret = EINVAL;
413					}
414				} else {
415					*size = decmpfs_cnode_get_vnode_cached_size(VTOCMP(vp));	/* file info will be cached now, so get size */
416					ret = 0;
417				}
418			} else {
419				ret = EINVAL;
420			}
421		}
422	}
423
424	if (putaway) {		/* did we use hfs_vget() to get this vnode? */
425		vnode_put(vp);	/* if so, release it and set it to null */
426		vp = NULL;
427	}
428	return ret;
429}
430
431int
432hfs_hides_rsrc(vfs_context_t ctx, struct cnode *cp, int skiplock)
433{
434	if (ctx == decmpfs_ctx)
435		return 0;
436	if (!hfs_file_is_compressed(cp, skiplock))
437		return 0;
438	return decmpfs_hides_rsrc(ctx, cp->c_decmp);
439}
440
441int
442hfs_hides_xattr(vfs_context_t ctx, struct cnode *cp, const char *name, int skiplock)
443{
444	if (ctx == decmpfs_ctx)
445		return 0;
446	if (!hfs_file_is_compressed(cp, skiplock))
447		return 0;
448	return decmpfs_hides_xattr(ctx, cp->c_decmp, name);
449}
450#endif /* HFS_COMPRESSION */
451
452
453//
454// This function gets the doc_tombstone structure for the
455// current thread.  If the thread doesn't have one, the
456// structure is allocated.
457//
458static struct doc_tombstone *
459get_uthread_doc_tombstone(void)
460{
461	struct  uthread *ut;
462	ut = get_bsdthread_info(current_thread());
463
464	if (ut->t_tombstone == NULL) {
465		ut->t_tombstone = kalloc(sizeof(struct doc_tombstone));
466		if (ut->t_tombstone) {
467			memset(ut->t_tombstone, 0, sizeof(struct doc_tombstone));
468		}
469	}
470
471	return ut->t_tombstone;
472}
473
474//
475// This routine clears out the current tombstone for the
476// current thread and if necessary passes the doc-id of
477// the tombstone on to the dst_cnode.
478//
479// If the doc-id transfers to dst_cnode, we also generate
480// a doc-id changed fsevent.  Unlike all the other fsevents,
481// doc-id changed events can only be generated here in HFS
482// where we have the necessary info.
483//
484static void
485clear_tombstone_docid(struct  doc_tombstone *ut, struct hfsmount *hfsmp, struct cnode *dst_cnode)
486{
487	uint32_t old_id = ut->t_lastop_document_id;
488
489	ut->t_lastop_document_id = 0;
490	ut->t_lastop_parent = NULL;
491	ut->t_lastop_parent_vid = 0;
492	ut->t_lastop_filename[0] = '\0';
493
494	//
495	// If the lastop item is still the same and needs to be cleared,
496	// clear it.
497	//
498	if (dst_cnode && old_id && ut->t_lastop_item && vnode_vid(ut->t_lastop_item) == ut->t_lastop_item_vid) {
499		//
500		// clear the document_id from the file that used to have it.
501		// XXXdbg - we need to lock the other vnode and make sure to
502		// update it on disk.
503		//
504		struct cnode *ocp = VTOC(ut->t_lastop_item);
505		struct FndrExtendedFileInfo *ofip = (struct FndrExtendedFileInfo *)((char *)&ocp->c_attr.ca_finderinfo + 16);
506
507		// printf("clearing doc-id from ino %d\n", ocp->c_desc.cd_cnid);
508		ofip->document_id = 0;
509		ocp->c_bsdflags &= ~UF_TRACKED;
510		ocp->c_flag |= C_MODIFIED | C_FORCEUPDATE;   // mark it dirty
511		/* cat_update(hfsmp, &ocp->c_desc, &ocp->c_attr, NULL, NULL); */
512
513	}
514
515#if CONFIG_FSE
516	if (dst_cnode && old_id) {
517		struct FndrExtendedFileInfo *fip = (struct FndrExtendedFileInfo *)((char *)&dst_cnode->c_attr.ca_finderinfo + 16);
518
519		add_fsevent(FSE_DOCID_CHANGED, vfs_context_current(),
520			    FSE_ARG_DEV, hfsmp->hfs_raw_dev,
521			    FSE_ARG_INO, (ino64_t)ut->t_lastop_fileid,    // src inode #
522			    FSE_ARG_INO, (ino64_t)dst_cnode->c_fileid,    // dst inode #
523			    FSE_ARG_INT32, (uint32_t)fip->document_id,
524			    FSE_ARG_DONE);
525	}
526#endif
527	// last, clear these now that we're all done
528	ut->t_lastop_item     = NULL;
529	ut->t_lastop_fileid   = 0;
530	ut->t_lastop_item_vid = 0;
531}
532
533
534//
535// This function is used to filter out operations on temp
536// filenames.  We have to filter out operations on certain
537// temp filenames to work-around questionable application
538// behavior from apps like Autocad that perform unusual
539// sequences of file system operations for a "safe save".
540static int
541is_ignorable_temp_name(const char *nameptr, int len)
542{
543	if (len == 0) {
544		len = strlen(nameptr);
545	}
546
547	if (   strncmp(nameptr, "atmp", 4) == 0
548	   || (len > 4 && strncmp(nameptr+len-4, ".bak", 4) == 0)
549	   || (len > 4 && strncmp(nameptr+len-4, ".tmp", 4) == 0)) {
550		return 1;
551	}
552
553	return 0;
554}
555
556//
557// Decide if we need to save a tombstone or not.  Normally we always
558// save a tombstone - but if there already is one and the name we're
559// given is an ignorable name, then we will not save a tombstone.
560//
561static int
562should_save_docid_tombstone(struct doc_tombstone *ut, struct vnode *vp, struct componentname *cnp)
563{
564	if (cnp->cn_nameptr == NULL) {
565		return 0;
566	}
567
568	if (ut->t_lastop_document_id && ut->t_lastop_item == vp && is_ignorable_temp_name(cnp->cn_nameptr, cnp->cn_namelen)) {
569		return 0;
570	}
571
572	return 1;
573}
574
575
576//
577// This function saves a tombstone for the given vnode and name.  The
578// tombstone represents the parent directory and name where the document
579// used to live and the document-id of that file.  This info is recorded
580// in the doc_tombstone structure hanging off the uthread (which assumes
581// that all safe-save operations happen on the same thread).
582//
583// If later on the same parent/name combo comes back into existence then
584// we'll preserve the doc-id from this vnode onto the new vnode.
585//
586static void
587save_tombstone(struct hfsmount *hfsmp, struct vnode *dvp, struct vnode *vp, struct componentname *cnp, int for_unlink)
588{
589	struct cnode *cp = VTOC(vp);
590	struct  doc_tombstone *ut;
591	ut = get_uthread_doc_tombstone();
592
593	if (for_unlink && vp->v_type == VREG && cp->c_linkcount > 1) {
594		//
595		// a regular file that is being unlinked and that is also
596		// hardlinked should not clear the UF_TRACKED state or
597		// mess with the tombstone because somewhere else in the
598		// file system the file is still alive.
599		//
600		return;
601	}
602
603	ut->t_lastop_parent     = dvp;
604	ut->t_lastop_parent_vid = vnode_vid(dvp);
605	ut->t_lastop_fileid     = cp->c_fileid;
606	if (for_unlink) {
607		ut->t_lastop_item      = NULL;
608		ut->t_lastop_item_vid  = 0;
609	} else {
610		ut->t_lastop_item      = vp;
611		ut->t_lastop_item_vid  = vnode_vid(vp);
612	}
613
614	strlcpy((char *)&ut->t_lastop_filename[0], cnp->cn_nameptr, sizeof(ut->t_lastop_filename));
615
616	struct FndrExtendedFileInfo *fip = (struct FndrExtendedFileInfo *)((char *)&cp->c_attr.ca_finderinfo + 16);
617	ut->t_lastop_document_id = fip->document_id;
618
619	if (for_unlink) {
620		// clear this so it's never returned again
621		fip->document_id = 0;
622		cp->c_bsdflags &= ~UF_TRACKED;
623
624		if (ut->t_lastop_document_id) {
625			(void) cat_update(hfsmp, &cp->c_desc, &cp->c_attr, NULL, NULL);
626
627#if CONFIG_FSE
628			// this event is more of a "pending-delete"
629			add_fsevent(FSE_DOCID_CHANGED, vfs_context_current(),
630				    FSE_ARG_DEV, hfsmp->hfs_raw_dev,
631				    FSE_ARG_INO, (ino64_t)cp->c_fileid,       // src inode #
632				    FSE_ARG_INO, (ino64_t)0,                  // dst inode #
633				    FSE_ARG_INT32, ut->t_lastop_document_id,  // document id
634				    FSE_ARG_DONE);
635#endif
636		}
637	}
638}
639
640
641/*
642 * Open a file/directory.
643 */
644int
645hfs_vnop_open(struct vnop_open_args *ap)
646{
647	struct vnode *vp = ap->a_vp;
648	struct filefork *fp;
649	struct timeval tv;
650	int error;
651	static int past_bootup = 0;
652	struct cnode *cp = VTOC(vp);
653	struct hfsmount *hfsmp = VTOHFS(vp);
654
655#if HFS_COMPRESSION
656	if (ap->a_mode & FWRITE) {
657		/* open for write */
658		if ( hfs_file_is_compressed(cp, 1) ) { /* 1 == don't take the cnode lock */
659			/* opening a compressed file for write, so convert it to decompressed */
660			struct vnode *data_vp = NULL;
661			error = hfs_ref_data_vp(cp, &data_vp, 1); /* 1 == don't take the cnode lock */
662			if (0 == error) {
663				if (data_vp) {
664					error = decmpfs_decompress_file(data_vp, VTOCMP(data_vp), -1, 1, 0);
665					vnode_rele(data_vp);
666				} else {
667					error = EINVAL;
668				}
669			}
670			if (error != 0)
671				return error;
672		}
673	} else {
674		/* open for read */
675		if (hfs_file_is_compressed(cp, 1) ) { /* 1 == don't take the cnode lock */
676			if (VNODE_IS_RSRC(vp)) {
677				/* opening the resource fork of a compressed file, so nothing to do */
678			} else {
679				/* opening a compressed file for read, make sure it validates */
680				error = decmpfs_validate_compressed_file(vp, VTOCMP(vp));
681				if (error != 0)
682					return error;
683			}
684		}
685	}
686#endif
687
688	/*
689	 * Files marked append-only must be opened for appending.
690	 */
691	if ((cp->c_bsdflags & APPEND) && !vnode_isdir(vp) &&
692	    (ap->a_mode & (FWRITE | O_APPEND)) == FWRITE)
693		return (EPERM);
694
695	if (vnode_isreg(vp) && !UBCINFOEXISTS(vp))
696		return (EBUSY);  /* file is in use by the kernel */
697
698	/* Don't allow journal to be opened externally. */
699	if (hfs_is_journal_file(hfsmp, cp))
700		return (EPERM);
701
702	if ((hfsmp->hfs_flags & HFS_READ_ONLY) ||
703	    (hfsmp->jnl == NULL) ||
704#if NAMEDSTREAMS
705	    !vnode_isreg(vp) || vnode_isinuse(vp, 0) || vnode_isnamedstream(vp)) {
706#else
707	    !vnode_isreg(vp) || vnode_isinuse(vp, 0)) {
708#endif
709		return (0);
710	}
711
712	if ((error = hfs_lock(cp, HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT)))
713		return (error);
714
715#if QUOTA
716	/* If we're going to write to the file, initialize quotas. */
717	if ((ap->a_mode & FWRITE) && (hfsmp->hfs_flags & HFS_QUOTAS))
718		(void)hfs_getinoquota(cp);
719#endif /* QUOTA */
720
721	/*
722	 * On the first (non-busy) open of a fragmented
723	 * file attempt to de-frag it (if its less than 20MB).
724	 */
725	fp = VTOF(vp);
726	if (fp->ff_blocks &&
727	    fp->ff_extents[7].blockCount != 0 &&
728	    fp->ff_size <= (20 * 1024 * 1024)) {
729		int no_mods = 0;
730		struct timeval now;
731		/*
732		 * Wait until system bootup is done (3 min).
733		 * And don't relocate a file that's been modified
734		 * within the past minute -- this can lead to
735		 * system thrashing.
736		 */
737
738		if (!past_bootup) {
739			microuptime(&tv);
740			if (tv.tv_sec > (60*3)) {
741				past_bootup = 1;
742			}
743		}
744
745		microtime(&now);
746		if ((now.tv_sec - cp->c_mtime) > 60) {
747			no_mods = 1;
748		}
749
750		if (past_bootup && no_mods) {
751			(void) hfs_relocate(vp, hfsmp->nextAllocation + 4096,
752					vfs_context_ucred(ap->a_context),
753					vfs_context_proc(ap->a_context));
754		}
755	}
756
757	hfs_unlock(cp);
758
759	return (0);
760}
761
762
763/*
764 * Close a file/directory.
765 */
766int
767hfs_vnop_close(ap)
768	struct vnop_close_args /* {
769		struct vnode *a_vp;
770		int a_fflag;
771		vfs_context_t a_context;
772	} */ *ap;
773{
774	register struct vnode *vp = ap->a_vp;
775 	register struct cnode *cp;
776	struct proc *p = vfs_context_proc(ap->a_context);
777	struct hfsmount *hfsmp;
778	int busy;
779	int tooktrunclock = 0;
780	int knownrefs = 0;
781
782	if ( hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT) != 0)
783		return (0);
784	cp = VTOC(vp);
785	hfsmp = VTOHFS(vp);
786
787	/*
788	 * If the rsrc fork is a named stream, it can cause the data fork to
789	 * stay around, preventing de-allocation of these blocks.
790	 * Do checks for truncation on close. Purge extra extents if they exist.
791	 * Make sure the vp is not a directory, and that it has a resource fork,
792	 * and that resource fork is also a named stream.
793	 */
794
795	if ((vp->v_type == VREG) && (cp->c_rsrc_vp)
796			&& (vnode_isnamedstream(cp->c_rsrc_vp))) {
797		uint32_t blks;
798
799		blks = howmany(VTOF(vp)->ff_size, VTOVCB(vp)->blockSize);
800		/*
801		 * If there are extra blocks and there are only 2 refs on
802		 * this vp (ourselves + rsrc fork holding ref on us), go ahead
803		 * and try to truncate.
804		 */
805		if ((blks < VTOF(vp)->ff_blocks) && (!vnode_isinuse(vp, 2))) {
806			// release cnode lock; must acquire truncate lock BEFORE cnode lock
807			hfs_unlock(cp);
808
809			hfs_lock_truncate(cp, HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT);
810			tooktrunclock = 1;
811
812			if (hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT) != 0) {
813				hfs_unlock_truncate(cp, HFS_LOCK_DEFAULT);
814				// bail out if we can't re-acquire cnode lock
815				return 0;
816			}
817			// now re-test to make sure it's still valid
818			if (cp->c_rsrc_vp) {
819				knownrefs = 1 + vnode_isnamedstream(cp->c_rsrc_vp);
820				if (!vnode_isinuse(vp, knownrefs)){
821					// now we can truncate the file, if necessary
822					blks = howmany(VTOF(vp)->ff_size, VTOVCB(vp)->blockSize);
823					if (blks < VTOF(vp)->ff_blocks){
824						(void) hfs_truncate(vp, VTOF(vp)->ff_size, IO_NDELAY, 0, 0, ap->a_context);
825					}
826				}
827			}
828		}
829	}
830
831
832	// if we froze the fs and we're exiting, then "thaw" the fs
833	if (hfsmp->hfs_freezing_proc == p && proc_exiting(p)) {
834	    hfsmp->hfs_freezing_proc = NULL;
835	    hfs_unlock_global (hfsmp);
836		lck_rw_unlock_exclusive(&hfsmp->hfs_insync);
837	}
838
839	busy = vnode_isinuse(vp, 1);
840
841	if (busy) {
842		hfs_touchtimes(VTOHFS(vp), cp);
843	}
844	if (vnode_isdir(vp)) {
845		hfs_reldirhints(cp, busy);
846	} else if (vnode_issystem(vp) && !busy) {
847		vnode_recycle(vp);
848	}
849
850	if (tooktrunclock){
851		hfs_unlock_truncate(cp, HFS_LOCK_DEFAULT);
852	}
853	hfs_unlock(cp);
854
855	if (ap->a_fflag & FWASWRITTEN) {
856		hfs_sync_ejectable(hfsmp);
857	}
858
859	return (0);
860}
861
862/*
863 * Get basic attributes.
864 */
865int
866hfs_vnop_getattr(struct vnop_getattr_args *ap)
867{
868#define VNODE_ATTR_TIMES  \
869	(VNODE_ATTR_va_access_time|VNODE_ATTR_va_change_time|VNODE_ATTR_va_modify_time)
870#define VNODE_ATTR_AUTH  \
871	(VNODE_ATTR_va_mode | VNODE_ATTR_va_uid | VNODE_ATTR_va_gid | \
872         VNODE_ATTR_va_flags | VNODE_ATTR_va_acl)
873
874	struct vnode *vp = ap->a_vp;
875	struct vnode_attr *vap = ap->a_vap;
876	struct vnode *rvp = NULLVP;
877	struct hfsmount *hfsmp;
878	struct cnode *cp;
879	uint64_t data_size;
880	enum vtype v_type;
881	int error = 0;
882	cp = VTOC(vp);
883
884#if HFS_COMPRESSION
885	/* we need to inspect the decmpfs state of the file before we take the hfs cnode lock */
886	int compressed = 0;
887	int hide_size = 0;
888	off_t uncompressed_size = -1;
889	if (VATTR_IS_ACTIVE(vap, va_data_size) || VATTR_IS_ACTIVE(vap, va_total_alloc) || VATTR_IS_ACTIVE(vap, va_data_alloc) || VATTR_IS_ACTIVE(vap, va_total_size)) {
890		/* we only care about whether the file is compressed if asked for the uncompressed size */
891		if (VNODE_IS_RSRC(vp)) {
892			/* if it's a resource fork, decmpfs may want us to hide the size */
893			hide_size = hfs_hides_rsrc(ap->a_context, cp, 0);
894		} else {
895			/* if it's a data fork, we need to know if it was compressed so we can report the uncompressed size */
896			compressed = hfs_file_is_compressed(cp, 0);
897		}
898		if ((VATTR_IS_ACTIVE(vap, va_data_size) || VATTR_IS_ACTIVE(vap, va_total_size))) {
899			// if it's compressed
900			if (compressed || (!VNODE_IS_RSRC(vp) && cp->c_decmp && cp->c_decmp->cmp_type >= CMP_MAX)) {
901				if (0 != hfs_uncompressed_size_of_compressed_file(NULL, vp, 0, &uncompressed_size, 0)) {
902					/* failed to get the uncompressed size, we'll check for this later */
903					uncompressed_size = -1;
904				} else {
905					// fake that it's compressed
906					compressed = 1;
907				}
908			}
909		}
910	}
911#endif
912
913	/*
914	 * Shortcut for vnode_authorize path.  Each of the attributes
915	 * in this set is updated atomically so we don't need to take
916	 * the cnode lock to access them.
917	 */
918	if ((vap->va_active & ~VNODE_ATTR_AUTH) == 0) {
919		/* Make sure file still exists. */
920		if (cp->c_flag & C_NOEXISTS)
921			return (ENOENT);
922
923		vap->va_uid = cp->c_uid;
924		vap->va_gid = cp->c_gid;
925		vap->va_mode = cp->c_mode;
926		vap->va_flags = cp->c_bsdflags;
927		vap->va_supported |= VNODE_ATTR_AUTH & ~VNODE_ATTR_va_acl;
928
929		if ((cp->c_attr.ca_recflags & kHFSHasSecurityMask) == 0) {
930			vap->va_acl = (kauth_acl_t) KAUTH_FILESEC_NONE;
931			VATTR_SET_SUPPORTED(vap, va_acl);
932		}
933
934		return (0);
935	}
936
937	hfsmp = VTOHFS(vp);
938	v_type = vnode_vtype(vp);
939	/*
940	 * If time attributes are requested and we have cnode times
941	 * that require updating, then acquire an exclusive lock on
942	 * the cnode before updating the times.  Otherwise we can
943	 * just acquire a shared lock.
944	 */
945	if ((vap->va_active & VNODE_ATTR_TIMES) &&
946	    (cp->c_touch_acctime || cp->c_touch_chgtime || cp->c_touch_modtime)) {
947		if ((error = hfs_lock(cp, HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT)))
948			return (error);
949		hfs_touchtimes(hfsmp, cp);
950	}
951 	else {
952		if ((error = hfs_lock(cp, HFS_SHARED_LOCK, HFS_LOCK_DEFAULT)))
953			return (error);
954	}
955
956	if (v_type == VDIR) {
957		data_size = (cp->c_entries + 2) * AVERAGE_HFSDIRENTRY_SIZE;
958
959		if (VATTR_IS_ACTIVE(vap, va_nlink)) {
960			int nlink;
961
962			/*
963			 * For directories, the va_nlink is esentially a count
964			 * of the ".." references to a directory plus the "."
965			 * reference and the directory itself. So for HFS+ this
966			 * becomes the sub-directory count plus two.
967			 *
968			 * In the absence of a sub-directory count we use the
969			 * directory's item count.  This will be too high in
970			 * most cases since it also includes files.
971			 */
972			if ((hfsmp->hfs_flags & HFS_FOLDERCOUNT) &&
973			    (cp->c_attr.ca_recflags & kHFSHasFolderCountMask))
974				nlink = cp->c_attr.ca_dircount;  /* implied ".." entries */
975			else
976				nlink = cp->c_entries;
977
978			/* Account for ourself and our "." entry */
979			nlink += 2;
980			 /* Hide our private directories. */
981			if (cp->c_cnid == kHFSRootFolderID) {
982				if (hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid != 0) {
983					--nlink;
984				}
985				if (hfsmp->hfs_private_desc[DIR_HARDLINKS].cd_cnid != 0) {
986					--nlink;
987				}
988			}
989			VATTR_RETURN(vap, va_nlink, (u_int64_t)nlink);
990		}
991		if (VATTR_IS_ACTIVE(vap, va_nchildren)) {
992			int entries;
993
994			entries = cp->c_entries;
995			/* Hide our private files and directories. */
996			if (cp->c_cnid == kHFSRootFolderID) {
997				if (hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid != 0)
998					--entries;
999				if (hfsmp->hfs_private_desc[DIR_HARDLINKS].cd_cnid != 0)
1000					--entries;
1001				if (hfsmp->jnl || ((hfsmp->vcbAtrb & kHFSVolumeJournaledMask) && (hfsmp->hfs_flags & HFS_READ_ONLY)))
1002					entries -= 2;   /* hide the journal files */
1003			}
1004			VATTR_RETURN(vap, va_nchildren, entries);
1005		}
1006		/*
1007		 * The va_dirlinkcount is the count of real directory hard links.
1008		 * (i.e. its not the sum of the implied "." and ".." references)
1009		 */
1010		if (VATTR_IS_ACTIVE(vap, va_dirlinkcount)) {
1011			VATTR_RETURN(vap, va_dirlinkcount, (uint32_t)cp->c_linkcount);
1012		}
1013	} else /* !VDIR */ {
1014		data_size = VCTOF(vp, cp)->ff_size;
1015
1016		VATTR_RETURN(vap, va_nlink, (u_int64_t)cp->c_linkcount);
1017		if (VATTR_IS_ACTIVE(vap, va_data_alloc)) {
1018			u_int64_t blocks;
1019
1020#if HFS_COMPRESSION
1021			if (hide_size) {
1022				VATTR_RETURN(vap, va_data_alloc, 0);
1023			} else if (compressed) {
1024				/* for compressed files, we report all allocated blocks as belonging to the data fork */
1025				blocks = cp->c_blocks;
1026				VATTR_RETURN(vap, va_data_alloc, blocks * (u_int64_t)hfsmp->blockSize);
1027			}
1028			else
1029#endif
1030			{
1031				blocks = VCTOF(vp, cp)->ff_blocks;
1032				VATTR_RETURN(vap, va_data_alloc, blocks * (u_int64_t)hfsmp->blockSize);
1033			}
1034		}
1035	}
1036
1037	/* conditional because 64-bit arithmetic can be expensive */
1038	if (VATTR_IS_ACTIVE(vap, va_total_size)) {
1039		if (v_type == VDIR) {
1040			VATTR_RETURN(vap, va_total_size, (cp->c_entries + 2) * AVERAGE_HFSDIRENTRY_SIZE);
1041		} else {
1042			u_int64_t total_size = ~0ULL;
1043			struct cnode *rcp;
1044#if HFS_COMPRESSION
1045			if (hide_size) {
1046				/* we're hiding the size of this file, so just return 0 */
1047				total_size = 0;
1048			} else if (compressed) {
1049				if (uncompressed_size == -1) {
1050					/*
1051					 * We failed to get the uncompressed size above,
1052					 * so we'll fall back to the standard path below
1053					 * since total_size is still -1
1054					 */
1055				} else {
1056					/* use the uncompressed size we fetched above */
1057					total_size = uncompressed_size;
1058				}
1059			}
1060#endif
1061			if (total_size == ~0ULL) {
1062				if (cp->c_datafork) {
1063					total_size = cp->c_datafork->ff_size;
1064				}
1065
1066				if (cp->c_blocks - VTOF(vp)->ff_blocks) {
1067					/* We deal with rsrc fork vnode iocount at the end of the function */
1068					error = hfs_vgetrsrc(hfsmp, vp, &rvp, TRUE, FALSE);
1069					if (error) {
1070						/*
1071						 * Note that we call hfs_vgetrsrc with error_on_unlinked
1072						 * set to FALSE.  This is because we may be invoked via
1073						 * fstat() on an open-unlinked file descriptor and we must
1074						 * continue to support access to the rsrc fork until it disappears.
1075						 * The code at the end of this function will be
1076						 * responsible for releasing the iocount generated by
1077						 * hfs_vgetrsrc.  This is because we can't drop the iocount
1078						 * without unlocking the cnode first.
1079						 */
1080						goto out;
1081					}
1082
1083					rcp = VTOC(rvp);
1084					if (rcp && rcp->c_rsrcfork) {
1085						total_size += rcp->c_rsrcfork->ff_size;
1086					}
1087				}
1088			}
1089
1090			VATTR_RETURN(vap, va_total_size, total_size);
1091		}
1092	}
1093	if (VATTR_IS_ACTIVE(vap, va_total_alloc)) {
1094		if (v_type == VDIR) {
1095			VATTR_RETURN(vap, va_total_alloc, 0);
1096		} else {
1097			VATTR_RETURN(vap, va_total_alloc, (u_int64_t)cp->c_blocks * (u_int64_t)hfsmp->blockSize);
1098		}
1099	}
1100
1101	/*
1102	 * If the VFS wants extended security data, and we know that we
1103	 * don't have any (because it never told us it was setting any)
1104	 * then we can return the supported bit and no data.  If we do
1105	 * have extended security, we can just leave the bit alone and
1106	 * the VFS will use the fallback path to fetch it.
1107	 */
1108	if (VATTR_IS_ACTIVE(vap, va_acl)) {
1109		if ((cp->c_attr.ca_recflags & kHFSHasSecurityMask) == 0) {
1110			vap->va_acl = (kauth_acl_t) KAUTH_FILESEC_NONE;
1111			VATTR_SET_SUPPORTED(vap, va_acl);
1112		}
1113	}
1114	if (VATTR_IS_ACTIVE(vap, va_access_time)) {
1115		/* Access times are lazily updated, get current time if needed */
1116		if (cp->c_touch_acctime) {
1117			struct timeval tv;
1118
1119			microtime(&tv);
1120			vap->va_access_time.tv_sec = tv.tv_sec;
1121		} else {
1122			vap->va_access_time.tv_sec = cp->c_atime;
1123		}
1124		vap->va_access_time.tv_nsec = 0;
1125		VATTR_SET_SUPPORTED(vap, va_access_time);
1126	}
1127	vap->va_create_time.tv_sec = cp->c_itime;
1128	vap->va_create_time.tv_nsec = 0;
1129	vap->va_modify_time.tv_sec = cp->c_mtime;
1130	vap->va_modify_time.tv_nsec = 0;
1131	vap->va_change_time.tv_sec = cp->c_ctime;
1132	vap->va_change_time.tv_nsec = 0;
1133	vap->va_backup_time.tv_sec = cp->c_btime;
1134	vap->va_backup_time.tv_nsec = 0;
1135
1136	/* See if we need to emit the date added field to the user */
1137	if (VATTR_IS_ACTIVE(vap, va_addedtime)) {
1138		u_int32_t dateadded = hfs_get_dateadded (cp);
1139		if (dateadded) {
1140			vap->va_addedtime.tv_sec = dateadded;
1141			vap->va_addedtime.tv_nsec = 0;
1142			VATTR_SET_SUPPORTED (vap, va_addedtime);
1143		}
1144	}
1145
1146	/* XXX is this really a good 'optimal I/O size'? */
1147	vap->va_iosize = hfsmp->hfs_logBlockSize;
1148	vap->va_uid = cp->c_uid;
1149	vap->va_gid = cp->c_gid;
1150	vap->va_mode = cp->c_mode;
1151	vap->va_flags = cp->c_bsdflags;
1152
1153	/*
1154	 * Exporting file IDs from HFS Plus:
1155	 *
1156	 * For "normal" files the c_fileid is the same value as the
1157	 * c_cnid.  But for hard link files, they are different - the
1158	 * c_cnid belongs to the active directory entry (ie the link)
1159	 * and the c_fileid is for the actual inode (ie the data file).
1160	 *
1161	 * The stat call (getattr) uses va_fileid and the Carbon APIs,
1162	 * which are hardlink-ignorant, will ask for va_linkid.
1163	 */
1164	vap->va_fileid = (u_int64_t)cp->c_fileid;
1165	/*
1166	 * We need to use the origin cache for both hardlinked files
1167	 * and directories. Hardlinked directories have multiple cnids
1168	 * and parents (one per link). Hardlinked files also have their
1169	 * own parents and link IDs separate from the indirect inode number.
1170	 * If we don't use the cache, we could end up vending the wrong ID
1171	 * because the cnode will only reflect the link that was looked up most recently.
1172	 */
1173	if (cp->c_flag & C_HARDLINK) {
1174		vap->va_linkid = (u_int64_t)hfs_currentcnid(cp);
1175		vap->va_parentid = (u_int64_t)hfs_currentparent(cp);
1176	} else {
1177		vap->va_linkid = (u_int64_t)cp->c_cnid;
1178		vap->va_parentid = (u_int64_t)cp->c_parentcnid;
1179	}
1180	vap->va_fsid = hfsmp->hfs_raw_dev;
1181	vap->va_filerev = 0;
1182	vap->va_encoding = cp->c_encoding;
1183	vap->va_rdev = (v_type == VBLK || v_type == VCHR) ? cp->c_rdev : 0;
1184#if HFS_COMPRESSION
1185	if (VATTR_IS_ACTIVE(vap, va_data_size)) {
1186		if (hide_size)
1187			vap->va_data_size = 0;
1188		else if (compressed) {
1189			if (uncompressed_size == -1) {
1190				/* failed to get the uncompressed size above, so just return data_size */
1191				vap->va_data_size = data_size;
1192			} else {
1193				/* use the uncompressed size we fetched above */
1194				vap->va_data_size = uncompressed_size;
1195			}
1196		} else
1197			vap->va_data_size = data_size;
1198//		vap->va_supported |= VNODE_ATTR_va_data_size;
1199		VATTR_SET_SUPPORTED(vap, va_data_size);
1200	}
1201#else
1202	vap->va_data_size = data_size;
1203	vap->va_supported |= VNODE_ATTR_va_data_size;
1204#endif
1205
1206	if (VATTR_IS_ACTIVE(vap, va_gen)) {
1207		if (UBCINFOEXISTS(vp) && (vp->v_ubcinfo->ui_flags & UI_ISMAPPED)) {
1208			/* While file is mmapped the generation count is invalid.
1209		 	 * However, bump the value so that the write-gen counter
1210		 	 * will be different once the file is unmapped (since,
1211		 	 * when unmapped the pageouts may not yet have happened)
1212		 	 */
1213			if (vp->v_ubcinfo->ui_flags & UI_MAPPEDWRITE) {
1214			     	hfs_incr_gencount (cp);
1215			}
1216			vap->va_gen = 0;
1217		} else {
1218			vap->va_gen = hfs_get_gencount(cp);
1219		}
1220
1221		VATTR_SET_SUPPORTED(vap, va_gen);
1222	}
1223	if (VATTR_IS_ACTIVE(vap, va_document_id)) {
1224		vap->va_document_id = hfs_get_document_id(cp);
1225		VATTR_SET_SUPPORTED(vap, va_document_id);
1226	}
1227
1228	/* Mark them all at once instead of individual VATTR_SET_SUPPORTED calls. */
1229	vap->va_supported |= VNODE_ATTR_va_create_time | VNODE_ATTR_va_modify_time |
1230	                     VNODE_ATTR_va_change_time| VNODE_ATTR_va_backup_time |
1231	                     VNODE_ATTR_va_iosize | VNODE_ATTR_va_uid |
1232	                     VNODE_ATTR_va_gid | VNODE_ATTR_va_mode |
1233	                     VNODE_ATTR_va_flags |VNODE_ATTR_va_fileid |
1234	                     VNODE_ATTR_va_linkid | VNODE_ATTR_va_parentid |
1235	                     VNODE_ATTR_va_fsid | VNODE_ATTR_va_filerev |
1236	                     VNODE_ATTR_va_encoding | VNODE_ATTR_va_rdev;
1237
1238	/* If this is the root, let VFS to find out the mount name, which
1239	 * may be different from the real name.  Otherwise, we need to take care
1240	 * for hardlinked files, which need to be looked up, if necessary
1241	 */
1242	if (VATTR_IS_ACTIVE(vap, va_name) && (cp->c_cnid != kHFSRootFolderID)) {
1243		struct cat_desc linkdesc;
1244		int lockflags;
1245		int uselinkdesc = 0;
1246		cnid_t nextlinkid = 0;
1247		cnid_t prevlinkid = 0;
1248
1249		/* Get the name for ATTR_CMN_NAME.  We need to take special care for hardlinks
1250		 * here because the info. for the link ID requested by getattrlist may be
1251		 * different than what's currently in the cnode.  This is because the cnode
1252		 * will be filled in with the information for the most recent link ID that went
1253		 * through namei/lookup().  If there are competing lookups for hardlinks that point
1254	 	 * to the same inode, one (or more) getattrlists could be vended incorrect name information.
1255		 * Also, we need to beware of open-unlinked files which could have a namelen of 0.
1256		 */
1257
1258		if ((cp->c_flag & C_HARDLINK) &&
1259				((cp->c_desc.cd_namelen == 0) || (vap->va_linkid != cp->c_cnid))) {
1260			/*
1261			 * If we have no name and our link ID is the raw inode number, then we may
1262			 * have an open-unlinked file.  Go to the next link in this case.
1263			 */
1264			if ((cp->c_desc.cd_namelen == 0) && (vap->va_linkid == cp->c_fileid)) {
1265				if ((error = hfs_lookup_siblinglinks(hfsmp, vap->va_linkid, &prevlinkid, &nextlinkid))){
1266					goto out;
1267				}
1268			}
1269			else {
1270				/* just use link obtained from vap above */
1271				nextlinkid = vap->va_linkid;
1272			}
1273
1274			/* We need to probe the catalog for the descriptor corresponding to the link ID
1275			 * stored in nextlinkid.  Note that we don't know if we have the exclusive lock
1276			 * for the cnode here, so we can't just update the descriptor.  Instead,
1277			 * we should just store the descriptor's value locally and then use it to pass
1278			 * out the name value as needed below.
1279			 */
1280			if (nextlinkid){
1281				lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_SHARED_LOCK);
1282				error = cat_findname(hfsmp, nextlinkid, &linkdesc);
1283				hfs_systemfile_unlock(hfsmp, lockflags);
1284				if (error == 0) {
1285					uselinkdesc = 1;
1286				}
1287			}
1288		}
1289
1290		/* By this point, we've either patched up the name above and the c_desc
1291		 * points to the correct data, or it already did, in which case we just proceed
1292		 * by copying the name into the vap.  Note that we will never set va_name to
1293		 * supported if nextlinkid is never initialized.  This could happen in the degenerate
1294		 * case above involving the raw inode number, where it has no nextlinkid.  In this case
1295		 * we will simply not mark the name bit as supported.
1296		 */
1297		if (uselinkdesc) {
1298			strlcpy(vap->va_name, (const char*) linkdesc.cd_nameptr, MAXPATHLEN);
1299			VATTR_SET_SUPPORTED(vap, va_name);
1300			cat_releasedesc(&linkdesc);
1301		}
1302		else if (cp->c_desc.cd_namelen) {
1303			strlcpy(vap->va_name, (const char*) cp->c_desc.cd_nameptr, MAXPATHLEN);
1304			VATTR_SET_SUPPORTED(vap, va_name);
1305		}
1306	}
1307
1308out:
1309	hfs_unlock(cp);
1310	/*
1311	 * We need to vnode_put the rsrc fork vnode only *after* we've released
1312	 * the cnode lock, since vnode_put can trigger an inactive call, which
1313	 * will go back into HFS and try to acquire a cnode lock.
1314	 */
1315	if (rvp) {
1316		vnode_put (rvp);
1317	}
1318
1319	return (error);
1320}
1321
1322int
1323hfs_vnop_setattr(ap)
1324	struct vnop_setattr_args /* {
1325		struct vnode *a_vp;
1326		struct vnode_attr *a_vap;
1327		vfs_context_t a_context;
1328	} */ *ap;
1329{
1330	struct vnode_attr *vap = ap->a_vap;
1331	struct vnode *vp = ap->a_vp;
1332	struct cnode *cp = NULL;
1333	struct hfsmount *hfsmp;
1334	kauth_cred_t cred = vfs_context_ucred(ap->a_context);
1335	struct proc *p = vfs_context_proc(ap->a_context);
1336	int error = 0;
1337	uid_t nuid;
1338	gid_t ngid;
1339	time_t orig_ctime;
1340
1341	orig_ctime = VTOC(vp)->c_ctime;
1342
1343#if HFS_COMPRESSION
1344	int decmpfs_reset_state = 0;
1345	/*
1346	 we call decmpfs_update_attributes even if the file is not compressed
1347	 because we want to update the incoming flags if the xattrs are invalid
1348	 */
1349	error = decmpfs_update_attributes(vp, vap);
1350	if (error)
1351		return error;
1352#endif
1353	//
1354	// if this is not a size-changing setattr and it is not just
1355	// an atime update, then check for a snapshot.
1356	//
1357	if (!VATTR_IS_ACTIVE(vap, va_data_size) && !(vap->va_active == VNODE_ATTR_va_access_time)) {
1358		check_for_tracked_file(vp, orig_ctime, NAMESPACE_HANDLER_METADATA_MOD, NSPACE_REARM_NO_ARG);
1359	}
1360
1361#if CONFIG_PROTECT
1362	if ((error = cp_handle_vnop(vp, CP_WRITE_ACCESS, 0)) != 0) {
1363		return (error);
1364	}
1365#endif /* CONFIG_PROTECT */
1366
1367	hfsmp = VTOHFS(vp);
1368
1369	/* Don't allow modification of the journal. */
1370	if (hfs_is_journal_file(hfsmp, VTOC(vp))) {
1371		return (EPERM);
1372	}
1373
1374	//
1375	// Check if we'll need a document_id and if so, get it before we lock the
1376	// the cnode to avoid any possible deadlock with the root vnode which has
1377	// to get locked to get the document id
1378	//
1379	u_int32_t document_id=0;
1380	if (VATTR_IS_ACTIVE(vap, va_flags) && (vap->va_flags & UF_TRACKED) && !(VTOC(vp)->c_bsdflags & UF_TRACKED)) {
1381		struct FndrExtendedDirInfo *fip = (struct FndrExtendedDirInfo *)((char *)&(VTOC(vp)->c_attr.ca_finderinfo) + 16);
1382		//
1383		// If the document_id is not set, get a new one.  It will be set
1384		// on the file down below once we hold the cnode lock.
1385		//
1386		if (fip->document_id == 0) {
1387			if (hfs_generate_document_id(hfsmp, &document_id) != 0) {
1388				document_id = 0;
1389			}
1390		}
1391	}
1392
1393
1394	/*
1395	 * File size change request.
1396	 * We are guaranteed that this is not a directory, and that
1397	 * the filesystem object is writeable.
1398	 *
1399	 * NOTE: HFS COMPRESSION depends on the data_size being set *before* the bsd flags are updated
1400	 */
1401	VATTR_SET_SUPPORTED(vap, va_data_size);
1402	if (VATTR_IS_ACTIVE(vap, va_data_size) && !vnode_islnk(vp)) {
1403#if HFS_COMPRESSION
1404		/* keep the compressed state locked until we're done truncating the file */
1405		decmpfs_cnode *dp = VTOCMP(vp);
1406		if (!dp) {
1407			/*
1408			 * call hfs_lazy_init_decmpfs_cnode() to make sure that the decmpfs_cnode
1409			 * is filled in; we need a decmpfs_cnode to lock out decmpfs state changes
1410			 * on this file while it's truncating
1411			 */
1412			dp = hfs_lazy_init_decmpfs_cnode(VTOC(vp));
1413			if (!dp) {
1414				/* failed to allocate a decmpfs_cnode */
1415				return ENOMEM; /* what should this be? */
1416			}
1417		}
1418
1419		check_for_tracked_file(vp, orig_ctime, vap->va_data_size == 0 ? NAMESPACE_HANDLER_TRUNCATE_OP|NAMESPACE_HANDLER_DELETE_OP : NAMESPACE_HANDLER_TRUNCATE_OP, NULL);
1420
1421		decmpfs_lock_compressed_data(dp, 1);
1422		if (hfs_file_is_compressed(VTOC(vp), 1)) {
1423			error = decmpfs_decompress_file(vp, dp, -1/*vap->va_data_size*/, 0, 1);
1424			if (error != 0) {
1425				decmpfs_unlock_compressed_data(dp, 1);
1426				return error;
1427			}
1428		}
1429#endif
1430
1431		/* Take truncate lock before taking cnode lock. */
1432		hfs_lock_truncate(VTOC(vp), HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT);
1433
1434		/* Perform the ubc_setsize before taking the cnode lock. */
1435		ubc_setsize(vp, vap->va_data_size);
1436
1437		if ((error = hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT))) {
1438			hfs_unlock_truncate(VTOC(vp), HFS_LOCK_DEFAULT);
1439#if HFS_COMPRESSION
1440			decmpfs_unlock_compressed_data(dp, 1);
1441#endif
1442			return (error);
1443		}
1444		cp = VTOC(vp);
1445
1446		error = hfs_truncate(vp, vap->va_data_size, vap->va_vaflags & 0xffff, 1, 0, ap->a_context);
1447
1448		hfs_unlock_truncate(cp, HFS_LOCK_DEFAULT);
1449#if HFS_COMPRESSION
1450		decmpfs_unlock_compressed_data(dp, 1);
1451#endif
1452		if (error)
1453			goto out;
1454	}
1455	if (cp == NULL) {
1456		if ((error = hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT)))
1457			return (error);
1458		cp = VTOC(vp);
1459	}
1460
1461	/*
1462	 * If it is just an access time update request by itself
1463	 * we know the request is from kernel level code, and we
1464	 * can delay it without being as worried about consistency.
1465	 * This change speeds up mmaps, in the rare case that they
1466	 * get caught behind a sync.
1467	 */
1468
1469	if (vap->va_active == VNODE_ATTR_va_access_time) {
1470		cp->c_touch_acctime=TRUE;
1471		goto out;
1472	}
1473
1474
1475
1476	/*
1477	 * Owner/group change request.
1478	 * We are guaranteed that the new owner/group is valid and legal.
1479	 */
1480	VATTR_SET_SUPPORTED(vap, va_uid);
1481	VATTR_SET_SUPPORTED(vap, va_gid);
1482	nuid = VATTR_IS_ACTIVE(vap, va_uid) ? vap->va_uid : (uid_t)VNOVAL;
1483	ngid = VATTR_IS_ACTIVE(vap, va_gid) ? vap->va_gid : (gid_t)VNOVAL;
1484	if (((nuid != (uid_t)VNOVAL) || (ngid != (gid_t)VNOVAL)) &&
1485	    ((error = hfs_chown(vp, nuid, ngid, cred, p)) != 0))
1486		goto out;
1487
1488	/*
1489	 * Mode change request.
1490	 * We are guaranteed that the mode value is valid and that in
1491	 * conjunction with the owner and group, this change is legal.
1492   	*/
1493	VATTR_SET_SUPPORTED(vap, va_mode);
1494	if (VATTR_IS_ACTIVE(vap, va_mode) &&
1495	    ((error = hfs_chmod(vp, (int)vap->va_mode, cred, p)) != 0))
1496	    goto out;
1497
1498	/*
1499	 * File flags change.
1500	 * We are guaranteed that only flags allowed to change given the
1501	 * current securelevel are being changed.
1502	 */
1503	VATTR_SET_SUPPORTED(vap, va_flags);
1504	if (VATTR_IS_ACTIVE(vap, va_flags)) {
1505		u_int16_t *fdFlags;
1506
1507#if HFS_COMPRESSION
1508		if ((cp->c_bsdflags ^ vap->va_flags) & UF_COMPRESSED) {
1509			/*
1510			 * the UF_COMPRESSED was toggled, so reset our cached compressed state
1511			 * but we don't want to actually do the update until we've released the cnode lock down below
1512			 * NOTE: turning the flag off doesn't actually decompress the file, so that we can
1513			 * turn off the flag and look at the "raw" file for debugging purposes
1514			 */
1515			decmpfs_reset_state = 1;
1516		}
1517#endif
1518		if ((vap->va_flags & UF_TRACKED) && !(cp->c_bsdflags & UF_TRACKED)) {
1519			struct FndrExtendedDirInfo *fip = (struct FndrExtendedDirInfo *)((char *)&cp->c_attr.ca_finderinfo + 16);
1520
1521			//
1522			// we're marking this item UF_TRACKED.  if the document_id is
1523			// not set, get a new one and put it on the file.
1524			//
1525			if (fip->document_id == 0) {
1526				if (document_id != 0) {
1527					// printf("SETATTR: assigning doc-id %d to %s (ino %d)\n", document_id, vp->v_name, cp->c_desc.cd_cnid);
1528					fip->document_id = (uint32_t)document_id;
1529#if CONFIG_FSE
1530					add_fsevent(FSE_DOCID_CHANGED, ap->a_context,
1531						    FSE_ARG_DEV, hfsmp->hfs_raw_dev,
1532						    FSE_ARG_INO, (ino64_t)0,                // src inode #
1533						    FSE_ARG_INO, (ino64_t)cp->c_fileid,     // dst inode #
1534						    FSE_ARG_INT32, document_id,
1535						    FSE_ARG_DONE);
1536#endif
1537				} else {
1538					// printf("hfs: could not acquire a new document_id for %s (ino %d)\n", vp->v_name, cp->c_desc.cd_cnid);
1539				}
1540			}
1541
1542		} else if (!(vap->va_flags & UF_TRACKED) && (cp->c_bsdflags & UF_TRACKED)) {
1543			//
1544			// UF_TRACKED is being cleared so clear the document_id
1545			//
1546			struct FndrExtendedDirInfo *fip = (struct FndrExtendedDirInfo *)((char *)&cp->c_attr.ca_finderinfo + 16);
1547			if (fip->document_id) {
1548				// printf("SETATTR: clearing doc-id %d from %s (ino %d)\n", fip->document_id, vp->v_name, cp->c_desc.cd_cnid);
1549#if CONFIG_FSE
1550				add_fsevent(FSE_DOCID_CHANGED, ap->a_context,
1551					    FSE_ARG_DEV, hfsmp->hfs_raw_dev,
1552					    FSE_ARG_INO, (ino64_t)cp->c_fileid,          // src inode #
1553					    FSE_ARG_INO, (ino64_t)0,                     // dst inode #
1554					    FSE_ARG_INT32, fip->document_id,             // document id
1555					    FSE_ARG_DONE);
1556#endif
1557				fip->document_id = 0;
1558				cp->c_bsdflags &= ~UF_TRACKED;
1559			}
1560		}
1561
1562		cp->c_bsdflags = vap->va_flags;
1563		cp->c_touch_chgtime = TRUE;
1564
1565
1566		/*
1567		 * Mirror the UF_HIDDEN flag to the invisible bit of the Finder Info.
1568		 *
1569		 * The fdFlags for files and frFlags for folders are both 8 bytes
1570		 * into the userInfo (the first 16 bytes of the Finder Info).  They
1571		 * are both 16-bit fields.
1572		 */
1573		fdFlags = (u_int16_t *) &cp->c_finderinfo[8];
1574		if (vap->va_flags & UF_HIDDEN)
1575			*fdFlags |= OSSwapHostToBigConstInt16(kFinderInvisibleMask);
1576		else
1577			*fdFlags &= ~OSSwapHostToBigConstInt16(kFinderInvisibleMask);
1578	}
1579
1580	/*
1581	 * Timestamp updates.
1582	 */
1583	VATTR_SET_SUPPORTED(vap, va_create_time);
1584	VATTR_SET_SUPPORTED(vap, va_access_time);
1585	VATTR_SET_SUPPORTED(vap, va_modify_time);
1586	VATTR_SET_SUPPORTED(vap, va_backup_time);
1587	VATTR_SET_SUPPORTED(vap, va_change_time);
1588	if (VATTR_IS_ACTIVE(vap, va_create_time) ||
1589	    VATTR_IS_ACTIVE(vap, va_access_time) ||
1590	    VATTR_IS_ACTIVE(vap, va_modify_time) ||
1591	    VATTR_IS_ACTIVE(vap, va_backup_time)) {
1592		if (VATTR_IS_ACTIVE(vap, va_create_time))
1593			cp->c_itime = vap->va_create_time.tv_sec;
1594		if (VATTR_IS_ACTIVE(vap, va_access_time)) {
1595			cp->c_atime = vap->va_access_time.tv_sec;
1596			cp->c_touch_acctime = FALSE;
1597		}
1598		if (VATTR_IS_ACTIVE(vap, va_modify_time)) {
1599			cp->c_mtime = vap->va_modify_time.tv_sec;
1600			cp->c_touch_modtime = FALSE;
1601			cp->c_touch_chgtime = TRUE;
1602
1603			/*
1604			 * The utimes system call can reset the modification
1605			 * time but it doesn't know about HFS create times.
1606			 * So we need to ensure that the creation time is
1607			 * always at least as old as the modification time.
1608			 */
1609			if ((VTOVCB(vp)->vcbSigWord == kHFSPlusSigWord) &&
1610			    (cp->c_cnid != kHFSRootFolderID) &&
1611			    (cp->c_mtime < cp->c_itime)) {
1612				cp->c_itime = cp->c_mtime;
1613			}
1614		}
1615		if (VATTR_IS_ACTIVE(vap, va_backup_time))
1616			cp->c_btime = vap->va_backup_time.tv_sec;
1617		cp->c_flag |= C_MODIFIED;
1618	}
1619
1620	/*
1621	 * Set name encoding.
1622	 */
1623	VATTR_SET_SUPPORTED(vap, va_encoding);
1624	if (VATTR_IS_ACTIVE(vap, va_encoding)) {
1625		cp->c_encoding = vap->va_encoding;
1626		hfs_setencodingbits(hfsmp, cp->c_encoding);
1627	}
1628
1629	if ((error = hfs_update(vp, TRUE)) != 0)
1630		goto out;
1631out:
1632	if (cp) {
1633		/* Purge origin cache for cnode, since caller now has correct link ID for it
1634		 * We purge it here since it was acquired for us during lookup, and we no longer need it.
1635		 */
1636		if ((cp->c_flag & C_HARDLINK) && (vp->v_type != VDIR)){
1637			hfs_relorigin(cp, 0);
1638		}
1639
1640		hfs_unlock(cp);
1641#if HFS_COMPRESSION
1642		if (decmpfs_reset_state) {
1643			/*
1644			 * we've changed the UF_COMPRESSED flag, so reset the decmpfs state for this cnode
1645			 * but don't do it while holding the hfs cnode lock
1646			 */
1647			decmpfs_cnode *dp = VTOCMP(vp);
1648			if (!dp) {
1649				/*
1650				 * call hfs_lazy_init_decmpfs_cnode() to make sure that the decmpfs_cnode
1651				 * is filled in; we need a decmpfs_cnode to prevent decmpfs state changes
1652				 * on this file if it's locked
1653				 */
1654				dp = hfs_lazy_init_decmpfs_cnode(VTOC(vp));
1655				if (!dp) {
1656					/* failed to allocate a decmpfs_cnode */
1657					return ENOMEM; /* what should this be? */
1658				}
1659			}
1660			decmpfs_cnode_set_vnode_state(dp, FILE_TYPE_UNKNOWN, 0);
1661		}
1662#endif
1663	}
1664	return (error);
1665}
1666
1667
1668/*
1669 * Change the mode on a file.
1670 * cnode must be locked before calling.
1671 */
1672int
1673hfs_chmod(struct vnode *vp, int mode, __unused kauth_cred_t cred, __unused struct proc *p)
1674{
1675	register struct cnode *cp = VTOC(vp);
1676
1677	if (VTOVCB(vp)->vcbSigWord != kHFSPlusSigWord)
1678		return (0);
1679
1680	// Don't allow modification of the journal or journal_info_block
1681	if (hfs_is_journal_file(VTOHFS(vp), cp)) {
1682		return EPERM;
1683	}
1684
1685#if OVERRIDE_UNKNOWN_PERMISSIONS
1686	if (((unsigned int)vfs_flags(VTOVFS(vp))) & MNT_UNKNOWNPERMISSIONS) {
1687		return (0);
1688	};
1689#endif
1690	cp->c_mode &= ~ALLPERMS;
1691	cp->c_mode |= (mode & ALLPERMS);
1692	cp->c_touch_chgtime = TRUE;
1693	return (0);
1694}
1695
1696
1697int
1698hfs_write_access(struct vnode *vp, kauth_cred_t cred, struct proc *p, Boolean considerFlags)
1699{
1700	struct cnode *cp = VTOC(vp);
1701	int retval = 0;
1702	int is_member;
1703
1704	/*
1705	 * Disallow write attempts on read-only file systems;
1706	 * unless the file is a socket, fifo, or a block or
1707	 * character device resident on the file system.
1708	 */
1709	switch (vnode_vtype(vp)) {
1710	case VDIR:
1711 	case VLNK:
1712	case VREG:
1713		if (VTOHFS(vp)->hfs_flags & HFS_READ_ONLY)
1714			return (EROFS);
1715		break;
1716	default:
1717		break;
1718 	}
1719
1720	/* If immutable bit set, nobody gets to write it. */
1721	if (considerFlags && (cp->c_bsdflags & IMMUTABLE))
1722		return (EPERM);
1723
1724	/* Otherwise, user id 0 always gets access. */
1725	if (!suser(cred, NULL))
1726		return (0);
1727
1728	/* Otherwise, check the owner. */
1729	if ((retval = hfs_owner_rights(VTOHFS(vp), cp->c_uid, cred, p, false)) == 0)
1730		return ((cp->c_mode & S_IWUSR) == S_IWUSR ? 0 : EACCES);
1731
1732	/* Otherwise, check the groups. */
1733	if (kauth_cred_ismember_gid(cred, cp->c_gid, &is_member) == 0 && is_member) {
1734		return ((cp->c_mode & S_IWGRP) == S_IWGRP ? 0 : EACCES);
1735 	}
1736
1737	/* Otherwise, check everyone else. */
1738	return ((cp->c_mode & S_IWOTH) == S_IWOTH ? 0 : EACCES);
1739}
1740
1741
1742/*
1743 * Perform chown operation on cnode cp;
1744 * code must be locked prior to call.
1745 */
1746int
1747#if !QUOTA
1748hfs_chown(struct vnode *vp, uid_t uid, gid_t gid, __unused kauth_cred_t cred,
1749	__unused struct proc *p)
1750#else
1751hfs_chown(struct vnode *vp, uid_t uid, gid_t gid, kauth_cred_t cred,
1752	__unused struct proc *p)
1753#endif
1754{
1755	register struct cnode *cp = VTOC(vp);
1756	uid_t ouid;
1757	gid_t ogid;
1758#if QUOTA
1759	int error = 0;
1760	register int i;
1761	int64_t change;
1762#endif /* QUOTA */
1763
1764	if (VTOVCB(vp)->vcbSigWord != kHFSPlusSigWord)
1765		return (ENOTSUP);
1766
1767	if (((unsigned int)vfs_flags(VTOVFS(vp))) & MNT_UNKNOWNPERMISSIONS)
1768		return (0);
1769
1770	if (uid == (uid_t)VNOVAL)
1771		uid = cp->c_uid;
1772	if (gid == (gid_t)VNOVAL)
1773		gid = cp->c_gid;
1774
1775#if 0	/* we are guaranteed that this is already the case */
1776	/*
1777	 * If we don't own the file, are trying to change the owner
1778	 * of the file, or are not a member of the target group,
1779	 * the caller must be superuser or the call fails.
1780	 */
1781	if ((kauth_cred_getuid(cred) != cp->c_uid || uid != cp->c_uid ||
1782	    (gid != cp->c_gid &&
1783	     (kauth_cred_ismember_gid(cred, gid, &is_member) || !is_member))) &&
1784	    (error = suser(cred, 0)))
1785		return (error);
1786#endif
1787
1788	ogid = cp->c_gid;
1789	ouid = cp->c_uid;
1790#if QUOTA
1791	if ((error = hfs_getinoquota(cp)))
1792		return (error);
1793	if (ouid == uid) {
1794		dqrele(cp->c_dquot[USRQUOTA]);
1795		cp->c_dquot[USRQUOTA] = NODQUOT;
1796	}
1797	if (ogid == gid) {
1798		dqrele(cp->c_dquot[GRPQUOTA]);
1799		cp->c_dquot[GRPQUOTA] = NODQUOT;
1800	}
1801
1802	/*
1803	 * Eventually need to account for (fake) a block per directory
1804	 * if (vnode_isdir(vp))
1805	 *     change = VTOHFS(vp)->blockSize;
1806	 * else
1807	 */
1808
1809	change = (int64_t)(cp->c_blocks) * (int64_t)VTOVCB(vp)->blockSize;
1810	(void) hfs_chkdq(cp, -change, cred, CHOWN);
1811	(void) hfs_chkiq(cp, -1, cred, CHOWN);
1812	for (i = 0; i < MAXQUOTAS; i++) {
1813		dqrele(cp->c_dquot[i]);
1814		cp->c_dquot[i] = NODQUOT;
1815	}
1816#endif /* QUOTA */
1817	cp->c_gid = gid;
1818	cp->c_uid = uid;
1819#if QUOTA
1820	if ((error = hfs_getinoquota(cp)) == 0) {
1821		if (ouid == uid) {
1822			dqrele(cp->c_dquot[USRQUOTA]);
1823			cp->c_dquot[USRQUOTA] = NODQUOT;
1824		}
1825		if (ogid == gid) {
1826			dqrele(cp->c_dquot[GRPQUOTA]);
1827			cp->c_dquot[GRPQUOTA] = NODQUOT;
1828		}
1829		if ((error = hfs_chkdq(cp, change, cred, CHOWN)) == 0) {
1830			if ((error = hfs_chkiq(cp, 1, cred, CHOWN)) == 0)
1831				goto good;
1832			else
1833				(void) hfs_chkdq(cp, -change, cred, CHOWN|FORCE);
1834		}
1835		for (i = 0; i < MAXQUOTAS; i++) {
1836			dqrele(cp->c_dquot[i]);
1837			cp->c_dquot[i] = NODQUOT;
1838		}
1839	}
1840	cp->c_gid = ogid;
1841	cp->c_uid = ouid;
1842	if (hfs_getinoquota(cp) == 0) {
1843		if (ouid == uid) {
1844			dqrele(cp->c_dquot[USRQUOTA]);
1845			cp->c_dquot[USRQUOTA] = NODQUOT;
1846		}
1847		if (ogid == gid) {
1848			dqrele(cp->c_dquot[GRPQUOTA]);
1849			cp->c_dquot[GRPQUOTA] = NODQUOT;
1850		}
1851		(void) hfs_chkdq(cp, change, cred, FORCE|CHOWN);
1852		(void) hfs_chkiq(cp, 1, cred, FORCE|CHOWN);
1853		(void) hfs_getinoquota(cp);
1854	}
1855	return (error);
1856good:
1857	if (hfs_getinoquota(cp))
1858		panic("hfs_chown: lost quota");
1859#endif /* QUOTA */
1860
1861
1862	/*
1863	  According to the SUSv3 Standard, chown() shall mark
1864	  for update the st_ctime field of the file.
1865	  (No exceptions mentioned)
1866	*/
1867		cp->c_touch_chgtime = TRUE;
1868	return (0);
1869}
1870
1871
1872/*
1873 * hfs_vnop_exchange:
1874 *
1875 * Inputs:
1876 * 		'from' vnode/cnode
1877 * 		'to' vnode/cnode
1878 * 		options flag bits
1879 * 		vfs_context
1880 *
1881 * Discussion:
1882 * hfs_vnop_exchange is used to service the exchangedata(2) system call.
1883 * Per the requirements of that system call, this function "swaps" some
1884 * of the information that lives in one catalog record for some that
1885 * lives in another.  Note that not everything is swapped; in particular,
1886 * the extent information stored in each cnode is kept local to that
1887 * cnode.  This allows existing file descriptor references to continue
1888 * to operate on the same content, regardless of the location in the
1889 * namespace that the file may have moved to.  See inline comments
1890 * in the function for more information.
1891 */
1892int
1893hfs_vnop_exchange(ap)
1894	struct vnop_exchange_args /* {
1895		struct vnode *a_fvp;
1896		struct vnode *a_tvp;
1897		int a_options;
1898		vfs_context_t a_context;
1899	} */ *ap;
1900{
1901	struct vnode *from_vp = ap->a_fvp;
1902	struct vnode *to_vp = ap->a_tvp;
1903	struct cnode *from_cp;
1904	struct cnode *to_cp;
1905	struct hfsmount *hfsmp;
1906	struct cat_desc tempdesc;
1907	struct cat_attr tempattr;
1908	const unsigned char *from_nameptr;
1909	const unsigned char *to_nameptr;
1910	char from_iname[32];
1911	char to_iname[32];
1912	uint32_t to_flag_special;
1913	uint32_t from_flag_special;
1914	cnid_t  from_parid;
1915	cnid_t  to_parid;
1916	int lockflags;
1917	int error = 0, started_tr = 0, got_cookie = 0;
1918	cat_cookie_t cookie;
1919	time_t orig_from_ctime, orig_to_ctime;
1920
1921	/*
1922	 * VFS does the following checks:
1923	 * 1. Validate that both are files.
1924	 * 2. Validate that both are on the same mount.
1925	 * 3. Validate that they're not the same vnode.
1926	 */
1927
1928	orig_from_ctime = VTOC(from_vp)->c_ctime;
1929	orig_to_ctime = VTOC(to_vp)->c_ctime;
1930
1931
1932#if CONFIG_PROTECT
1933	/*
1934	 * Do not allow exchangedata/F_MOVEDATAEXTENTS on data-protected filesystems
1935	 * because the EAs will not be swapped.  As a result, the persistent keys would not
1936	 * match and the files will be garbage.
1937	 */
1938	if (cp_fs_protected (vnode_mount(from_vp))) {
1939		return EINVAL;
1940	}
1941#endif
1942
1943#if HFS_COMPRESSION
1944	if ( hfs_file_is_compressed(VTOC(from_vp), 0) ) {
1945		if ( 0 != ( error = decmpfs_decompress_file(from_vp, VTOCMP(from_vp), -1, 0, 1) ) ) {
1946			return error;
1947		}
1948	}
1949
1950	if ( hfs_file_is_compressed(VTOC(to_vp), 0) ) {
1951		if ( 0 != ( error = decmpfs_decompress_file(to_vp, VTOCMP(to_vp), -1, 0, 1) ) ) {
1952			return error;
1953		}
1954	}
1955#endif // HFS_COMPRESSION
1956
1957	/*
1958	 * Normally, we want to notify the user handlers about the event,
1959	 * except if it's a handler driving the event.
1960	 */
1961	if ((ap->a_options & FSOPT_EXCHANGE_DATA_ONLY) == 0) {
1962		check_for_tracked_file(from_vp, orig_from_ctime, NAMESPACE_HANDLER_WRITE_OP, NULL);
1963		check_for_tracked_file(to_vp, orig_to_ctime, NAMESPACE_HANDLER_WRITE_OP, NULL);
1964	} else {
1965		/*
1966		 * We're doing a data-swap.
1967		 * Take the truncate lock/cnode lock, then verify there are no mmap references.
1968		 * Issue a hfs_filedone to flush out all of the remaining state for this file.
1969		 * Allow the rest of the codeflow to re-acquire the cnode locks in order.
1970		 */
1971
1972		hfs_lock_truncate (VTOC(from_vp), HFS_SHARED_LOCK, HFS_LOCK_DEFAULT);
1973
1974		if ((error = hfs_lock(VTOC(from_vp), HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT))) {
1975			hfs_unlock_truncate (VTOC(from_vp), HFS_LOCK_DEFAULT);
1976			return error;
1977		}
1978
1979		/* Verify the source file is not in use by anyone besides us (including mmap refs) */
1980		if (vnode_isinuse(from_vp, 1)) {
1981			error = EBUSY;
1982			hfs_unlock(VTOC(from_vp));
1983			hfs_unlock_truncate (VTOC(from_vp), HFS_LOCK_DEFAULT);
1984			return error;
1985		}
1986
1987		/* Flush out the data in the source file */
1988		VTOC(from_vp)->c_flag |= C_SWAPINPROGRESS;
1989		error = hfs_filedone (from_vp, ap->a_context);
1990		VTOC(from_vp)->c_flag &= ~C_SWAPINPROGRESS;
1991		hfs_unlock(VTOC(from_vp));
1992		hfs_unlock_truncate(VTOC(from_vp), HFS_LOCK_DEFAULT);
1993
1994		if (error) {
1995			return error;
1996		}
1997	}
1998
1999	if ((error = hfs_lockpair(VTOC(from_vp), VTOC(to_vp), HFS_EXCLUSIVE_LOCK)))
2000		return (error);
2001
2002	from_cp = VTOC(from_vp);
2003	to_cp = VTOC(to_vp);
2004	hfsmp = VTOHFS(from_vp);
2005
2006	/* Resource forks cannot be exchanged. */
2007	if ( VNODE_IS_RSRC(from_vp) || VNODE_IS_RSRC(to_vp)) {
2008		error = EINVAL;
2009		goto exit;
2010	}
2011
2012	// Don't allow modification of the journal or journal_info_block
2013	if (hfs_is_journal_file(hfsmp, from_cp) ||
2014	    hfs_is_journal_file(hfsmp, to_cp)) {
2015		error = EPERM;
2016		goto exit;
2017	}
2018
2019	/*
2020	 * Ok, now that all of the pre-flighting is done, call the underlying
2021	 * function if needed.
2022	 */
2023	if (ap->a_options & FSOPT_EXCHANGE_DATA_ONLY) {
2024		error = hfs_movedata(from_vp, to_vp);
2025		goto exit;
2026	}
2027
2028
2029	if ((error = hfs_start_transaction(hfsmp)) != 0) {
2030	    goto exit;
2031	}
2032	started_tr = 1;
2033
2034	/*
2035	 * Reserve some space in the Catalog file.
2036	 */
2037	if ((error = cat_preflight(hfsmp, CAT_EXCHANGE, &cookie, vfs_context_proc(ap->a_context)))) {
2038		goto exit;
2039	}
2040	got_cookie = 1;
2041
2042	/* The backend code always tries to delete the virtual
2043	 * extent id for exchanging files so we need to lock
2044	 * the extents b-tree.
2045	 */
2046	lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG | SFL_EXTENTS | SFL_ATTRIBUTE, HFS_EXCLUSIVE_LOCK);
2047
2048	/* Account for the location of the catalog objects. */
2049	if (from_cp->c_flag & C_HARDLINK) {
2050		MAKE_INODE_NAME(from_iname, sizeof(from_iname),
2051				from_cp->c_attr.ca_linkref);
2052		from_nameptr = (unsigned char *)from_iname;
2053		from_parid = hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid;
2054		from_cp->c_hint = 0;
2055	} else {
2056		from_nameptr = from_cp->c_desc.cd_nameptr;
2057		from_parid = from_cp->c_parentcnid;
2058	}
2059	if (to_cp->c_flag & C_HARDLINK) {
2060		MAKE_INODE_NAME(to_iname, sizeof(to_iname),
2061				to_cp->c_attr.ca_linkref);
2062		to_nameptr = (unsigned char *)to_iname;
2063		to_parid = hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid;
2064		to_cp->c_hint = 0;
2065	} else {
2066		to_nameptr = to_cp->c_desc.cd_nameptr;
2067		to_parid = to_cp->c_parentcnid;
2068	}
2069
2070	/*
2071	 * ExchangeFileIDs swaps the extent information attached to two
2072	 * different file IDs.  It also swaps the extent information that
2073	 * may live in the extents-overflow B-Tree.
2074	 *
2075	 * We do this in a transaction as this may require a lot of B-Tree nodes
2076	 * to do completely, particularly if one of the files in question
2077	 * has a lot of extents.
2078	 *
2079	 * For example, assume "file1" has fileID 50, and "file2" has fileID 52.
2080	 * For the on-disk records, which are assumed to be synced, we will
2081	 * first swap the resident inline-8 extents as part of the catalog records.
2082	 * Then we will swap any extents overflow records for each file.
2083	 *
2084	 * When this function is done, "file1" will have fileID 52, and "file2" will
2085	 * have fileID 50.
2086	 */
2087	error = ExchangeFileIDs(hfsmp, from_nameptr, to_nameptr, from_parid,
2088	                        to_parid, from_cp->c_hint, to_cp->c_hint);
2089	hfs_systemfile_unlock(hfsmp, lockflags);
2090
2091	/*
2092	 * Note that we don't need to exchange any extended attributes
2093	 * since the attributes are keyed by file ID.
2094	 */
2095
2096	if (error != E_NONE) {
2097		error = MacToVFSError(error);
2098		goto exit;
2099	}
2100
2101	/* Purge the vnodes from the name cache */
2102 	if (from_vp)
2103		cache_purge(from_vp);
2104	if (to_vp)
2105		cache_purge(to_vp);
2106
2107	/* Bump both source and destination write counts before any swaps. */
2108	{
2109		hfs_incr_gencount (from_cp);
2110		hfs_incr_gencount (to_cp);
2111	}
2112
2113
2114	/* Save a copy of "from" attributes before swapping. */
2115	bcopy(&from_cp->c_desc, &tempdesc, sizeof(struct cat_desc));
2116	bcopy(&from_cp->c_attr, &tempattr, sizeof(struct cat_attr));
2117
2118	/* Save whether or not each cnode is a hardlink or has EAs */
2119	from_flag_special = from_cp->c_flag & (C_HARDLINK | C_HASXATTRS);
2120	to_flag_special = to_cp->c_flag & (C_HARDLINK | C_HASXATTRS);
2121
2122	/* Drop the special bits from each cnode */
2123	from_cp->c_flag &= ~(C_HARDLINK | C_HASXATTRS);
2124	to_cp->c_flag &= ~(C_HARDLINK | C_HASXATTRS);
2125
2126	/*
2127	 * Complete the in-memory portion of the copy.
2128	 *
2129	 * ExchangeFileIDs swaps the on-disk records involved.  We complete the
2130	 * operation by swapping the in-memory contents of the two files here.
2131	 * We swap the cnode descriptors, which contain name, BSD attributes,
2132	 * timestamps, etc, about the file.
2133	 *
2134	 * NOTE: We do *NOT* swap the fileforks of the two cnodes.  We have
2135	 * already swapped the on-disk extent information.  As long as we swap the
2136	 * IDs, the in-line resident 8 extents that live in the filefork data
2137	 * structure will point to the right data for the new file ID if we leave
2138	 * them alone.
2139	 *
2140	 * As a result, any file descriptor that points to a particular
2141	 * vnode (even though it should change names), will continue
2142	 * to point to the same content.
2143	 */
2144
2145	/* Copy the "to" -> "from" cnode */
2146	bcopy(&to_cp->c_desc, &from_cp->c_desc, sizeof(struct cat_desc));
2147
2148	from_cp->c_hint = 0;
2149	/*
2150	 * If 'to' was a hardlink, then we copied over its link ID/CNID/(namespace ID)
2151	 * when we bcopy'd the descriptor above.  However, the cnode attributes
2152	 * are not bcopied.  As a result, make sure to swap the file IDs of each item.
2153	 *
2154	 * Further, other hardlink attributes must be moved along in this swap:
2155	 * the linkcount, the linkref, and the firstlink all need to move
2156	 * along with the file IDs.  See note below regarding the flags and
2157	 * what moves vs. what does not.
2158	 *
2159	 * For Reference:
2160	 * linkcount == total # of hardlinks.
2161	 * linkref == the indirect inode pointer.
2162	 * firstlink == the first hardlink in the chain (written to the raw inode).
2163	 * These three are tied to the fileID and must move along with the rest of the data.
2164	 */
2165	from_cp->c_fileid = to_cp->c_attr.ca_fileid;
2166
2167	from_cp->c_itime = to_cp->c_itime;
2168	from_cp->c_btime = to_cp->c_btime;
2169	from_cp->c_atime = to_cp->c_atime;
2170	from_cp->c_ctime = to_cp->c_ctime;
2171	from_cp->c_gid = to_cp->c_gid;
2172	from_cp->c_uid = to_cp->c_uid;
2173	from_cp->c_bsdflags = to_cp->c_bsdflags;
2174	from_cp->c_mode = to_cp->c_mode;
2175	from_cp->c_linkcount = to_cp->c_linkcount;
2176	from_cp->c_attr.ca_linkref = to_cp->c_attr.ca_linkref;
2177	from_cp->c_attr.ca_firstlink = to_cp->c_attr.ca_firstlink;
2178
2179	/*
2180	 * The cnode flags need to stay with the cnode and not get transferred
2181	 * over along with everything else because they describe the content; they are
2182	 * not attributes that reflect changes specific to the file ID.  In general,
2183	 * fields that are tied to the file ID are the ones that will move.
2184	 *
2185	 * This reflects the fact that the file may have borrowed blocks, dirty metadata,
2186	 * or other extents, which may not yet have been written to the catalog.  If
2187	 * they were, they would have been transferred above in the ExchangeFileIDs call above...
2188	 *
2189	 * The flags that are special are:
2190	 * C_HARDLINK, C_HASXATTRS
2191	 *
2192	 * These flags move with the item and file ID in the namespace since their
2193	 * state is tied to that of the file ID.
2194	 *
2195	 * So to transfer the flags, we have to take the following steps
2196	 * 1) Store in a localvar whether or not the special bits are set.
2197	 * 2) Drop the special bits from the current flags
2198	 * 3) swap the special flag bits to their destination
2199	 */
2200	from_cp->c_flag |= to_flag_special;
2201	from_cp->c_attr.ca_recflags = to_cp->c_attr.ca_recflags;
2202	bcopy(to_cp->c_finderinfo, from_cp->c_finderinfo, 32);
2203
2204
2205	/* Copy the "from" -> "to" cnode */
2206	bcopy(&tempdesc, &to_cp->c_desc, sizeof(struct cat_desc));
2207	to_cp->c_hint = 0;
2208	/*
2209	 * Pull the file ID from the tempattr we copied above. We can't assume
2210	 * it is the same as the CNID.
2211	 */
2212	to_cp->c_fileid = tempattr.ca_fileid;
2213	to_cp->c_itime = tempattr.ca_itime;
2214	to_cp->c_btime = tempattr.ca_btime;
2215	to_cp->c_atime = tempattr.ca_atime;
2216	to_cp->c_ctime = tempattr.ca_ctime;
2217	to_cp->c_gid = tempattr.ca_gid;
2218	to_cp->c_uid = tempattr.ca_uid;
2219	to_cp->c_bsdflags = tempattr.ca_flags;
2220	to_cp->c_mode = tempattr.ca_mode;
2221	to_cp->c_linkcount = tempattr.ca_linkcount;
2222	to_cp->c_attr.ca_linkref = tempattr.ca_linkref;
2223	to_cp->c_attr.ca_firstlink = tempattr.ca_firstlink;
2224
2225	/*
2226	 * Only OR in the "from" flags into our cnode flags below.
2227	 * Leave the rest of the flags alone.
2228	 */
2229	to_cp->c_flag |= from_flag_special;
2230
2231	to_cp->c_attr.ca_recflags = tempattr.ca_recflags;
2232	bcopy(tempattr.ca_finderinfo, to_cp->c_finderinfo, 32);
2233
2234
2235	/* Rehash the cnodes using their new file IDs */
2236	hfs_chash_rehash(hfsmp, from_cp, to_cp);
2237
2238	/*
2239	 * When a file moves out of "Cleanup At Startup"
2240	 * we can drop its NODUMP status.
2241	 */
2242	if ((from_cp->c_bsdflags & UF_NODUMP) &&
2243	    (from_cp->c_parentcnid != to_cp->c_parentcnid)) {
2244		from_cp->c_bsdflags &= ~UF_NODUMP;
2245		from_cp->c_touch_chgtime = TRUE;
2246	}
2247	if ((to_cp->c_bsdflags & UF_NODUMP) &&
2248	    (to_cp->c_parentcnid != from_cp->c_parentcnid)) {
2249		to_cp->c_bsdflags &= ~UF_NODUMP;
2250		to_cp->c_touch_chgtime = TRUE;
2251	}
2252
2253exit:
2254	if (got_cookie) {
2255	        cat_postflight(hfsmp, &cookie, vfs_context_proc(ap->a_context));
2256	}
2257	if (started_tr) {
2258	    hfs_end_transaction(hfsmp);
2259	}
2260
2261	hfs_unlockpair(from_cp, to_cp);
2262	return (error);
2263}
2264
2265int
2266hfs_vnop_mmap(struct vnop_mmap_args *ap)
2267{
2268	struct vnode *vp = ap->a_vp;
2269	int error;
2270
2271	if (VNODE_IS_RSRC(vp)) {
2272		/* allow pageins of the resource fork */
2273	} else {
2274		int compressed = hfs_file_is_compressed(VTOC(vp), 1); /* 1 == don't take the cnode lock */
2275		time_t orig_ctime = VTOC(vp)->c_ctime;
2276
2277		if (!compressed && (VTOC(vp)->c_bsdflags & UF_COMPRESSED)) {
2278			error = check_for_dataless_file(vp, NAMESPACE_HANDLER_READ_OP);
2279			if (error != 0) {
2280				return error;
2281			}
2282		}
2283
2284		if (ap->a_fflags & PROT_WRITE) {
2285			check_for_tracked_file(vp, orig_ctime, NAMESPACE_HANDLER_WRITE_OP, NULL);
2286
2287			/* even though we're manipulating a cnode field here, we're only monotonically increasing
2288			 * the generation counter.  The vnode can't be recycled (because we hold a FD in order to cause the
2289			 * map to happen).  So it's safe to do this without holding the cnode lock.  The caller's only
2290			  * requirement is that the number has been changed.
2291			 */
2292			struct cnode *cp = VTOC(vp);
2293			if (S_ISREG(cp->c_attr.ca_mode) || S_ISLNK(cp->c_attr.ca_mode)) {
2294				hfs_incr_gencount(cp);
2295			}
2296		}
2297	}
2298
2299	//
2300	// NOTE: we return ENOTSUP because we want the cluster layer
2301	//       to actually do all the real work.
2302	//
2303	return (ENOTSUP);
2304}
2305
2306/*
2307 * hfs_movedata
2308 *
2309 * This is a non-symmetric variant of exchangedata.  In this function,
2310 * the contents of the fork in from_vp are moved to the fork
2311 * specified by to_vp.
2312 *
2313 * The cnodes pointed to by 'from_vp' and 'to_vp' must be locked.
2314 *
2315 * The vnode pointed to by 'to_vp' *must* be empty prior to invoking this function.
2316 * We impose this restriction because we may not be able to fully delete the entire
2317 * file's contents in a single transaction, particularly if it has a lot of extents.
2318 * In the normal file deletion codepath, the file is screened for two conditions:
2319 * 1) bigger than 400MB, and 2) more than 8 extents.  If so, the file is relocated to
2320 * the hidden directory and the deletion is broken up into multiple truncates.  We can't
2321 * do that here because both files need to exist in the namespace. The main reason this
2322 * is imposed is that we may have to touch a whole lot of bitmap blocks if there are
2323 * many extents.
2324 *
2325 * Any data written to 'from_vp' after this call completes is not guaranteed
2326 * to be moved.
2327 *
2328 * Arguments:
2329 * vnode from_vp: source file
2330 * vnode to_vp: destination file; must be empty
2331 *
2332 * Returns:
2333 *	EFBIG - Destination file was not empty
2334 *	0	- success
2335 *
2336 *
2337 */
2338int hfs_movedata (struct vnode *from_vp, struct vnode *to_vp) {
2339
2340	struct cnode *from_cp;
2341	struct cnode *to_cp;
2342	struct hfsmount *hfsmp = NULL;
2343	int error = 0;
2344	int started_tr = 0;
2345	int lockflags = 0;
2346	int overflow_blocks;
2347	int rsrc = 0;
2348
2349
2350	/* Get the HFS pointers */
2351	from_cp = VTOC(from_vp);
2352	to_cp = VTOC(to_vp);
2353	hfsmp = VTOHFS(from_vp);
2354
2355	/* Verify that neither source/dest file is open-unlinked */
2356	if (from_cp->c_flag & (C_DELETED | C_NOEXISTS)) {
2357		error = EBUSY;
2358		goto movedata_exit;
2359	}
2360
2361	if (to_cp->c_flag & (C_DELETED | C_NOEXISTS)) {
2362		error = EBUSY;
2363		goto movedata_exit;
2364	}
2365
2366	/*
2367	 * Verify the source file is not in use by anyone besides us.
2368	 *
2369	 * This function is typically invoked by a namespace handler
2370	 * process responding to a temporarily stalled system call.
2371	 * The FD that it is working off of is opened O_EVTONLY, so
2372	 * it really has no active usecounts (the kusecount from O_EVTONLY
2373	 * is subtracted from the total usecounts).
2374	 *
2375	 * As a result, we shouldn't have any active usecounts against
2376	 * this vnode when we go to check it below.
2377	 */
2378	if (vnode_isinuse(from_vp, 0)) {
2379		error = EBUSY;
2380		goto movedata_exit;
2381	}
2382
2383	if (from_cp->c_rsrc_vp == from_vp) {
2384		rsrc = 1;
2385	}
2386
2387	/*
2388	 * We assume that the destination file is already empty.
2389	 * Verify that it is.
2390	 */
2391	if (rsrc) {
2392		if (to_cp->c_rsrcfork->ff_size > 0) {
2393			error = EFBIG;
2394			goto movedata_exit;
2395		}
2396	}
2397	else {
2398		if (to_cp->c_datafork->ff_size > 0) {
2399			error = EFBIG;
2400			goto movedata_exit;
2401		}
2402	}
2403
2404	/* If the source has the rsrc open, make sure the destination is also the rsrc */
2405	if (rsrc) {
2406		if (to_vp != to_cp->c_rsrc_vp) {
2407			error = EINVAL;
2408			goto movedata_exit;
2409		}
2410	}
2411	else {
2412		/* Verify that both forks are data forks */
2413		if (to_vp != to_cp->c_vp) {
2414			error = EINVAL;
2415			goto movedata_exit;
2416		}
2417	}
2418
2419	/*
2420	 * See if the source file has overflow extents.  If it doesn't, we don't
2421	 * need to call into MoveData, and the catalog will be enough.
2422	 */
2423	if (rsrc) {
2424		overflow_blocks = overflow_extents(from_cp->c_rsrcfork);
2425	}
2426	else {
2427		overflow_blocks = overflow_extents(from_cp->c_datafork);
2428	}
2429
2430	if ((error = hfs_start_transaction (hfsmp)) != 0) {
2431		goto movedata_exit;
2432	}
2433	started_tr = 1;
2434
2435	/* Lock the system files: catalog, extents, attributes */
2436	lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG | SFL_EXTENTS | SFL_ATTRIBUTE, HFS_EXCLUSIVE_LOCK);
2437
2438	/* Copy over any catalog allocation data into the new spot. */
2439	if (rsrc) {
2440		if ((error = hfs_move_fork (from_cp->c_rsrcfork, from_cp, to_cp->c_rsrcfork, to_cp))){
2441			hfs_systemfile_unlock(hfsmp, lockflags);
2442			goto movedata_exit;
2443		}
2444	}
2445	else {
2446		if ((error = hfs_move_fork (from_cp->c_datafork, from_cp, to_cp->c_datafork, to_cp))) {
2447			hfs_systemfile_unlock(hfsmp, lockflags);
2448			goto movedata_exit;
2449		}
2450	}
2451
2452	/*
2453	 * Note that because all we're doing is moving the extents around, we can
2454	 * probably do this in a single transaction:  Each extent record (group of 8)
2455	 * is 64 bytes.  A extent overflow B-Tree node is typically 4k.  This means
2456	 * each node can hold roughly ~60 extent records == (480 extents).
2457	 *
2458	 * If a file was massively fragmented and had 20k extents, this means we'd
2459	 * roughly touch 20k/480 == 41 to 42 nodes, plus the index nodes, for half
2460	 * of the operation.  (inserting or deleting). So if we're manipulating 80-100
2461	 * nodes, this is basically 320k of data to write to the journal in
2462	 * a bad case.
2463	 */
2464	if (overflow_blocks != 0) {
2465		if (rsrc) {
2466			error = MoveData(hfsmp, from_cp->c_cnid, to_cp->c_cnid, 1);
2467		}
2468		else {
2469			error = MoveData (hfsmp, from_cp->c_cnid, to_cp->c_cnid, 0);
2470		}
2471	}
2472
2473	if (error) {
2474		/* Reverse the operation. Copy the fork data back into the source */
2475		if (rsrc) {
2476			hfs_move_fork (to_cp->c_rsrcfork, to_cp, from_cp->c_rsrcfork, from_cp);
2477		}
2478		else {
2479			hfs_move_fork (to_cp->c_datafork, to_cp, from_cp->c_datafork, from_cp);
2480		}
2481	}
2482	else {
2483		struct cat_fork *src_data = NULL;
2484		struct cat_fork *src_rsrc = NULL;
2485		struct cat_fork *dst_data = NULL;
2486		struct cat_fork *dst_rsrc = NULL;
2487
2488		/* Touch the times*/
2489		to_cp->c_touch_acctime = TRUE;
2490		to_cp->c_touch_chgtime = TRUE;
2491		to_cp->c_touch_modtime = TRUE;
2492
2493		from_cp->c_touch_acctime = TRUE;
2494		from_cp->c_touch_chgtime = TRUE;
2495		from_cp->c_touch_modtime = TRUE;
2496
2497		hfs_touchtimes(hfsmp, to_cp);
2498		hfs_touchtimes(hfsmp, from_cp);
2499
2500		if (from_cp->c_datafork) {
2501			src_data = &from_cp->c_datafork->ff_data;
2502		}
2503		if (from_cp->c_rsrcfork) {
2504			src_rsrc = &from_cp->c_rsrcfork->ff_data;
2505		}
2506
2507		if (to_cp->c_datafork) {
2508			dst_data = &to_cp->c_datafork->ff_data;
2509		}
2510		if (to_cp->c_rsrcfork) {
2511			dst_rsrc = &to_cp->c_rsrcfork->ff_data;
2512		}
2513
2514		/* Update the catalog nodes */
2515		(void) cat_update(hfsmp, &from_cp->c_desc, &from_cp->c_attr,
2516						  src_data, src_rsrc);
2517
2518		(void) cat_update(hfsmp, &to_cp->c_desc, &to_cp->c_attr,
2519						  dst_data, dst_rsrc);
2520
2521	}
2522	/* unlock the system files */
2523	hfs_systemfile_unlock(hfsmp, lockflags);
2524
2525
2526movedata_exit:
2527	if (started_tr) {
2528		hfs_end_transaction(hfsmp);
2529	}
2530
2531	return error;
2532
2533}
2534
2535/*
2536 * Copy all of the catalog and runtime data in srcfork to dstfork.
2537 *
2538 * This allows us to maintain the invalid ranges across the movedata operation so
2539 * we don't need to force all of the pending IO right now. In addition, we move all
2540 * non overflow-extent extents into the destination here.
2541 */
2542static int hfs_move_fork (struct filefork *srcfork, struct cnode *src_cp,
2543						  struct filefork *dstfork, struct cnode *dst_cp) {
2544	struct rl_entry *invalid_range;
2545	int size = sizeof(struct HFSPlusExtentDescriptor);
2546	size = size * kHFSPlusExtentDensity;
2547
2548	/* If the dstfork has any invalid ranges, bail out */
2549	invalid_range = TAILQ_FIRST(&dstfork->ff_invalidranges);
2550	if (invalid_range != NULL) {
2551		return EFBIG;
2552	}
2553
2554	if (dstfork->ff_data.cf_size != 0 || dstfork->ff_data.cf_new_size != 0) {
2555		return EFBIG;
2556	}
2557
2558	/* First copy the invalid ranges */
2559	while ((invalid_range = TAILQ_FIRST(&srcfork->ff_invalidranges))) {
2560		off_t start = invalid_range->rl_start;
2561		off_t end = invalid_range->rl_end;
2562
2563		/* Remove it from the srcfork and add it to dstfork */
2564		rl_remove(start, end, &srcfork->ff_invalidranges);
2565		rl_add(start, end, &dstfork->ff_invalidranges);
2566	}
2567
2568	/*
2569	 * Ignore the ff_union.  We don't move symlinks or system files.
2570	 * Now copy the in-catalog extent information
2571	 */
2572	dstfork->ff_data.cf_size = srcfork->ff_data.cf_size;
2573	dstfork->ff_data.cf_new_size = srcfork->ff_data.cf_new_size;
2574	dstfork->ff_data.cf_vblocks = srcfork->ff_data.cf_vblocks;
2575	dstfork->ff_data.cf_blocks = srcfork->ff_data.cf_blocks;
2576
2577	/* just memcpy the whole array of extents to the new location. */
2578	memcpy (dstfork->ff_data.cf_extents, srcfork->ff_data.cf_extents, size);
2579
2580	/*
2581	 * Copy the cnode attribute data.
2582	 *
2583	 */
2584	src_cp->c_blocks -= srcfork->ff_data.cf_vblocks;
2585	src_cp->c_blocks -= srcfork->ff_data.cf_blocks;
2586
2587	dst_cp->c_blocks += srcfork->ff_data.cf_vblocks;
2588	dst_cp->c_blocks += srcfork->ff_data.cf_blocks;
2589
2590	/* Now delete the entries in the source fork */
2591	srcfork->ff_data.cf_size = 0;
2592	srcfork->ff_data.cf_new_size = 0;
2593	srcfork->ff_data.cf_union.cfu_bytesread = 0;
2594	srcfork->ff_data.cf_vblocks = 0;
2595	srcfork->ff_data.cf_blocks = 0;
2596
2597	/* Zero out the old extents */
2598	bzero (srcfork->ff_data.cf_extents, size);
2599	return 0;
2600}
2601
2602
2603/*
2604 *  cnode must be locked
2605 */
2606int
2607hfs_fsync(struct vnode *vp, int waitfor, int fullsync, struct proc *p)
2608{
2609	struct cnode *cp = VTOC(vp);
2610	struct filefork *fp = NULL;
2611	int retval = 0;
2612	struct hfsmount *hfsmp = VTOHFS(vp);
2613	struct rl_entry *invalid_range;
2614	struct timeval tv;
2615	int waitdata;		/* attributes necessary for data retrieval */
2616	int wait;		/* all other attributes (e.g. atime, etc.) */
2617	int lockflag;
2618	int took_trunc_lock = 0;
2619	int locked_buffers = 0;
2620
2621	/*
2622	 * Applications which only care about data integrity rather than full
2623	 * file integrity may opt out of (delay) expensive metadata update
2624	 * operations as a performance optimization.
2625	 */
2626	wait = (waitfor == MNT_WAIT);
2627	waitdata = (waitfor == MNT_DWAIT) | wait;
2628	if (always_do_fullfsync)
2629		fullsync = 1;
2630
2631	/* HFS directories don't have any data blocks. */
2632	if (vnode_isdir(vp))
2633		goto metasync;
2634	fp = VTOF(vp);
2635
2636	/*
2637	 * For system files flush the B-tree header and
2638	 * for regular files write out any clusters
2639	 */
2640	if (vnode_issystem(vp)) {
2641	    if (VTOF(vp)->fcbBTCBPtr != NULL) {
2642			// XXXdbg
2643			if (hfsmp->jnl == NULL) {
2644				BTFlushPath(VTOF(vp));
2645			}
2646	    }
2647	} else if (UBCINFOEXISTS(vp)) {
2648		hfs_unlock(cp);
2649		hfs_lock_truncate(cp, HFS_SHARED_LOCK, HFS_LOCK_DEFAULT);
2650		took_trunc_lock = 1;
2651
2652		if (fp->ff_unallocblocks != 0) {
2653			hfs_unlock_truncate(cp, HFS_LOCK_DEFAULT);
2654
2655			hfs_lock_truncate(cp, HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT);
2656		}
2657		/* Don't hold cnode lock when calling into cluster layer. */
2658		(void) cluster_push(vp, waitdata ? IO_SYNC : 0);
2659
2660		hfs_lock(cp, HFS_EXCLUSIVE_LOCK, HFS_LOCK_ALLOW_NOEXISTS);
2661	}
2662	/*
2663	 * When MNT_WAIT is requested and the zero fill timeout
2664	 * has expired then we must explicitly zero out any areas
2665	 * that are currently marked invalid (holes).
2666	 *
2667	 * Files with NODUMP can bypass zero filling here.
2668	 */
2669	if (fp && (((cp->c_flag & C_ALWAYS_ZEROFILL) && !TAILQ_EMPTY(&fp->ff_invalidranges)) ||
2670	    ((wait || (cp->c_flag & C_ZFWANTSYNC)) &&
2671		((cp->c_bsdflags & UF_NODUMP) == 0) &&
2672		UBCINFOEXISTS(vp) && (vnode_issystem(vp) ==0) &&
2673		cp->c_zftimeout != 0))) {
2674
2675		microuptime(&tv);
2676		if ((cp->c_flag & C_ALWAYS_ZEROFILL) == 0 && !fullsync && tv.tv_sec < (long)cp->c_zftimeout) {
2677			/* Remember that a force sync was requested. */
2678			cp->c_flag |= C_ZFWANTSYNC;
2679			goto datasync;
2680		}
2681		if (!TAILQ_EMPTY(&fp->ff_invalidranges)) {
2682			if (!took_trunc_lock || (cp->c_truncatelockowner == HFS_SHARED_OWNER)) {
2683				hfs_unlock(cp);
2684				if (took_trunc_lock) {
2685					hfs_unlock_truncate(cp, HFS_LOCK_DEFAULT);
2686				}
2687				hfs_lock_truncate(cp, HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT);
2688				hfs_lock(cp, HFS_EXCLUSIVE_LOCK, HFS_LOCK_ALLOW_NOEXISTS);
2689				took_trunc_lock = 1;
2690			}
2691			while ((invalid_range = TAILQ_FIRST(&fp->ff_invalidranges))) {
2692				off_t start = invalid_range->rl_start;
2693				off_t end = invalid_range->rl_end;
2694
2695				/* The range about to be written must be validated
2696				 * first, so that VNOP_BLOCKMAP() will return the
2697				 * appropriate mapping for the cluster code:
2698				 */
2699				rl_remove(start, end, &fp->ff_invalidranges);
2700
2701				/* Don't hold cnode lock when calling into cluster layer. */
2702				hfs_unlock(cp);
2703				(void) cluster_write(vp, (struct uio *) 0,
2704						     fp->ff_size, end + 1, start, (off_t)0,
2705						     IO_HEADZEROFILL | IO_NOZERODIRTY | IO_NOCACHE);
2706				hfs_lock(cp, HFS_EXCLUSIVE_LOCK, HFS_LOCK_ALLOW_NOEXISTS);
2707				cp->c_flag |= C_MODIFIED;
2708			}
2709			hfs_unlock(cp);
2710			(void) cluster_push(vp, waitdata ? IO_SYNC : 0);
2711			hfs_lock(cp, HFS_EXCLUSIVE_LOCK, HFS_LOCK_ALLOW_NOEXISTS);
2712		}
2713		cp->c_flag &= ~C_ZFWANTSYNC;
2714		cp->c_zftimeout = 0;
2715	}
2716datasync:
2717	if (took_trunc_lock) {
2718		hfs_unlock_truncate(cp, HFS_LOCK_DEFAULT);
2719		took_trunc_lock = 0;
2720	}
2721	/*
2722	 * if we have a journal and if journal_active() returns != 0 then the
2723	 * we shouldn't do anything to a locked block (because it is part
2724	 * of a transaction).  otherwise we'll just go through the normal
2725	 * code path and flush the buffer.  note journal_active() can return
2726	 * -1 if the journal is invalid -- however we still need to skip any
2727	 * locked blocks as they get cleaned up when we finish the transaction
2728	 * or close the journal.
2729	 */
2730	// if (hfsmp->jnl && journal_active(hfsmp->jnl) >= 0)
2731	if (hfsmp->jnl)
2732	        lockflag = BUF_SKIP_LOCKED;
2733	else
2734	        lockflag = 0;
2735
2736	/*
2737	 * Flush all dirty buffers associated with a vnode.
2738	 * Record how many of them were dirty AND locked (if necessary).
2739	 */
2740	locked_buffers = buf_flushdirtyblks_skipinfo(vp, waitdata, lockflag, "hfs_fsync");
2741	if ((lockflag & BUF_SKIP_LOCKED) && (locked_buffers) && (vnode_vtype(vp) == VLNK)) {
2742		/*
2743		 * If there are dirty symlink buffers, then we may need to take action
2744		 * to prevent issues later on if we are journaled. If we're fsyncing a
2745		 * symlink vnode then we are in one of three cases:
2746		 *
2747		 * 1) automatic sync has fired.  In this case, we don't want the behavior to change.
2748		 *
2749		 * 2) Someone has opened the FD for the symlink (not what it points to)
2750		 * and has issued an fsync against it.  This should be rare, and we don't
2751		 * want the behavior to change.
2752		 *
2753		 * 3) We are being called by a vclean which is trying to reclaim this
2754		 * symlink vnode.  If this is the case, then allowing this fsync to
2755		 * proceed WITHOUT flushing the journal could result in the vclean
2756		 * invalidating the buffer's blocks before the journal transaction is
2757		 * written to disk.  To prevent this, we force a journal flush
2758		 * if the vnode is in the middle of a recycle (VL_TERMINATE or VL_DEAD is set).
2759		 */
2760		if (vnode_isrecycled(vp)) {
2761			fullsync = 1;
2762		}
2763	}
2764
2765metasync:
2766	if (vnode_isreg(vp) && vnode_issystem(vp)) {
2767		if (VTOF(vp)->fcbBTCBPtr != NULL) {
2768			microuptime(&tv);
2769			BTSetLastSync(VTOF(vp), tv.tv_sec);
2770		}
2771		cp->c_touch_acctime = FALSE;
2772		cp->c_touch_chgtime = FALSE;
2773		cp->c_touch_modtime = FALSE;
2774	} else if ( !(vp->v_flag & VSWAP) ) /* User file */ {
2775		retval = hfs_update(vp, wait);
2776
2777		/*
2778		 * When MNT_WAIT is requested push out the catalog record for
2779		 * this file.  If they asked for a full fsync, we can skip this
2780		 * because the journal_flush or hfs_metasync_all will push out
2781		 * all of the metadata changes.
2782		 */
2783   		if ((retval == 0) && wait && !fullsync && cp->c_hint &&
2784   		    !ISSET(cp->c_flag, C_DELETED | C_NOEXISTS)) {
2785   			hfs_metasync(VTOHFS(vp), (daddr64_t)cp->c_hint, p);
2786		}
2787
2788		/*
2789		 * If this was a full fsync, make sure all metadata
2790		 * changes get to stable storage.
2791		 */
2792		if (fullsync) {
2793			if (hfsmp->jnl) {
2794				hfs_journal_flush(hfsmp, FALSE);
2795
2796				if (journal_uses_fua(hfsmp->jnl)) {
2797					/*
2798					 * the journal_flush did NOT issue a sync track cache command,
2799					 * and the fullsync indicates we are supposed to flush all cached
2800					 * data to the media, so issue the sync track cache command
2801					 * explicitly
2802					 */
2803					VNOP_IOCTL(hfsmp->hfs_devvp, DKIOCSYNCHRONIZECACHE, NULL, FWRITE, NULL);
2804				}
2805			} else {
2806				retval = hfs_metasync_all(hfsmp);
2807				/* XXX need to pass context! */
2808				VNOP_IOCTL(hfsmp->hfs_devvp, DKIOCSYNCHRONIZECACHE, NULL, FWRITE, NULL);
2809			}
2810		}
2811	}
2812
2813	return (retval);
2814}
2815
2816
2817/* Sync an hfs catalog b-tree node */
2818int
2819hfs_metasync(struct hfsmount *hfsmp, daddr64_t node, __unused struct proc *p)
2820{
2821	vnode_t	vp;
2822	buf_t	bp;
2823	int lockflags;
2824
2825	vp = HFSTOVCB(hfsmp)->catalogRefNum;
2826
2827	// XXXdbg - don't need to do this on a journaled volume
2828	if (hfsmp->jnl) {
2829		return 0;
2830	}
2831
2832	lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_EXCLUSIVE_LOCK);
2833	/*
2834	 * Look for a matching node that has been delayed
2835	 * but is not part of a set (B_LOCKED).
2836	 *
2837	 * BLK_ONLYVALID causes buf_getblk to return a
2838	 * buf_t for the daddr64_t specified only if it's
2839	 * currently resident in the cache... the size
2840	 * parameter to buf_getblk is ignored when this flag
2841	 * is set
2842	 */
2843	bp = buf_getblk(vp, node, 0, 0, 0, BLK_META | BLK_ONLYVALID);
2844
2845	if (bp) {
2846	        if ((buf_flags(bp) & (B_LOCKED | B_DELWRI)) == B_DELWRI)
2847		        (void) VNOP_BWRITE(bp);
2848		else
2849		        buf_brelse(bp);
2850	}
2851
2852	hfs_systemfile_unlock(hfsmp, lockflags);
2853
2854	return (0);
2855}
2856
2857
2858/*
2859 * Sync all hfs B-trees.  Use this instead of journal_flush for a volume
2860 * without a journal.  Note that the volume bitmap does not get written;
2861 * we rely on fsck_hfs to fix that up (which it can do without any loss
2862 * of data).
2863 */
2864int
2865hfs_metasync_all(struct hfsmount *hfsmp)
2866{
2867	int lockflags;
2868
2869	/* Lock all of the B-trees so we get a mutually consistent state */
2870	lockflags = hfs_systemfile_lock(hfsmp,
2871		SFL_CATALOG|SFL_EXTENTS|SFL_ATTRIBUTE, HFS_EXCLUSIVE_LOCK);
2872
2873	/* Sync each of the B-trees */
2874	if (hfsmp->hfs_catalog_vp)
2875		hfs_btsync(hfsmp->hfs_catalog_vp, 0);
2876	if (hfsmp->hfs_extents_vp)
2877		hfs_btsync(hfsmp->hfs_extents_vp, 0);
2878	if (hfsmp->hfs_attribute_vp)
2879		hfs_btsync(hfsmp->hfs_attribute_vp, 0);
2880
2881	/* Wait for all of the writes to complete */
2882	if (hfsmp->hfs_catalog_vp)
2883		vnode_waitforwrites(hfsmp->hfs_catalog_vp, 0, 0, 0, "hfs_metasync_all");
2884	if (hfsmp->hfs_extents_vp)
2885		vnode_waitforwrites(hfsmp->hfs_extents_vp, 0, 0, 0, "hfs_metasync_all");
2886	if (hfsmp->hfs_attribute_vp)
2887		vnode_waitforwrites(hfsmp->hfs_attribute_vp, 0, 0, 0, "hfs_metasync_all");
2888
2889	hfs_systemfile_unlock(hfsmp, lockflags);
2890
2891	return 0;
2892}
2893
2894
2895/*ARGSUSED 1*/
2896static int
2897hfs_btsync_callback(struct buf *bp, __unused void *dummy)
2898{
2899	buf_clearflags(bp, B_LOCKED);
2900	(void) buf_bawrite(bp);
2901
2902	return(BUF_CLAIMED);
2903}
2904
2905
2906int
2907hfs_btsync(struct vnode *vp, int sync_transaction)
2908{
2909	struct cnode *cp = VTOC(vp);
2910	struct timeval tv;
2911	int    flags = 0;
2912
2913	if (sync_transaction)
2914	        flags |= BUF_SKIP_NONLOCKED;
2915	/*
2916	 * Flush all dirty buffers associated with b-tree.
2917	 */
2918	buf_iterate(vp, hfs_btsync_callback, flags, 0);
2919
2920	microuptime(&tv);
2921	if (vnode_issystem(vp) && (VTOF(vp)->fcbBTCBPtr != NULL))
2922		(void) BTSetLastSync(VTOF(vp), tv.tv_sec);
2923	cp->c_touch_acctime = FALSE;
2924	cp->c_touch_chgtime = FALSE;
2925	cp->c_touch_modtime = FALSE;
2926
2927	return 0;
2928}
2929
2930/*
2931 * Remove a directory.
2932 */
2933int
2934hfs_vnop_rmdir(ap)
2935	struct vnop_rmdir_args /* {
2936		struct vnode *a_dvp;
2937		struct vnode *a_vp;
2938		struct componentname *a_cnp;
2939		vfs_context_t a_context;
2940	} */ *ap;
2941{
2942	struct vnode *dvp = ap->a_dvp;
2943	struct vnode *vp = ap->a_vp;
2944	struct cnode *dcp = VTOC(dvp);
2945	struct cnode *cp = VTOC(vp);
2946	int error;
2947	time_t orig_ctime;
2948
2949	orig_ctime = VTOC(vp)->c_ctime;
2950
2951	if (!S_ISDIR(cp->c_mode)) {
2952		return (ENOTDIR);
2953	}
2954	if (dvp == vp) {
2955		return (EINVAL);
2956	}
2957
2958	check_for_tracked_file(vp, orig_ctime, NAMESPACE_HANDLER_DELETE_OP, NULL);
2959	cp = VTOC(vp);
2960
2961	if ((error = hfs_lockpair(dcp, cp, HFS_EXCLUSIVE_LOCK))) {
2962		return (error);
2963	}
2964
2965	/* Check for a race with rmdir on the parent directory */
2966	if (dcp->c_flag & (C_DELETED | C_NOEXISTS)) {
2967		hfs_unlockpair (dcp, cp);
2968		return ENOENT;
2969	}
2970
2971	//
2972	// if the item is tracked but doesn't have a document_id, assign one and generate an fsevent for it
2973	//
2974	if ((cp->c_bsdflags & UF_TRACKED) && ((struct FndrExtendedDirInfo *)((char *)&cp->c_attr.ca_finderinfo + 16))->document_id == 0) {
2975		uint32_t newid;
2976
2977		hfs_unlockpair(dcp, cp);
2978
2979		if (hfs_generate_document_id(VTOHFS(vp), &newid) == 0) {
2980			hfs_lockpair(dcp, cp, HFS_EXCLUSIVE_LOCK);
2981			((struct FndrExtendedDirInfo *)((char *)&cp->c_attr.ca_finderinfo + 16))->document_id = newid;
2982#if CONFIG_FSE
2983			add_fsevent(FSE_DOCID_CHANGED, vfs_context_current(),
2984				    FSE_ARG_DEV,   VTOHFS(vp)->hfs_raw_dev,
2985				    FSE_ARG_INO,   (ino64_t)0,             // src inode #
2986				    FSE_ARG_INO,   (ino64_t)cp->c_fileid,  // dst inode #
2987				    FSE_ARG_INT32, newid,
2988				    FSE_ARG_DONE);
2989#endif
2990		} else {
2991			// XXXdbg - couldn't get a new docid... what to do?  can't really fail the rm...
2992			hfs_lockpair(dcp, cp, HFS_EXCLUSIVE_LOCK);
2993		}
2994	}
2995
2996	error = hfs_removedir(dvp, vp, ap->a_cnp, 0, 0);
2997
2998	hfs_unlockpair(dcp, cp);
2999
3000	return (error);
3001}
3002
3003/*
3004 * Remove a directory
3005 *
3006 * Both dvp and vp cnodes are locked
3007 */
3008int
3009hfs_removedir(struct vnode *dvp, struct vnode *vp, struct componentname *cnp,
3010              int skip_reserve, int only_unlink)
3011{
3012	struct cnode *cp;
3013	struct cnode *dcp;
3014	struct hfsmount * hfsmp;
3015	struct cat_desc desc;
3016	int lockflags;
3017	int error = 0, started_tr = 0;
3018
3019	cp = VTOC(vp);
3020	dcp = VTOC(dvp);
3021	hfsmp = VTOHFS(vp);
3022
3023	if (dcp == cp) {
3024		return (EINVAL);	/* cannot remove "." */
3025	}
3026	if (cp->c_flag & (C_NOEXISTS | C_DELETED)) {
3027		return (0);
3028	}
3029	if (cp->c_entries != 0) {
3030		return (ENOTEMPTY);
3031	}
3032
3033	/*
3034	 * If the directory is open or in use (e.g. opendir() or current working
3035	 * directory for some process); wait for inactive/reclaim to actually
3036	 * remove cnode from the catalog.  Both inactive and reclaim codepaths are capable
3037	 * of removing open-unlinked directories from the catalog, as well as getting rid
3038	 * of EAs still on the element.  So change only_unlink to true, so that it will get
3039	 * cleaned up below.
3040	 *
3041	 * Otherwise, we can get into a weird old mess where the directory has C_DELETED,
3042	 * but it really means C_NOEXISTS because the item was actually removed from the
3043	 * catalog.  Then when we try to remove the entry from the catalog later on, it won't
3044	 * really be there anymore.
3045	 */
3046	if (vnode_isinuse(vp, 0))  {
3047		only_unlink = 1;
3048	}
3049
3050	/* Deal with directory hardlinks */
3051	if (cp->c_flag & C_HARDLINK) {
3052		/*
3053		 * Note that if we have a directory which was a hardlink at any point,
3054		 * its actual directory data is stored in the directory inode in the hidden
3055		 * directory rather than the leaf element(s) present in the namespace.
3056		 *
3057		 * If there are still other hardlinks to this directory,
3058		 * then we'll just eliminate this particular link and the vnode will still exist.
3059		 * If this is the last link to an empty directory, then we'll open-unlink the
3060		 * directory and it will be only tagged with C_DELETED (as opposed to C_NOEXISTS).
3061		 *
3062		 * We could also return EBUSY here.
3063		 */
3064
3065		return hfs_unlink(hfsmp, dvp, vp, cnp, skip_reserve);
3066	}
3067
3068	/*
3069	 * In a few cases, we may want to allow the directory to persist in an
3070	 * open-unlinked state.  If the directory is being open-unlinked (still has usecount
3071	 * references), or if it has EAs, or if it was being deleted as part of a rename,
3072	 * then we go ahead and move it to the hidden directory.
3073	 *
3074	 * If the directory is being open-unlinked, then we want to keep the catalog entry
3075	 * alive so that future EA calls and fchmod/fstat etc. do not cause issues later.
3076	 *
3077	 * If the directory had EAs, then we want to use the open-unlink trick so that the
3078	 * EA removal is not done in one giant transaction.  Otherwise, it could cause a panic
3079	 * due to overflowing the journal.
3080	 *
3081	 * Finally, if it was deleted as part of a rename, we move it to the hidden directory
3082	 * in order to maintain rename atomicity.
3083	 *
3084	 * Note that the allow_dirs argument to hfs_removefile specifies that it is
3085	 * supposed to handle directories for this case.
3086     */
3087
3088	if (((hfsmp->hfs_attribute_vp != NULL) &&
3089	    ((cp->c_attr.ca_recflags & kHFSHasAttributesMask) != 0)) ||
3090		(only_unlink != 0)) {
3091
3092		int ret = hfs_removefile(dvp, vp, cnp, 0, 0, 1, NULL, only_unlink);
3093		/*
3094		 * Even though hfs_vnop_rename calls vnode_recycle for us on tvp we call
3095		 * it here just in case we were invoked by rmdir() on a directory that had
3096		 * EAs.  To ensure that we start reclaiming the space as soon as possible,
3097		 * we call vnode_recycle on the directory.
3098		 */
3099		vnode_recycle(vp);
3100
3101		return ret;
3102
3103	}
3104
3105	dcp->c_flag |= C_DIR_MODIFICATION;
3106
3107#if QUOTA
3108	if (hfsmp->hfs_flags & HFS_QUOTAS)
3109		(void)hfs_getinoquota(cp);
3110#endif
3111	if ((error = hfs_start_transaction(hfsmp)) != 0) {
3112	    goto out;
3113	}
3114	started_tr = 1;
3115
3116	/*
3117	 * Verify the directory is empty (and valid).
3118	 * (Rmdir ".." won't be valid since
3119	 *  ".." will contain a reference to
3120	 *  the current directory and thus be
3121	 *  non-empty.)
3122	 */
3123	if ((dcp->c_bsdflags & APPEND) || (cp->c_bsdflags & (IMMUTABLE | APPEND))) {
3124		error = EPERM;
3125		goto out;
3126	}
3127
3128	/* Remove the entry from the namei cache: */
3129	cache_purge(vp);
3130
3131	/*
3132	 * Protect against a race with rename by using the component
3133	 * name passed in and parent id from dvp (instead of using
3134	 * the cp->c_desc which may have changed).
3135	 */
3136	desc.cd_nameptr = (const u_int8_t *)cnp->cn_nameptr;
3137	desc.cd_namelen = cnp->cn_namelen;
3138	desc.cd_parentcnid = dcp->c_fileid;
3139	desc.cd_cnid = cp->c_cnid;
3140	desc.cd_flags = CD_ISDIR;
3141	desc.cd_encoding = cp->c_encoding;
3142	desc.cd_hint = 0;
3143
3144	if (!hfs_valid_cnode(hfsmp, dvp, cnp, cp->c_fileid, NULL, &error)) {
3145	    error = 0;
3146	    goto out;
3147	}
3148
3149	/* Remove entry from catalog */
3150	lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG | SFL_ATTRIBUTE | SFL_BITMAP, HFS_EXCLUSIVE_LOCK);
3151
3152	if (!skip_reserve) {
3153		/*
3154		 * Reserve some space in the Catalog file.
3155		 */
3156		if ((error = cat_preflight(hfsmp, CAT_DELETE, NULL, 0))) {
3157			hfs_systemfile_unlock(hfsmp, lockflags);
3158			goto out;
3159		}
3160	}
3161
3162	error = cat_delete(hfsmp, &desc, &cp->c_attr);
3163
3164	if (!error) {
3165		//
3166		// if skip_reserve == 1 then we're being called from hfs_vnop_rename() and thus
3167		// we don't need to touch the document_id as it's handled by the rename code.
3168		// otherwise it's a normal remove and we need to save the document id in the
3169		// per thread struct and clear it from the cnode.
3170		//
3171		struct  doc_tombstone *ut;
3172		ut = get_uthread_doc_tombstone();
3173		if (!skip_reserve && (cp->c_bsdflags & UF_TRACKED) && should_save_docid_tombstone(ut, vp, cnp)) {
3174
3175			if (ut->t_lastop_document_id) {
3176				clear_tombstone_docid(ut, hfsmp, NULL);
3177			}
3178			save_tombstone(hfsmp, dvp, vp, cnp, 1);
3179
3180		}
3181
3182		/* The parent lost a child */
3183		if (dcp->c_entries > 0)
3184			dcp->c_entries--;
3185		DEC_FOLDERCOUNT(hfsmp, dcp->c_attr);
3186		dcp->c_dirchangecnt++;
3187		{
3188			struct FndrExtendedDirInfo *extinfo = (struct FndrExtendedDirInfo *)((u_int8_t*)dcp->c_finderinfo + 16);
3189			extinfo->write_gen_counter = OSSwapHostToBigInt32(OSSwapBigToHostInt32(extinfo->write_gen_counter) + 1);
3190		}
3191		dcp->c_touch_chgtime = TRUE;
3192		dcp->c_touch_modtime = TRUE;
3193		hfs_touchtimes(hfsmp, cp);
3194		(void) cat_update(hfsmp, &dcp->c_desc, &dcp->c_attr, NULL, NULL);
3195		cp->c_flag &= ~(C_MODIFIED | C_FORCEUPDATE);
3196	}
3197
3198	hfs_systemfile_unlock(hfsmp, lockflags);
3199
3200	if (error)
3201		goto out;
3202
3203#if QUOTA
3204	if (hfsmp->hfs_flags & HFS_QUOTAS)
3205		(void)hfs_chkiq(cp, -1, NOCRED, 0);
3206#endif /* QUOTA */
3207
3208	hfs_volupdate(hfsmp, VOL_RMDIR, (dcp->c_cnid == kHFSRootFolderID));
3209
3210	/* Mark C_NOEXISTS since the catalog entry is now gone */
3211	cp->c_flag |= C_NOEXISTS;
3212out:
3213	dcp->c_flag &= ~C_DIR_MODIFICATION;
3214	wakeup((caddr_t)&dcp->c_flag);
3215
3216	if (started_tr) {
3217	    hfs_end_transaction(hfsmp);
3218	}
3219
3220	return (error);
3221}
3222
3223
3224/*
3225 * Remove a file or link.
3226 */
3227int
3228hfs_vnop_remove(ap)
3229	struct vnop_remove_args /* {
3230		struct vnode *a_dvp;
3231		struct vnode *a_vp;
3232		struct componentname *a_cnp;
3233		int a_flags;
3234		vfs_context_t a_context;
3235	} */ *ap;
3236{
3237	struct vnode *dvp = ap->a_dvp;
3238	struct vnode *vp = ap->a_vp;
3239	struct cnode *dcp = VTOC(dvp);
3240	struct cnode *cp;
3241	struct vnode *rvp = NULL;
3242	int error=0, recycle_rsrc=0;
3243	int recycle_vnode = 0;
3244	uint32_t rsrc_vid = 0;
3245	time_t orig_ctime;
3246
3247	if (dvp == vp) {
3248		return (EINVAL);
3249	}
3250
3251	orig_ctime = VTOC(vp)->c_ctime;
3252	if (!vnode_isnamedstream(vp) && ((ap->a_flags & VNODE_REMOVE_SKIP_NAMESPACE_EVENT) == 0)) {
3253		error = check_for_tracked_file(vp, orig_ctime, NAMESPACE_HANDLER_DELETE_OP, NULL);
3254		if (error) {
3255			// XXXdbg - decide on a policy for handling namespace handler failures!
3256			// for now we just let them proceed.
3257		}
3258	}
3259	error = 0;
3260
3261	cp = VTOC(vp);
3262
3263relock:
3264
3265	hfs_lock_truncate(cp, HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT);
3266
3267	if ((error = hfs_lockpair(dcp, cp, HFS_EXCLUSIVE_LOCK))) {
3268		hfs_unlock_truncate(cp, HFS_LOCK_DEFAULT);
3269		if (rvp) {
3270			vnode_put (rvp);
3271		}
3272		return (error);
3273	}
3274	//
3275	// if the item is tracked but doesn't have a document_id, assign one and generate an fsevent for it
3276	//
3277	if ((cp->c_bsdflags & UF_TRACKED) && ((struct FndrExtendedDirInfo *)((char *)&cp->c_attr.ca_finderinfo + 16))->document_id == 0) {
3278		uint32_t newid;
3279
3280		hfs_unlockpair(dcp, cp);
3281
3282		if (hfs_generate_document_id(VTOHFS(vp), &newid) == 0) {
3283			hfs_lockpair(dcp, cp, HFS_EXCLUSIVE_LOCK);
3284			((struct FndrExtendedDirInfo *)((char *)&cp->c_attr.ca_finderinfo + 16))->document_id = newid;
3285#if CONFIG_FSE
3286			add_fsevent(FSE_DOCID_CHANGED, vfs_context_current(),
3287				    FSE_ARG_DEV,   VTOHFS(vp)->hfs_raw_dev,
3288				    FSE_ARG_INO,   (ino64_t)0,             // src inode #
3289				    FSE_ARG_INO,   (ino64_t)cp->c_fileid,  // dst inode #
3290				    FSE_ARG_INT32, newid,
3291				    FSE_ARG_DONE);
3292#endif
3293		} else {
3294			// XXXdbg - couldn't get a new docid... what to do?  can't really fail the rm...
3295			hfs_lockpair(dcp, cp, HFS_EXCLUSIVE_LOCK);
3296		}
3297	}
3298
3299	/*
3300	 * Lazily respond to determining if there is a valid resource fork
3301	 * vnode attached to 'cp' if it is a regular file or symlink.
3302	 * If the vnode does not exist, then we may proceed without having to
3303	 * create it.
3304	 *
3305	 * If, however, it does exist, then we need to acquire an iocount on the
3306	 * vnode after acquiring its vid.  This ensures that if we have to do I/O
3307	 * against it, it can't get recycled from underneath us in the middle
3308	 * of this call.
3309	 *
3310	 * Note: this function may be invoked for directory hardlinks, so just skip these
3311	 * steps if 'vp' is a directory.
3312	 */
3313
3314	if ((vp->v_type == VLNK) || (vp->v_type == VREG)) {
3315		if ((cp->c_rsrc_vp) && (rvp == NULL)) {
3316			/* We need to acquire the rsrc vnode */
3317			rvp = cp->c_rsrc_vp;
3318			rsrc_vid = vnode_vid (rvp);
3319
3320			/* Unlock everything to acquire iocount on the rsrc vnode */
3321			hfs_unlock_truncate (cp, HFS_LOCK_DEFAULT);
3322			hfs_unlockpair (dcp, cp);
3323			/* Use the vid to maintain identity on rvp */
3324			if (vnode_getwithvid(rvp, rsrc_vid)) {
3325				/*
3326				 * If this fails, then it was recycled or
3327				 * reclaimed in the interim.  Reset fields and
3328				 * start over.
3329				 */
3330				rvp = NULL;
3331				rsrc_vid = 0;
3332			}
3333			goto relock;
3334		}
3335	}
3336
3337	/*
3338	 * Check to see if we raced rmdir for the parent directory
3339	 * hfs_removefile already checks for a race on vp/cp
3340	 */
3341	if (dcp->c_flag & (C_DELETED | C_NOEXISTS)) {
3342		error = ENOENT;
3343		goto rm_done;
3344	}
3345
3346	error = hfs_removefile(dvp, vp, ap->a_cnp, ap->a_flags, 0, 0, NULL, 0);
3347
3348	/*
3349	 * If the remove succeeded in deleting the file, then we may need to mark
3350	 * the resource fork for recycle so that it is reclaimed as quickly
3351	 * as possible.  If it were not recycled quickly, then this resource fork
3352	 * vnode could keep a v_parent reference on the data fork, which prevents it
3353	 * from going through reclaim (by giving it extra usecounts), except in the force-
3354	 * unmount case.
3355	 *
3356	 * However, a caveat:  we need to continue to supply resource fork
3357	 * access to open-unlinked files even if the resource fork is not open.  This is
3358	 * a requirement for the compressed files work.  Luckily, hfs_vgetrsrc will handle
3359	 * this already if the data fork has been re-parented to the hidden directory.
3360	 *
3361	 * As a result, all we really need to do here is mark the resource fork vnode
3362	 * for recycle.  If it goes out of core, it can be brought in again if needed.
3363	 * If the cnode was instead marked C_NOEXISTS, then there wouldn't be any
3364	 * more work.
3365	 */
3366	if (error == 0) {
3367	   	if (rvp) {
3368	    	recycle_rsrc = 1;
3369		}
3370		/*
3371		 * If the target was actually removed from the catalog schedule it for
3372		 * full reclamation/inactivation.  We hold an iocount on it so it should just
3373		 * get marked with MARKTERM
3374		 */
3375		if (cp->c_flag & C_NOEXISTS) {
3376			recycle_vnode = 1;
3377		}
3378	}
3379
3380
3381	/*
3382	 * Drop the truncate lock before unlocking the cnode
3383	 * (which can potentially perform a vnode_put and
3384	 * recycle the vnode which in turn might require the
3385	 * truncate lock)
3386	 */
3387rm_done:
3388	hfs_unlock_truncate(cp, HFS_LOCK_DEFAULT);
3389	hfs_unlockpair(dcp, cp);
3390
3391	if (recycle_rsrc) {
3392		/* inactive or reclaim on rvp will clean up the blocks from the rsrc fork */
3393		vnode_recycle(rvp);
3394	}
3395	if (recycle_vnode) {
3396		vnode_recycle (vp);
3397	}
3398
3399	if (rvp) {
3400		/* drop iocount on rsrc fork, was obtained at beginning of fxn */
3401		vnode_put(rvp);
3402	}
3403
3404	return (error);
3405}
3406
3407
3408int
3409hfs_removefile_callback(struct buf *bp, void *hfsmp) {
3410
3411        if ( !(buf_flags(bp) & B_META))
3412	        panic("hfs: symlink bp @ %p is not marked meta-data!\n", bp);
3413	/*
3414	 * it's part of the current transaction, kill it.
3415	 */
3416	journal_kill_block(((struct hfsmount *)hfsmp)->jnl, bp);
3417
3418	return (BUF_CLAIMED);
3419}
3420
3421/*
3422 * hfs_removefile
3423 *
3424 * Similar to hfs_vnop_remove except there are additional options.
3425 * This function may be used to remove directories if they have
3426 * lots of EA's -- note the 'allow_dirs' argument.
3427 *
3428 * This function is able to delete blocks & fork data for the resource
3429 * fork even if it does not exist in core (and have a backing vnode).
3430 * It should infer the correct behavior based on the number of blocks
3431 * in the cnode and whether or not the resource fork pointer exists or
3432 * not.  As a result, one only need pass in the 'vp' corresponding to the
3433 * data fork of this file (or main vnode in the case of a directory).
3434 * Passing in a resource fork will result in an error.
3435 *
3436 * Because we do not create any vnodes in this function, we are not at
3437 * risk of deadlocking against ourselves by double-locking.
3438 *
3439 * Requires cnode and truncate locks to be held.
3440 */
3441int
3442hfs_removefile(struct vnode *dvp, struct vnode *vp, struct componentname *cnp,
3443               int flags, int skip_reserve, int allow_dirs,
3444			   __unused struct vnode *rvp, int only_unlink)
3445{
3446	struct cnode *cp;
3447	struct cnode *dcp;
3448	struct vnode *rsrc_vp = NULL;
3449	struct hfsmount *hfsmp;
3450	struct cat_desc desc;
3451	struct timeval tv;
3452	int dataforkbusy = 0;
3453	int rsrcforkbusy = 0;
3454	int lockflags;
3455	int error = 0;
3456	int started_tr = 0;
3457	int isbigfile = 0, defer_remove=0, isdir=0;
3458	int update_vh = 0;
3459
3460	cp = VTOC(vp);
3461	dcp = VTOC(dvp);
3462	hfsmp = VTOHFS(vp);
3463
3464	/* Check if we lost a race post lookup. */
3465	if (cp->c_flag & (C_NOEXISTS | C_DELETED)) {
3466		return (0);
3467	}
3468
3469	if (!hfs_valid_cnode(hfsmp, dvp, cnp, cp->c_fileid, NULL, &error)) {
3470	    return 0;
3471	}
3472
3473	/* Make sure a remove is permitted */
3474	if (VNODE_IS_RSRC(vp)) {
3475		return (EPERM);
3476	}
3477	else {
3478		/*
3479		 * We know it's a data fork.
3480		 * Probe the cnode to see if we have a valid resource fork
3481		 * in hand or not.
3482		 */
3483		rsrc_vp = cp->c_rsrc_vp;
3484	}
3485
3486	/* Don't allow deleting the journal or journal_info_block. */
3487	if (hfs_is_journal_file(hfsmp, cp)) {
3488		return (EPERM);
3489	}
3490
3491	/*
3492	 * If removing a symlink, then we need to ensure that the
3493	 * data blocks for the symlink are not still in-flight or pending.
3494	 * If so, we will unlink the symlink here, making its blocks
3495	 * available for re-allocation by a subsequent transaction.  That is OK, but
3496	 * then the I/O for the data blocks could then go out before the journal
3497	 * transaction that created it was flushed, leading to I/O ordering issues.
3498	 */
3499	if (vp->v_type == VLNK) {
3500		/*
3501		 * This will block if the asynchronous journal flush is in progress.
3502		 * If this symlink is not being renamed over and doesn't have any open FDs,
3503		 * then we'll remove it from the journal's bufs below in kill_block.
3504		 */
3505		buf_wait_for_shadow_io (vp, 0);
3506	}
3507
3508	/*
3509	 * Hard links require special handling.
3510	 */
3511	if (cp->c_flag & C_HARDLINK) {
3512		if ((flags & VNODE_REMOVE_NODELETEBUSY) && vnode_isinuse(vp, 0)) {
3513			return (EBUSY);
3514		} else {
3515			/* A directory hard link with a link count of one is
3516			 * treated as a regular directory.  Therefore it should
3517			 * only be removed using rmdir().
3518			 */
3519			if ((vnode_isdir(vp) == 1) && (cp->c_linkcount == 1) &&
3520			    (allow_dirs == 0)) {
3521			    	return (EPERM);
3522			}
3523			return hfs_unlink(hfsmp, dvp, vp, cnp, skip_reserve);
3524		}
3525	}
3526
3527	/* Directories should call hfs_rmdir! (unless they have a lot of attributes) */
3528	if (vnode_isdir(vp)) {
3529		if (allow_dirs == 0)
3530			return (EPERM);  /* POSIX */
3531		isdir = 1;
3532	}
3533	/* Sanity check the parent ids. */
3534	if ((cp->c_parentcnid != hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid) &&
3535	    (cp->c_parentcnid != dcp->c_fileid)) {
3536		return (EINVAL);
3537	}
3538
3539	dcp->c_flag |= C_DIR_MODIFICATION;
3540
3541	// this guy is going away so mark him as such
3542	cp->c_flag |= C_DELETED;
3543
3544
3545	/* Remove our entry from the namei cache. */
3546	cache_purge(vp);
3547
3548	/*
3549	 * If the caller was operating on a file (as opposed to a
3550	 * directory with EAs), then we need to figure out
3551	 * whether or not it has a valid resource fork vnode.
3552	 *
3553	 * If there was a valid resource fork vnode, then we need
3554	 * to use hfs_truncate to eliminate its data.  If there is
3555	 * no vnode, then we hold the cnode lock which would
3556	 * prevent it from being created.  As a result,
3557	 * we can use the data deletion functions which do not
3558	 * require that a cnode/vnode pair exist.
3559	 */
3560
3561	/* Check if this file is being used. */
3562	if (isdir == 0) {
3563		dataforkbusy = vnode_isinuse(vp, 0);
3564		/*
3565		 * At this point, we know that 'vp' points to the
3566		 * a data fork because we checked it up front. And if
3567		 * there is no rsrc fork, rsrc_vp will be NULL.
3568		 */
3569		if (rsrc_vp && (cp->c_blocks - VTOF(vp)->ff_blocks)) {
3570			rsrcforkbusy = vnode_isinuse(rsrc_vp, 0);
3571		}
3572	}
3573
3574	/* Check if we have to break the deletion into multiple pieces. */
3575	if (isdir == 0) {
3576		isbigfile = ((cp->c_datafork->ff_size >= HFS_BIGFILE_SIZE) && overflow_extents(VTOF(vp)));
3577	}
3578
3579	/* Check if the file has xattrs.  If it does we'll have to delete them in
3580	   individual transactions in case there are too many */
3581	if ((hfsmp->hfs_attribute_vp != NULL) &&
3582	    (cp->c_attr.ca_recflags & kHFSHasAttributesMask) != 0) {
3583	    defer_remove = 1;
3584	}
3585
3586	/* If we are explicitly told to only unlink item and move to hidden dir, then do it */
3587	if (only_unlink) {
3588		defer_remove = 1;
3589	}
3590
3591	/*
3592	 * Carbon semantics prohibit deleting busy files.
3593	 * (enforced when VNODE_REMOVE_NODELETEBUSY is requested)
3594	 */
3595	if (dataforkbusy || rsrcforkbusy) {
3596		if ((flags & VNODE_REMOVE_NODELETEBUSY) ||
3597		    (hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid == 0)) {
3598			error = EBUSY;
3599			goto out;
3600		}
3601	}
3602
3603#if QUOTA
3604	if (hfsmp->hfs_flags & HFS_QUOTAS)
3605		(void)hfs_getinoquota(cp);
3606#endif /* QUOTA */
3607
3608	/*
3609	 * Do a ubc_setsize to indicate we need to wipe contents if:
3610	 *  1) item is a regular file.
3611	 *  2) Neither fork is busy AND we are not told to unlink this.
3612	 *
3613	 * We need to check for the defer_remove since it can be set without
3614	 * having a busy data or rsrc fork
3615	 */
3616	if (isdir == 0 && (!dataforkbusy || !rsrcforkbusy) && (defer_remove == 0)) {
3617		/*
3618		 * A ubc_setsize can cause a pagein so defer it
3619		 * until after the cnode lock is dropped.  The
3620		 * cnode lock cannot be dropped/reacquired here
3621		 * since we might already hold the journal lock.
3622		 */
3623		if (!dataforkbusy && cp->c_datafork->ff_blocks && !isbigfile) {
3624			cp->c_flag |= C_NEED_DATA_SETSIZE;
3625		}
3626		if (!rsrcforkbusy && rsrc_vp) {
3627			cp->c_flag |= C_NEED_RSRC_SETSIZE;
3628		}
3629	}
3630
3631	if ((error = hfs_start_transaction(hfsmp)) != 0) {
3632	    goto out;
3633	}
3634	started_tr = 1;
3635
3636	// XXXdbg - if we're journaled, kill any dirty symlink buffers
3637	if (hfsmp->jnl && vnode_islnk(vp) && (defer_remove == 0)) {
3638	        buf_iterate(vp, hfs_removefile_callback, BUF_SKIP_NONLOCKED, (void *)hfsmp);
3639	}
3640
3641	/*
3642	 * Prepare to truncate any non-busy forks.  Busy forks will
3643	 * get truncated when their vnode goes inactive.
3644	 * Note that we will only enter this region if we
3645	 * can avoid creating an open-unlinked file.  If
3646	 * either region is busy, we will have to create an open
3647	 * unlinked file.
3648	 *
3649	 * Since we are deleting the file, we need to stagger the runtime
3650	 * modifications to do things in such a way that a crash won't
3651	 * result in us getting overlapped extents or any other
3652	 * bad inconsistencies.  As such, we call prepare_release_storage
3653	 * which updates the UBC, updates quota information, and releases
3654	 * any loaned blocks that belong to this file.  No actual
3655	 * truncation or bitmap manipulation is done until *AFTER*
3656	 * the catalog record is removed.
3657	 */
3658	if (isdir == 0 && (!dataforkbusy && !rsrcforkbusy) && (only_unlink == 0)) {
3659
3660		if (!dataforkbusy && !isbigfile && cp->c_datafork->ff_blocks != 0) {
3661
3662			error = hfs_prepare_release_storage (hfsmp, vp);
3663			if (error) {
3664				goto out;
3665			}
3666			update_vh = 1;
3667		}
3668
3669		/*
3670		 * If the resource fork vnode does not exist, we can skip this step.
3671		 */
3672		if (!rsrcforkbusy && rsrc_vp) {
3673			error = hfs_prepare_release_storage (hfsmp, rsrc_vp);
3674			if (error) {
3675				goto out;
3676			}
3677			update_vh = 1;
3678		}
3679	}
3680
3681	/*
3682	 * Protect against a race with rename by using the component
3683	 * name passed in and parent id from dvp (instead of using
3684	 * the cp->c_desc which may have changed).   Also, be aware that
3685	 * because we allow directories to be passed in, we need to special case
3686	 * this temporary descriptor in case we were handed a directory.
3687	 */
3688	if (isdir) {
3689		desc.cd_flags = CD_ISDIR;
3690	}
3691	else {
3692		desc.cd_flags = 0;
3693	}
3694	desc.cd_encoding = cp->c_desc.cd_encoding;
3695	desc.cd_nameptr = (const u_int8_t *)cnp->cn_nameptr;
3696	desc.cd_namelen = cnp->cn_namelen;
3697	desc.cd_parentcnid = dcp->c_fileid;
3698	desc.cd_hint = cp->c_desc.cd_hint;
3699	desc.cd_cnid = cp->c_cnid;
3700	microtime(&tv);
3701
3702	/*
3703	 * There are two cases to consider:
3704	 *  1. File/Dir is busy/big/defer_remove ==> move/rename the file/dir
3705	 *  2. File is not in use ==> remove the file
3706	 *
3707	 * We can get a directory in case 1 because it may have had lots of attributes,
3708	 * which need to get removed here.
3709	 */
3710	if (dataforkbusy || rsrcforkbusy || isbigfile || defer_remove) {
3711		char delname[32];
3712		struct cat_desc to_desc;
3713		struct cat_desc todir_desc;
3714
3715		/*
3716		 * Orphan this file or directory (move to hidden directory).
3717		 * Again, we need to take care that we treat directories as directories,
3718		 * and files as files.  Because directories with attributes can be passed in
3719		 * check to make sure that we have a directory or a file before filling in the
3720		 * temporary descriptor's flags.  We keep orphaned directories AND files in
3721		 * the FILE_HARDLINKS private directory since we're generalizing over all
3722		 * orphaned filesystem objects.
3723		 */
3724		bzero(&todir_desc, sizeof(todir_desc));
3725		todir_desc.cd_parentcnid = 2;
3726
3727		MAKE_DELETED_NAME(delname, sizeof(delname), cp->c_fileid);
3728		bzero(&to_desc, sizeof(to_desc));
3729		to_desc.cd_nameptr = (const u_int8_t *)delname;
3730		to_desc.cd_namelen = strlen(delname);
3731		to_desc.cd_parentcnid = hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid;
3732		if (isdir) {
3733			to_desc.cd_flags = CD_ISDIR;
3734		}
3735		else {
3736			to_desc.cd_flags = 0;
3737		}
3738		to_desc.cd_cnid = cp->c_cnid;
3739
3740		lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_EXCLUSIVE_LOCK);
3741		if (!skip_reserve) {
3742			if ((error = cat_preflight(hfsmp, CAT_RENAME, NULL, 0))) {
3743				hfs_systemfile_unlock(hfsmp, lockflags);
3744				goto out;
3745			}
3746		}
3747
3748		error = cat_rename(hfsmp, &desc, &todir_desc,
3749				&to_desc, (struct cat_desc *)NULL);
3750
3751		if (error == 0) {
3752			hfsmp->hfs_private_attr[FILE_HARDLINKS].ca_entries++;
3753			if (isdir == 1) {
3754				INC_FOLDERCOUNT(hfsmp, hfsmp->hfs_private_attr[FILE_HARDLINKS]);
3755			}
3756			(void) cat_update(hfsmp, &hfsmp->hfs_private_desc[FILE_HARDLINKS],
3757			                  &hfsmp->hfs_private_attr[FILE_HARDLINKS], NULL, NULL);
3758
3759			/* Update the parent directory */
3760			if (dcp->c_entries > 0)
3761				dcp->c_entries--;
3762			if (isdir == 1) {
3763				DEC_FOLDERCOUNT(hfsmp, dcp->c_attr);
3764			}
3765			dcp->c_dirchangecnt++;
3766			{
3767				struct FndrExtendedDirInfo *extinfo = (struct FndrExtendedDirInfo *)((u_int8_t*)dcp->c_finderinfo + 16);
3768				extinfo->write_gen_counter = OSSwapHostToBigInt32(OSSwapBigToHostInt32(extinfo->write_gen_counter) + 1);
3769			}
3770			dcp->c_ctime = tv.tv_sec;
3771			dcp->c_mtime = tv.tv_sec;
3772			(void) cat_update(hfsmp, &dcp->c_desc, &dcp->c_attr, NULL, NULL);
3773
3774			/* Update the file or directory's state */
3775			cp->c_flag |= C_DELETED;
3776			cp->c_ctime = tv.tv_sec;
3777			--cp->c_linkcount;
3778			(void) cat_update(hfsmp, &to_desc, &cp->c_attr, NULL, NULL);
3779		}
3780		hfs_systemfile_unlock(hfsmp, lockflags);
3781		if (error)
3782			goto out;
3783
3784	}
3785	else {
3786		/*
3787		 * Nobody is using this item; we can safely remove everything.
3788		 */
3789		struct filefork *temp_rsrc_fork = NULL;
3790#if QUOTA
3791		off_t savedbytes;
3792		int blksize = hfsmp->blockSize;
3793#endif
3794		u_int32_t fileid = cp->c_fileid;
3795
3796		/*
3797		 * Figure out if we need to read the resource fork data into
3798		 * core before wiping out the catalog record.
3799		 *
3800		 * 1) Must not be a directory
3801		 * 2) cnode's c_rsrcfork ptr must be NULL.
3802		 * 3) rsrc fork must have actual blocks
3803		 */
3804		if ((isdir == 0) && (cp->c_rsrcfork == NULL) &&
3805				(cp->c_blocks - VTOF(vp)->ff_blocks)) {
3806			/*
3807			 * The resource fork vnode & filefork did not exist.
3808			 * Create a temporary one for use in this function only.
3809			 */
3810			MALLOC_ZONE (temp_rsrc_fork, struct filefork *, sizeof (struct filefork), M_HFSFORK, M_WAITOK);
3811			bzero(temp_rsrc_fork, sizeof(struct filefork));
3812			temp_rsrc_fork->ff_cp = cp;
3813			rl_init(&temp_rsrc_fork->ff_invalidranges);
3814		}
3815
3816		lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG | SFL_ATTRIBUTE | SFL_BITMAP, HFS_EXCLUSIVE_LOCK);
3817
3818		/* Look up the resource fork first, if necessary */
3819		if (temp_rsrc_fork) {
3820			error = cat_lookup (hfsmp, &desc, 1, 0, (struct cat_desc*) NULL,
3821					(struct cat_attr*) NULL, &temp_rsrc_fork->ff_data, NULL);
3822			if (error) {
3823				FREE_ZONE (temp_rsrc_fork, sizeof(struct filefork), M_HFSFORK);
3824				hfs_systemfile_unlock (hfsmp, lockflags);
3825				goto out;
3826			}
3827		}
3828
3829		if (!skip_reserve) {
3830			if ((error = cat_preflight(hfsmp, CAT_DELETE, NULL, 0))) {
3831				if (temp_rsrc_fork) {
3832					FREE_ZONE (temp_rsrc_fork, sizeof(struct filefork), M_HFSFORK);
3833				}
3834				hfs_systemfile_unlock(hfsmp, lockflags);
3835				goto out;
3836			}
3837		}
3838
3839		error = cat_delete(hfsmp, &desc, &cp->c_attr);
3840
3841		if (error && error != ENXIO && error != ENOENT) {
3842			printf("hfs_removefile: deleting file %s (id=%d) vol=%s err=%d\n",
3843				   cp->c_desc.cd_nameptr, cp->c_attr.ca_fileid, hfsmp->vcbVN, error);
3844		}
3845
3846		if (error == 0) {
3847			/* Update the parent directory */
3848			if (dcp->c_entries > 0)
3849				dcp->c_entries--;
3850			dcp->c_dirchangecnt++;
3851			{
3852				struct FndrExtendedDirInfo *extinfo = (struct FndrExtendedDirInfo *)((u_int8_t*)dcp->c_finderinfo + 16);
3853				extinfo->write_gen_counter = OSSwapHostToBigInt32(OSSwapBigToHostInt32(extinfo->write_gen_counter) + 1);
3854			}
3855			dcp->c_ctime = tv.tv_sec;
3856			dcp->c_mtime = tv.tv_sec;
3857			(void) cat_update(hfsmp, &dcp->c_desc, &dcp->c_attr, NULL, NULL);
3858		}
3859		hfs_systemfile_unlock(hfsmp, lockflags);
3860
3861		if (error) {
3862			if (temp_rsrc_fork) {
3863				FREE_ZONE (temp_rsrc_fork, sizeof(struct filefork), M_HFSFORK);
3864			}
3865			goto out;
3866		}
3867
3868		/*
3869		 * Now that we've wiped out the catalog record, the file effectively doesn't
3870		 * exist anymore. So update the quota records to reflect the loss of the
3871		 * data fork and the resource fork.
3872		 */
3873#if QUOTA
3874		if (cp->c_datafork->ff_blocks > 0) {
3875			savedbytes = ((off_t)cp->c_datafork->ff_blocks * (off_t)blksize);
3876			(void) hfs_chkdq(cp, (int64_t)-(savedbytes), NOCRED, 0);
3877		}
3878
3879		/*
3880		 * We may have just deleted the catalog record for a resource fork even
3881		 * though it did not exist in core as a vnode. However, just because there
3882		 * was a resource fork pointer in the cnode does not mean that it had any blocks.
3883		 */
3884		if (temp_rsrc_fork || cp->c_rsrcfork) {
3885			if (cp->c_rsrcfork) {
3886			   	if (cp->c_rsrcfork->ff_blocks > 0) {
3887					savedbytes = ((off_t)cp->c_rsrcfork->ff_blocks * (off_t)blksize);
3888					(void) hfs_chkdq(cp, (int64_t)-(savedbytes), NOCRED, 0);
3889				}
3890			}
3891			else {
3892				/* we must have used a temporary fork */
3893				savedbytes = ((off_t)temp_rsrc_fork->ff_blocks * (off_t)blksize);
3894				(void) hfs_chkdq(cp, (int64_t)-(savedbytes), NOCRED, 0);
3895			}
3896		}
3897
3898		if (hfsmp->hfs_flags & HFS_QUOTAS) {
3899			(void)hfs_chkiq(cp, -1, NOCRED, 0);
3900		}
3901#endif
3902
3903		/*
3904		 * If we didn't get any errors deleting the catalog entry, then go ahead
3905		 * and release the backing store now.  The filefork pointers are still valid.
3906		 */
3907		if (temp_rsrc_fork) {
3908			error = hfs_release_storage (hfsmp, cp->c_datafork, temp_rsrc_fork, fileid);
3909		}
3910		else {
3911			/* if cp->c_rsrcfork == NULL, hfs_release_storage will skip over it. */
3912			error = hfs_release_storage (hfsmp, cp->c_datafork, cp->c_rsrcfork, fileid);
3913		}
3914		if (error) {
3915			/*
3916			 * If we encountered an error updating the extents and bitmap,
3917			 * mark the volume inconsistent.  At this point, the catalog record has
3918			 * already been deleted, so we can't recover it at this point. We need
3919			 * to proceed and update the volume header and mark the cnode C_NOEXISTS.
3920			 * The subsequent fsck should be able to recover the free space for us.
3921			 */
3922			hfs_mark_volume_inconsistent(hfsmp);
3923		}
3924		else {
3925			/* reset update_vh to 0, since hfs_release_storage should have done it for us */
3926			update_vh = 0;
3927		}
3928
3929		/* Get rid of the temporary rsrc fork */
3930		if (temp_rsrc_fork) {
3931			FREE_ZONE (temp_rsrc_fork, sizeof(struct filefork), M_HFSFORK);
3932		}
3933
3934		cp->c_flag |= C_NOEXISTS;
3935		cp->c_flag &= ~C_DELETED;
3936
3937		cp->c_touch_chgtime = TRUE;   /* XXX needed ? */
3938		--cp->c_linkcount;
3939
3940		/*
3941		 * We must never get a directory if we're in this else block.  We could
3942		 * accidentally drop the number of files in the volume header if we did.
3943		 */
3944		hfs_volupdate(hfsmp, VOL_RMFILE, (dcp->c_cnid == kHFSRootFolderID));
3945
3946	}
3947
3948	//
3949	// if skip_reserve == 1 then we're being called from hfs_vnop_rename() and thus
3950	// we don't need to touch the document_id as it's handled by the rename code.
3951	// otherwise it's a normal remove and we need to save the document id in the
3952	// per thread struct and clear it from the cnode.
3953	//
3954	struct  doc_tombstone *ut;
3955	ut = get_uthread_doc_tombstone();
3956	if (!error && !skip_reserve && (cp->c_bsdflags & UF_TRACKED) && should_save_docid_tombstone(ut, vp, cnp)) {
3957
3958		if (ut->t_lastop_document_id) {
3959			clear_tombstone_docid(ut, hfsmp, NULL);
3960		}
3961		save_tombstone(hfsmp, dvp, vp, cnp, 1);
3962
3963	}
3964
3965
3966	/*
3967	 * All done with this cnode's descriptor...
3968	 *
3969	 * Note: all future catalog calls for this cnode must be by
3970	 * fileid only.  This is OK for HFS (which doesn't have file
3971	 * thread records) since HFS doesn't support the removal of
3972	 * busy files.
3973	 */
3974	cat_releasedesc(&cp->c_desc);
3975
3976out:
3977	if (error) {
3978	    cp->c_flag &= ~C_DELETED;
3979	}
3980
3981	if (update_vh) {
3982		/*
3983		 * If we bailed out earlier, we may need to update the volume header
3984		 * to deal with the borrowed blocks accounting.
3985		 */
3986		hfs_volupdate (hfsmp, VOL_UPDATE, 0);
3987	}
3988
3989	if (started_tr) {
3990	    hfs_end_transaction(hfsmp);
3991	}
3992
3993	dcp->c_flag &= ~C_DIR_MODIFICATION;
3994	wakeup((caddr_t)&dcp->c_flag);
3995
3996	return (error);
3997}
3998
3999
4000__private_extern__ void
4001replace_desc(struct cnode *cp, struct cat_desc *cdp)
4002{
4003	// fixes 4348457 and 4463138
4004	if (&cp->c_desc == cdp) {
4005	    return;
4006	}
4007
4008	/* First release allocated name buffer */
4009	if (cp->c_desc.cd_flags & CD_HASBUF && cp->c_desc.cd_nameptr != 0) {
4010		const u_int8_t *name = cp->c_desc.cd_nameptr;
4011
4012		cp->c_desc.cd_nameptr = 0;
4013		cp->c_desc.cd_namelen = 0;
4014		cp->c_desc.cd_flags &= ~CD_HASBUF;
4015		vfs_removename((const char *)name);
4016	}
4017	bcopy(cdp, &cp->c_desc, sizeof(cp->c_desc));
4018
4019	/* Cnode now owns the name buffer */
4020	cdp->cd_nameptr = 0;
4021	cdp->cd_namelen = 0;
4022	cdp->cd_flags &= ~CD_HASBUF;
4023}
4024
4025
4026/*
4027 * Rename a cnode.
4028 *
4029 * The VFS layer guarantees that:
4030 *   - source and destination will either both be directories, or
4031 *     both not be directories.
4032 *   - all the vnodes are from the same file system
4033 *
4034 * When the target is a directory, HFS must ensure that its empty.
4035 *
4036 * Note that this function requires up to 6 vnodes in order to work properly
4037 * if it is operating on files (and not on directories).  This is because only
4038 * files can have resource forks, and we now require iocounts to be held on the
4039 * vnodes corresponding to the resource forks (if applicable) as well as
4040 * the files or directories undergoing rename.  The problem with not holding
4041 * iocounts on the resource fork vnodes is that it can lead to a deadlock
4042 * situation: The rsrc fork of the source file may be recycled and reclaimed
4043 * in order to provide a vnode for the destination file's rsrc fork.  Since
4044 * data and rsrc forks share the same cnode, we'd eventually try to lock the
4045 * source file's cnode in order to sync its rsrc fork to disk, but it's already
4046 * been locked.  By taking the rsrc fork vnodes up front we ensure that they
4047 * cannot be recycled, and that the situation mentioned above cannot happen.
4048 */
4049int
4050hfs_vnop_rename(ap)
4051	struct vnop_rename_args  /* {
4052		struct vnode *a_fdvp;
4053		struct vnode *a_fvp;
4054		struct componentname *a_fcnp;
4055		struct vnode *a_tdvp;
4056		struct vnode *a_tvp;
4057		struct componentname *a_tcnp;
4058		vfs_context_t a_context;
4059	} */ *ap;
4060{
4061	struct vnode *tvp = ap->a_tvp;
4062	struct vnode *tdvp = ap->a_tdvp;
4063	struct vnode *fvp = ap->a_fvp;
4064	struct vnode *fdvp = ap->a_fdvp;
4065	/*
4066	 * Note that we only need locals for the target/destination's
4067 	 * resource fork vnode (and only if necessary).  We don't care if the
4068	 * source has a resource fork vnode or not.
4069	 */
4070	struct vnode *tvp_rsrc = NULLVP;
4071	uint32_t tvp_rsrc_vid = 0;
4072	struct componentname *tcnp = ap->a_tcnp;
4073	struct componentname *fcnp = ap->a_fcnp;
4074	struct proc *p = vfs_context_proc(ap->a_context);
4075	struct cnode *fcp;
4076	struct cnode *fdcp;
4077	struct cnode *tdcp;
4078	struct cnode *tcp;
4079	struct cnode *error_cnode;
4080	struct cat_desc from_desc;
4081	struct cat_desc to_desc;
4082	struct cat_desc out_desc;
4083	struct hfsmount *hfsmp;
4084	cat_cookie_t cookie;
4085	int tvp_deleted = 0;
4086	int started_tr = 0, got_cookie = 0;
4087	int took_trunc_lock = 0;
4088	int lockflags;
4089	int error;
4090	time_t orig_from_ctime, orig_to_ctime;
4091	int emit_rename = 1;
4092	int emit_delete = 1;
4093	int is_tracked = 0;
4094	int unlocked;
4095
4096	orig_from_ctime = VTOC(fvp)->c_ctime;
4097	if (tvp && VTOC(tvp)) {
4098		orig_to_ctime = VTOC(tvp)->c_ctime;
4099	} else {
4100		orig_to_ctime = ~0;
4101	}
4102
4103	hfsmp = VTOHFS(tdvp);
4104	/*
4105	 * Do special case checks here.  If fvp == tvp then we need to check the
4106	 * cnode with locks held.
4107	 */
4108	if (fvp == tvp) {
4109		int is_hardlink = 0;
4110		/*
4111		 * In this case, we do *NOT* ever emit a DELETE event.
4112		 * We may not necessarily emit a RENAME event
4113		 */
4114		emit_delete = 0;
4115		if ((error = hfs_lock(VTOC(fvp), HFS_SHARED_LOCK, HFS_LOCK_DEFAULT))) {
4116			return error;
4117		}
4118		/* Check to see if the item is a hardlink or not */
4119		is_hardlink = (VTOC(fvp)->c_flag & C_HARDLINK);
4120		hfs_unlock (VTOC(fvp));
4121
4122		/*
4123		 * If the item is not a hardlink, then case sensitivity must be off, otherwise
4124		 * two names should not resolve to the same cnode unless they were case variants.
4125		 */
4126		if (is_hardlink) {
4127			emit_rename = 0;
4128			/*
4129			 * Hardlinks are a little trickier.  We only want to emit a rename event
4130			 * if the item is a hardlink, the parent directories are the same, case sensitivity
4131			 * is off, and the case folded names are the same.  See the fvp == tvp case below for more
4132			 * info.
4133			 */
4134
4135			if ((fdvp == tdvp) && ((hfsmp->hfs_flags & HFS_CASE_SENSITIVE) == 0)) {
4136				if (hfs_namecmp((const u_int8_t *)fcnp->cn_nameptr, fcnp->cn_namelen,
4137							(const u_int8_t *)tcnp->cn_nameptr, tcnp->cn_namelen) == 0) {
4138					/* Then in this case only it is ok to emit a rename */
4139					emit_rename = 1;
4140				}
4141			}
4142		}
4143	}
4144	if (emit_rename) {
4145		/* c_bsdflags should only be assessed while holding the cnode lock.
4146		 * This is not done consistently throughout the code and can result
4147		 * in race.  This will be fixed via rdar://12181064
4148		 */
4149		if (VTOC(fvp)->c_bsdflags & UF_TRACKED) {
4150			is_tracked = 1;
4151		}
4152		check_for_tracked_file(fvp, orig_from_ctime, NAMESPACE_HANDLER_RENAME_OP, NULL);
4153	}
4154
4155	if (tvp && VTOC(tvp)) {
4156		if (emit_delete) {
4157			check_for_tracked_file(tvp, orig_to_ctime, NAMESPACE_HANDLER_DELETE_OP, NULL);
4158		}
4159	}
4160
4161retry:
4162	/* When tvp exists, take the truncate lock for hfs_removefile(). */
4163	if (tvp && (vnode_isreg(tvp) || vnode_islnk(tvp))) {
4164		hfs_lock_truncate(VTOC(tvp), HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT);
4165		took_trunc_lock = 1;
4166	}
4167
4168relock:
4169	error = hfs_lockfour(VTOC(fdvp), VTOC(fvp), VTOC(tdvp), tvp ? VTOC(tvp) : NULL,
4170	                     HFS_EXCLUSIVE_LOCK, &error_cnode);
4171	if (error) {
4172		if (took_trunc_lock) {
4173			hfs_unlock_truncate(VTOC(tvp), HFS_LOCK_DEFAULT);
4174			took_trunc_lock = 0;
4175		}
4176
4177		/*
4178		 * We hit an error path.  If we were trying to re-acquire the locks
4179		 * after coming through here once, we might have already obtained
4180		 * an iocount on tvp's resource fork vnode.  Drop that before dealing
4181		 * with the failure.  Note this is safe -- since we are in an
4182		 * error handling path, we can't be holding the cnode locks.
4183		 */
4184		if (tvp_rsrc) {
4185			vnode_put (tvp_rsrc);
4186			tvp_rsrc_vid = 0;
4187			tvp_rsrc = NULL;
4188		}
4189
4190		/*
4191		 * tvp might no longer exist.  If the cause of the lock failure
4192		 * was tvp, then we can try again with tvp/tcp set to NULL.
4193		 * This is ok because the vfs syscall will vnode_put the vnodes
4194		 * after we return from hfs_vnop_rename.
4195		 */
4196		if ((error == ENOENT) && (tvp != NULL) && (error_cnode == VTOC(tvp))) {
4197			tcp = NULL;
4198			tvp = NULL;
4199			goto retry;
4200		}
4201
4202		if (emit_rename && is_tracked) {
4203			resolve_nspace_item(fvp, NAMESPACE_HANDLER_RENAME_FAILED_OP | NAMESPACE_HANDLER_TRACK_EVENT);
4204		}
4205
4206		return (error);
4207	}
4208
4209	fdcp = VTOC(fdvp);
4210	fcp = VTOC(fvp);
4211	tdcp = VTOC(tdvp);
4212	tcp = tvp ? VTOC(tvp) : NULL;
4213
4214	//
4215	// if the item is tracked but doesn't have a document_id, assign one and generate an fsevent for it
4216	//
4217	unlocked = 0;
4218	if ((fcp->c_bsdflags & UF_TRACKED) && ((struct FndrExtendedDirInfo *)((char *)&fcp->c_attr.ca_finderinfo + 16))->document_id == 0) {
4219		uint32_t newid;
4220
4221		hfs_unlockfour(VTOC(fdvp), VTOC(fvp), VTOC(tdvp), tvp ? VTOC(tvp) : NULL);
4222		unlocked = 1;
4223
4224		if (hfs_generate_document_id(hfsmp, &newid) == 0) {
4225			hfs_lock(fcp, HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT);
4226			((struct FndrExtendedDirInfo *)((char *)&fcp->c_attr.ca_finderinfo + 16))->document_id = newid;
4227#if CONFIG_FSE
4228			add_fsevent(FSE_DOCID_CHANGED, vfs_context_current(),
4229				    FSE_ARG_DEV,   hfsmp->hfs_raw_dev,
4230				    FSE_ARG_INO,   (ino64_t)0,             // src inode #
4231				    FSE_ARG_INO,   (ino64_t)fcp->c_fileid,  // dst inode #
4232				    FSE_ARG_INT32, newid,
4233				    FSE_ARG_DONE);
4234#endif
4235			hfs_unlock(fcp);
4236		} else {
4237			// XXXdbg - couldn't get a new docid... what to do?  can't really fail the rename...
4238		}
4239
4240		//
4241		// check if we're going to need to fix tcp as well.  if we aren't, go back relock
4242		// everything.  otherwise continue on and fix up tcp as well before relocking.
4243		//
4244		if (tcp == NULL || !(tcp->c_bsdflags & UF_TRACKED) || ((struct FndrExtendedDirInfo *)((char *)&tcp->c_attr.ca_finderinfo + 16))->document_id != 0) {
4245			goto relock;
4246		}
4247	}
4248
4249	//
4250	// same thing for tcp if it's set
4251	//
4252	if (tcp && (tcp->c_bsdflags & UF_TRACKED) && ((struct FndrExtendedDirInfo *)((char *)&tcp->c_attr.ca_finderinfo + 16))->document_id == 0) {
4253		uint32_t newid;
4254
4255		if (!unlocked) {
4256			hfs_unlockfour(VTOC(fdvp), VTOC(fvp), VTOC(tdvp), tvp ? VTOC(tvp) : NULL);
4257			unlocked = 1;
4258		}
4259
4260		if (hfs_generate_document_id(hfsmp, &newid) == 0) {
4261			hfs_lock(tcp, HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT);
4262			((struct FndrExtendedDirInfo *)((char *)&tcp->c_attr.ca_finderinfo + 16))->document_id = newid;
4263#if CONFIG_FSE
4264			add_fsevent(FSE_DOCID_CHANGED, vfs_context_current(),
4265				    FSE_ARG_DEV,   hfsmp->hfs_raw_dev,
4266				    FSE_ARG_INO,   (ino64_t)0,             // src inode #
4267				    FSE_ARG_INO,   (ino64_t)tcp->c_fileid,  // dst inode #
4268				    FSE_ARG_INT32, newid,
4269				    FSE_ARG_DONE);
4270#endif
4271			hfs_unlock(tcp);
4272		} else {
4273			// XXXdbg - couldn't get a new docid... what to do?  can't really fail the rename...
4274		}
4275
4276		// go back up and relock everything.  next time through the if statement won't be true
4277		// and we'll skip over this block of code.
4278		goto relock;
4279	}
4280
4281
4282
4283	/*
4284	 * Acquire iocounts on the destination's resource fork vnode
4285	 * if necessary. If dst/src are files and the dst has a resource
4286	 * fork vnode, then we need to try and acquire an iocount on the rsrc vnode.
4287	 * If it does not exist, then we don't care and can skip it.
4288	 */
4289	if ((vnode_isreg(fvp)) || (vnode_islnk(fvp))) {
4290		if ((tvp) && (tcp->c_rsrc_vp) && (tvp_rsrc == NULL)) {
4291			tvp_rsrc = tcp->c_rsrc_vp;
4292			/*
4293			 * We can look at the vid here because we're holding the
4294			 * cnode lock on the underlying cnode for this rsrc vnode.
4295			 */
4296			tvp_rsrc_vid = vnode_vid (tvp_rsrc);
4297
4298			/* Unlock everything to acquire iocount on this rsrc vnode */
4299			if (took_trunc_lock) {
4300				hfs_unlock_truncate (VTOC(tvp), HFS_LOCK_DEFAULT);
4301				took_trunc_lock = 0;
4302			}
4303			hfs_unlockfour(fdcp, fcp, tdcp, tcp);
4304
4305			if (vnode_getwithvid (tvp_rsrc, tvp_rsrc_vid)) {
4306				/* iocount acquisition failed.  Reset fields and start over.. */
4307				tvp_rsrc_vid = 0;
4308				tvp_rsrc = NULL;
4309			}
4310			goto retry;
4311		}
4312	}
4313
4314
4315
4316	/* Ensure we didn't race src or dst parent directories with rmdir. */
4317	if (fdcp->c_flag & (C_NOEXISTS | C_DELETED)) {
4318		error = ENOENT;
4319		goto out;
4320	}
4321
4322	if (tdcp->c_flag & (C_NOEXISTS | C_DELETED)) {
4323		error = ENOENT;
4324		goto out;
4325	}
4326
4327
4328	/* Check for a race against unlink.  The hfs_valid_cnode checks validate
4329	 * the parent/child relationship with fdcp and tdcp, as well as the
4330	 * component name of the target cnodes.
4331	 */
4332	if ((fcp->c_flag & (C_NOEXISTS | C_DELETED)) || !hfs_valid_cnode(hfsmp, fdvp, fcnp, fcp->c_fileid, NULL, &error)) {
4333		error = ENOENT;
4334		goto out;
4335	}
4336
4337	if (tcp && ((tcp->c_flag & (C_NOEXISTS | C_DELETED)) || !hfs_valid_cnode(hfsmp, tdvp, tcnp, tcp->c_fileid, NULL, &error))) {
4338	    //
4339	    // hmm, the destination vnode isn't valid any more.
4340	    // in this case we can just drop him and pretend he
4341	    // never existed in the first place.
4342	    //
4343	    if (took_trunc_lock) {
4344			hfs_unlock_truncate(VTOC(tvp), HFS_LOCK_DEFAULT);
4345			took_trunc_lock = 0;
4346	    }
4347		error = 0;
4348
4349	    hfs_unlockfour(fdcp, fcp, tdcp, tcp);
4350
4351	    tcp = NULL;
4352	    tvp = NULL;
4353
4354	    // retry the locking with tvp null'ed out
4355	    goto retry;
4356	}
4357
4358	fdcp->c_flag |= C_DIR_MODIFICATION;
4359	if (fdvp != tdvp) {
4360	    tdcp->c_flag |= C_DIR_MODIFICATION;
4361	}
4362
4363	/*
4364	 * Disallow renaming of a directory hard link if the source and
4365	 * destination parent directories are different, or a directory whose
4366	 * descendant is a directory hard link and the one of the ancestors
4367	 * of the destination directory is a directory hard link.
4368	 */
4369	if (vnode_isdir(fvp) && (fdvp != tdvp)) {
4370		if (fcp->c_flag & C_HARDLINK) {
4371			error = EPERM;
4372			goto out;
4373		}
4374		if (fcp->c_attr.ca_recflags & kHFSHasChildLinkMask) {
4375		    lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_SHARED_LOCK);
4376		    if (cat_check_link_ancestry(hfsmp, tdcp->c_fileid, 0)) {
4377				error = EPERM;
4378				hfs_systemfile_unlock(hfsmp, lockflags);
4379				goto out;
4380			}
4381			hfs_systemfile_unlock(hfsmp, lockflags);
4382		}
4383	}
4384
4385	/*
4386	 * The following edge case is caught here:
4387	 * (to cannot be a descendent of from)
4388	 *
4389	 *       o fdvp
4390	 *      /
4391	 *     /
4392	 *    o fvp
4393	 *     \
4394	 *      \
4395	 *       o tdvp
4396	 *      /
4397	 *     /
4398	 *    o tvp
4399	 */
4400	if (tdcp->c_parentcnid == fcp->c_fileid) {
4401		error = EINVAL;
4402		goto out;
4403	}
4404
4405	/*
4406	 * The following two edge cases are caught here:
4407	 * (note tvp is not empty)
4408	 *
4409	 *       o tdvp               o tdvp
4410	 *      /                    /
4411	 *     /                    /
4412	 *    o tvp            tvp o fdvp
4413	 *     \                    \
4414	 *      \                    \
4415	 *       o fdvp               o fvp
4416	 *      /
4417	 *     /
4418	 *    o fvp
4419	 */
4420	if (tvp && vnode_isdir(tvp) && (tcp->c_entries != 0) && fvp != tvp) {
4421		error = ENOTEMPTY;
4422		goto out;
4423	}
4424
4425	/*
4426	 * The following edge case is caught here:
4427	 * (the from child and parent are the same)
4428	 *
4429	 *          o tdvp
4430	 *         /
4431	 *        /
4432	 *  fdvp o fvp
4433	 */
4434	if (fdvp == fvp) {
4435		error = EINVAL;
4436		goto out;
4437	}
4438
4439	/*
4440	 * Make sure "from" vnode and its parent are changeable.
4441	 */
4442	if ((fcp->c_bsdflags & (IMMUTABLE | APPEND)) || (fdcp->c_bsdflags & APPEND)) {
4443		error = EPERM;
4444		goto out;
4445	}
4446
4447	/*
4448	 * If the destination parent directory is "sticky", then the
4449	 * user must own the parent directory, or the destination of
4450	 * the rename, otherwise the destination may not be changed
4451	 * (except by root). This implements append-only directories.
4452	 *
4453	 * Note that checks for immutable and write access are done
4454	 * by the call to hfs_removefile.
4455	 */
4456	if (tvp && (tdcp->c_mode & S_ISTXT) &&
4457	    (suser(vfs_context_ucred(tcnp->cn_context), NULL)) &&
4458	    (kauth_cred_getuid(vfs_context_ucred(tcnp->cn_context)) != tdcp->c_uid) &&
4459	    (hfs_owner_rights(hfsmp, tcp->c_uid, vfs_context_ucred(tcnp->cn_context), p, false)) ) {
4460		error = EPERM;
4461		goto out;
4462	}
4463
4464	/* Don't allow modification of the journal or journal_info_block */
4465	if (hfs_is_journal_file(hfsmp, fcp) ||
4466	    (tcp && hfs_is_journal_file(hfsmp, tcp))) {
4467		error = EPERM;
4468		goto out;
4469	}
4470
4471#if QUOTA
4472	if (tvp)
4473		(void)hfs_getinoquota(tcp);
4474#endif
4475	/* Preflighting done, take fvp out of the name space. */
4476	cache_purge(fvp);
4477
4478	bzero(&from_desc, sizeof(from_desc));
4479	from_desc.cd_nameptr = (const u_int8_t *)fcnp->cn_nameptr;
4480	from_desc.cd_namelen = fcnp->cn_namelen;
4481	from_desc.cd_parentcnid = fdcp->c_fileid;
4482	from_desc.cd_flags = fcp->c_desc.cd_flags & ~(CD_HASBUF | CD_DECOMPOSED);
4483	from_desc.cd_cnid = fcp->c_cnid;
4484
4485	bzero(&to_desc, sizeof(to_desc));
4486	to_desc.cd_nameptr = (const u_int8_t *)tcnp->cn_nameptr;
4487	to_desc.cd_namelen = tcnp->cn_namelen;
4488	to_desc.cd_parentcnid = tdcp->c_fileid;
4489	to_desc.cd_flags = fcp->c_desc.cd_flags & ~(CD_HASBUF | CD_DECOMPOSED);
4490	to_desc.cd_cnid = fcp->c_cnid;
4491
4492	if ((error = hfs_start_transaction(hfsmp)) != 0) {
4493	    goto out;
4494	}
4495	started_tr = 1;
4496
4497	/* hfs_vnop_link() and hfs_vnop_rename() set kHFSHasChildLinkMask
4498	 * inside a journal transaction and without holding a cnode lock.
4499	 * As setting of this bit depends on being in journal transaction for
4500	 * concurrency, check this bit again after we start journal transaction for rename
4501	 * to ensure that this directory does not have any descendant that
4502	 * is a directory hard link.
4503	 */
4504	if (vnode_isdir(fvp) && (fdvp != tdvp)) {
4505		if (fcp->c_attr.ca_recflags & kHFSHasChildLinkMask) {
4506		    lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_SHARED_LOCK);
4507		    if (cat_check_link_ancestry(hfsmp, tdcp->c_fileid, 0)) {
4508				error = EPERM;
4509				hfs_systemfile_unlock(hfsmp, lockflags);
4510				goto out;
4511			}
4512			hfs_systemfile_unlock(hfsmp, lockflags);
4513		}
4514	}
4515
4516	// if it's a hardlink then re-lookup the name so
4517	// that we get the correct cnid in from_desc (see
4518	// the comment in hfs_removefile for more details)
4519	//
4520	if (fcp->c_flag & C_HARDLINK) {
4521	    struct cat_desc tmpdesc;
4522	    cnid_t real_cnid;
4523
4524	    tmpdesc.cd_nameptr = (const u_int8_t *)fcnp->cn_nameptr;
4525	    tmpdesc.cd_namelen = fcnp->cn_namelen;
4526	    tmpdesc.cd_parentcnid = fdcp->c_fileid;
4527	    tmpdesc.cd_hint = fdcp->c_childhint;
4528	    tmpdesc.cd_flags = fcp->c_desc.cd_flags & CD_ISDIR;
4529	    tmpdesc.cd_encoding = 0;
4530
4531	    lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_SHARED_LOCK);
4532
4533	    if (cat_lookup(hfsmp, &tmpdesc, 0, 0, NULL, NULL, NULL, &real_cnid) != 0) {
4534		hfs_systemfile_unlock(hfsmp, lockflags);
4535		goto out;
4536	    }
4537
4538	    // use the real cnid instead of whatever happened to be there
4539	    from_desc.cd_cnid = real_cnid;
4540	    hfs_systemfile_unlock(hfsmp, lockflags);
4541	}
4542
4543	/*
4544	 * Reserve some space in the Catalog file.
4545	 */
4546	if ((error = cat_preflight(hfsmp, CAT_RENAME + CAT_DELETE, &cookie, p))) {
4547		goto out;
4548	}
4549	got_cookie = 1;
4550
4551	/*
4552	 * If the destination exists then it may need to be removed.
4553	 *
4554	 * Due to HFS's locking system, we should always move the
4555	 * existing 'tvp' element to the hidden directory in hfs_vnop_rename.
4556	 * Because the VNOP_LOOKUP call enters and exits the filesystem independently
4557	 * of the actual vnop that it was trying to do (stat, link, readlink),
4558	 * we must release the cnode lock of that element during the interim to
4559	 * do MAC checking, vnode authorization, and other calls.  In that time,
4560	 * the item can be deleted (or renamed over). However, only in the rename
4561	 * case is it inappropriate to return ENOENT from any of those calls.  Either
4562	 * the call should return information about the old element (stale), or get
4563	 * information about the newer element that we are about to write in its place.
4564	 *
4565	 * HFS lookup has been modified to detect a rename and re-drive its
4566	 * lookup internally. For other calls that have already succeeded in
4567	 * their lookup call and are waiting to acquire the cnode lock in order
4568	 * to proceed, that cnode lock will not fail due to the cnode being marked
4569	 * C_NOEXISTS, because it won't have been marked as such.  It will only
4570	 * have C_DELETED.  Thus, they will simply act on the stale open-unlinked
4571	 * element.  All future callers will get the new element.
4572	 *
4573	 * To implement this behavior, we pass the "only_unlink" argument to
4574	 * hfs_removefile and hfs_removedir.  This will result in the vnode acting
4575	 * as though it is open-unlinked.  Additionally, when we are done moving the
4576	 * element to the hidden directory, we vnode_recycle the target so that it is
4577	 * reclaimed as soon as possible.  Reclaim and inactive are both
4578	 * capable of clearing out unused blocks for an open-unlinked file or dir.
4579	 */
4580	if (tvp) {
4581		//
4582		// if the destination has a document id, we need to preserve it
4583		//
4584		if (fvp != tvp) {
4585			uint32_t document_id;
4586			struct FndrExtendedDirInfo *ffip = (struct FndrExtendedDirInfo *)((char *)&fcp->c_attr.ca_finderinfo + 16);
4587			struct FndrExtendedDirInfo *tfip = (struct FndrExtendedDirInfo *)((char *)&tcp->c_attr.ca_finderinfo + 16);
4588
4589			if (ffip->document_id && tfip->document_id) {
4590				// both documents are tracked.  only save a tombstone from tcp and do nothing else.
4591				save_tombstone(hfsmp, tdvp, tvp, tcnp, 0);
4592			} else {
4593				struct  doc_tombstone *ut;
4594				ut = get_uthread_doc_tombstone();
4595
4596				document_id = tfip->document_id;
4597				tfip->document_id = 0;
4598
4599				if (document_id != 0) {
4600					// clear UF_TRACKED as well since tcp is now no longer tracked
4601					tcp->c_bsdflags &= ~UF_TRACKED;
4602					(void) cat_update(hfsmp, &tcp->c_desc, &tcp->c_attr, NULL, NULL);
4603				}
4604
4605				if (ffip->document_id == 0 && document_id != 0) {
4606					// printf("RENAME: preserving doc-id %d onto %s (from ino %d, to ino %d)\n", document_id, tcp->c_desc.cd_nameptr, tcp->c_desc.cd_cnid, fcp->c_desc.cd_cnid);
4607					fcp->c_bsdflags |= UF_TRACKED;
4608					ffip->document_id = document_id;
4609
4610					(void) cat_update(hfsmp, &fcp->c_desc, &fcp->c_attr, NULL, NULL);
4611#if CONFIG_FSE
4612					add_fsevent(FSE_DOCID_CHANGED, vfs_context_current(),
4613						    FSE_ARG_DEV, hfsmp->hfs_raw_dev,
4614						    FSE_ARG_INO, (ino64_t)tcp->c_fileid,           // src inode #
4615						    FSE_ARG_INO, (ino64_t)fcp->c_fileid,           // dst inode #
4616						    FSE_ARG_INT32, (uint32_t)ffip->document_id,
4617						    FSE_ARG_DONE);
4618#endif
4619				} else if ((fcp->c_bsdflags & UF_TRACKED) && should_save_docid_tombstone(ut, fvp, fcnp)) {
4620
4621					if (ut->t_lastop_document_id) {
4622						clear_tombstone_docid(ut, hfsmp, NULL);
4623					}
4624					save_tombstone(hfsmp, fdvp, fvp, fcnp, 0);
4625
4626					//printf("RENAME: (dest-exists): saving tombstone doc-id %lld @ %s (ino %d)\n",
4627					//       ut->t_lastop_document_id, ut->t_lastop_filename, fcp->c_desc.cd_cnid);
4628				}
4629			}
4630		}
4631
4632		/*
4633		 * When fvp matches tvp they could be case variants
4634		 * or matching hard links.
4635		 */
4636		if (fvp == tvp) {
4637			if (!(fcp->c_flag & C_HARDLINK)) {
4638				/*
4639				 * If they're not hardlinks, then fvp == tvp must mean we
4640				 * are using case-insensitive HFS because case-sensitive would
4641				 * not use the same vnode for both.  In this case we just update
4642				 * the catalog for: a -> A
4643				 */
4644				goto skip_rm;  /* simple case variant */
4645
4646			}
4647		   	/* For all cases below, we must be using hardlinks */
4648			else if ((fdvp != tdvp) ||
4649			           (hfsmp->hfs_flags & HFS_CASE_SENSITIVE)) {
4650				/*
4651				 * If the parent directories are not the same, AND the two items
4652				 * are hardlinks, posix says to do nothing:
4653				 * dir1/fred <-> dir2/bob   and the op was mv dir1/fred -> dir2/bob
4654				 * We just return 0 in this case.
4655				 *
4656				 * If case sensitivity is on, and we are using hardlinks
4657				 * then renaming is supposed to do nothing.
4658				 * dir1/fred <-> dir2/FRED, and op == mv dir1/fred -> dir2/FRED
4659				 */
4660				goto out;  /* matching hardlinks, nothing to do */
4661
4662			} else if (hfs_namecmp((const u_int8_t *)fcnp->cn_nameptr, fcnp->cn_namelen,
4663			                       (const u_int8_t *)tcnp->cn_nameptr, tcnp->cn_namelen) == 0) {
4664				/*
4665				 * If we get here, then the following must be true:
4666				 * a) We are running case-insensitive HFS+.
4667				 * b) Both paths 'fvp' and 'tvp' are in the same parent directory.
4668				 * c) the two names are case-variants of each other.
4669				 *
4670				 * In this case, we are really only dealing with a single catalog record
4671				 * whose name is being updated.
4672				 *
4673				 * op is dir1/fred -> dir1/FRED
4674				 *
4675				 * We need to special case the name matching, because if
4676				 * dir1/fred <-> dir1/bob were the two links, and the
4677				 * op was dir1/fred -> dir1/bob
4678				 * That would fail/do nothing.
4679				 */
4680				goto skip_rm;  /* case-variant hardlink in the same dir */
4681			} else {
4682				goto out;  /* matching hardlink, nothing to do */
4683			}
4684		}
4685
4686
4687		if (vnode_isdir(tvp)) {
4688			/*
4689			 * hfs_removedir will eventually call hfs_removefile on the directory
4690			 * we're working on, because only hfs_removefile does the renaming of the
4691			 * item to the hidden directory.  The directory will stay around in the
4692			 * hidden directory with C_DELETED until it gets an inactive or a reclaim.
4693			 * That way, we can destroy all of the EAs as needed and allow new ones to be
4694			 * written.
4695			 */
4696			error = hfs_removedir(tdvp, tvp, tcnp, HFSRM_SKIP_RESERVE, 1);
4697		}
4698		else {
4699			error = hfs_removefile(tdvp, tvp, tcnp, 0, HFSRM_SKIP_RESERVE, 0, NULL, 1);
4700
4701			/*
4702			 * If the destination file had a resource fork vnode, then we need to get rid of
4703			 * its blocks when there are no more references to it.  Because the call to
4704			 * hfs_removefile above always open-unlinks things, we need to force an inactive/reclaim
4705			 * on the resource fork vnode, in order to prevent block leaks.  Otherwise,
4706			 * the resource fork vnode could prevent the data fork vnode from going out of scope
4707			 * because it holds a v_parent reference on it.  So we mark it for termination
4708			 * with a call to vnode_recycle. hfs_vnop_reclaim has been modified so that it
4709			 * can clean up the blocks of open-unlinked files and resource forks.
4710			 *
4711			 * We can safely call vnode_recycle on the resource fork because we took an iocount
4712			 * reference on it at the beginning of the function.
4713			 */
4714
4715			if ((error == 0) && (tcp->c_flag & C_DELETED) && (tvp_rsrc)) {
4716				vnode_recycle(tvp_rsrc);
4717			}
4718		}
4719
4720		if (error) {
4721			goto out;
4722		}
4723
4724		tvp_deleted = 1;
4725
4726		/* Mark 'tcp' as being deleted due to a rename */
4727		tcp->c_flag |= C_RENAMED;
4728
4729		/*
4730		 * Aggressively mark tvp/tcp for termination to ensure that we recover all blocks
4731		 * as quickly as possible.
4732		 */
4733		vnode_recycle(tvp);
4734	} else {
4735		struct  doc_tombstone *ut;
4736		ut = get_uthread_doc_tombstone();
4737
4738		//
4739		// There is nothing at the destination.  If the file being renamed is
4740		// tracked, save a "tombstone" of the document_id.  If the file is
4741		// not a tracked file, then see if it needs to inherit a tombstone.
4742		//
4743		// NOTE: we do not save a tombstone if the file being renamed begins
4744		//       with "atmp" which is done to work-around AutoCad's bizarre
4745		//       5-step un-safe save behavior
4746		//
4747		if (fcp->c_bsdflags & UF_TRACKED) {
4748			if (should_save_docid_tombstone(ut, fvp, fcnp)) {
4749				save_tombstone(hfsmp, fdvp, fvp, fcnp, 0);
4750
4751				//printf("RENAME: (no dest): saving tombstone doc-id %lld @ %s (ino %d)\n",
4752				//       ut->t_lastop_document_id, ut->t_lastop_filename, fcp->c_desc.cd_cnid);
4753			} else {
4754				// intentionally do nothing
4755			}
4756		} else if (   ut->t_lastop_document_id != 0
4757			   && tdvp == ut->t_lastop_parent
4758			   && vnode_vid(tdvp) == ut->t_lastop_parent_vid
4759			   && strcmp((char *)ut->t_lastop_filename, (char *)tcnp->cn_nameptr) == 0) {
4760
4761			//printf("RENAME: %s (ino %d) inheriting doc-id %lld\n", tcnp->cn_nameptr, fcp->c_desc.cd_cnid, ut->t_lastop_document_id);
4762			struct FndrExtendedFileInfo *fip = (struct FndrExtendedFileInfo *)((char *)&fcp->c_attr.ca_finderinfo + 16);
4763			fcp->c_bsdflags |= UF_TRACKED;
4764			fip->document_id = ut->t_lastop_document_id;
4765			cat_update(hfsmp, &fcp->c_desc, &fcp->c_attr, NULL, NULL);
4766
4767			clear_tombstone_docid(ut, hfsmp, fcp);    // will send the docid-changed fsevent
4768
4769		} else if (ut->t_lastop_document_id && should_save_docid_tombstone(ut, fvp, fcnp) && should_save_docid_tombstone(ut, tvp, tcnp)) {
4770			// no match, clear the tombstone
4771			//printf("RENAME: clearing the tombstone %lld @ %s\n", ut->t_lastop_document_id, ut->t_lastop_filename);
4772			clear_tombstone_docid(ut, hfsmp, NULL);
4773		}
4774
4775	}
4776skip_rm:
4777	/*
4778	 * All done with tvp and fvp.
4779	 *
4780	 * We also jump to this point if there was no destination observed during lookup and namei.
4781	 * However, because only iocounts are held at the VFS layer, there is nothing preventing a
4782	 * competing thread from racing us and creating a file or dir at the destination of this rename
4783	 * operation.  If this occurs, it may cause us to get a spurious EEXIST out of the cat_rename
4784	 * call below.  To preserve rename's atomicity, we need to signal VFS to re-drive the
4785	 * namei/lookup and restart the rename operation.  EEXIST is an allowable errno to be bubbled
4786	 * out of the rename syscall, but not for this reason, since it is a synonym errno for ENOTEMPTY.
4787	 * To signal VFS, we return ERECYCLE (which is also used for lookup restarts). This errno
4788	 * will be swallowed and it will restart the operation.
4789	 */
4790
4791	lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_EXCLUSIVE_LOCK);
4792	error = cat_rename(hfsmp, &from_desc, &tdcp->c_desc, &to_desc, &out_desc);
4793	hfs_systemfile_unlock(hfsmp, lockflags);
4794
4795	if (error) {
4796		if (error == EEXIST) {
4797			error = ERECYCLE;
4798		}
4799		goto out;
4800	}
4801
4802	/* Invalidate negative cache entries in the destination directory */
4803	if (tdcp->c_flag & C_NEG_ENTRIES) {
4804		cache_purge_negatives(tdvp);
4805		tdcp->c_flag &= ~C_NEG_ENTRIES;
4806	}
4807
4808	/* Update cnode's catalog descriptor */
4809	replace_desc(fcp, &out_desc);
4810	fcp->c_parentcnid = tdcp->c_fileid;
4811	fcp->c_hint = 0;
4812
4813	/* Now indicate this cnode needs to have date-added written to the finderinfo */
4814	fcp->c_flag |= C_NEEDS_DATEADDED;
4815	(void) hfs_update (fvp, 0);
4816
4817
4818	hfs_volupdate(hfsmp, vnode_isdir(fvp) ? VOL_RMDIR : VOL_RMFILE,
4819	              (fdcp->c_cnid == kHFSRootFolderID));
4820	hfs_volupdate(hfsmp, vnode_isdir(fvp) ? VOL_MKDIR : VOL_MKFILE,
4821	              (tdcp->c_cnid == kHFSRootFolderID));
4822
4823	/* Update both parent directories. */
4824	if (fdvp != tdvp) {
4825		if (vnode_isdir(fvp)) {
4826			/* If the source directory has directory hard link
4827			 * descendants, set the kHFSHasChildLinkBit in the
4828			 * destination parent hierarchy
4829			 */
4830			if ((fcp->c_attr.ca_recflags & kHFSHasChildLinkMask) &&
4831			    !(tdcp->c_attr.ca_recflags & kHFSHasChildLinkMask)) {
4832
4833				tdcp->c_attr.ca_recflags |= kHFSHasChildLinkMask;
4834
4835				error = cat_set_childlinkbit(hfsmp, tdcp->c_parentcnid);
4836				if (error) {
4837					printf ("hfs_vnop_rename: error updating parent chain for %u\n", tdcp->c_cnid);
4838					error = 0;
4839				}
4840			}
4841			INC_FOLDERCOUNT(hfsmp, tdcp->c_attr);
4842			DEC_FOLDERCOUNT(hfsmp, fdcp->c_attr);
4843		}
4844		tdcp->c_entries++;
4845		tdcp->c_dirchangecnt++;
4846		{
4847			struct FndrExtendedDirInfo *extinfo = (struct FndrExtendedDirInfo *)((u_int8_t*)tdcp->c_finderinfo + 16);
4848			extinfo->write_gen_counter = OSSwapHostToBigInt32(OSSwapBigToHostInt32(extinfo->write_gen_counter) + 1);
4849		}
4850		if (fdcp->c_entries > 0)
4851			fdcp->c_entries--;
4852		fdcp->c_dirchangecnt++;
4853		fdcp->c_touch_chgtime = TRUE;
4854		fdcp->c_touch_modtime = TRUE;
4855
4856		fdcp->c_flag |= C_FORCEUPDATE;  // XXXdbg - force it out!
4857		(void) hfs_update(fdvp, 0);
4858	}
4859	{
4860		struct FndrExtendedDirInfo *extinfo = (struct FndrExtendedDirInfo *)((u_int8_t*)fdcp->c_finderinfo + 16);
4861		extinfo->write_gen_counter = OSSwapHostToBigInt32(OSSwapBigToHostInt32(extinfo->write_gen_counter) + 1);
4862	}
4863
4864	tdcp->c_childhint = out_desc.cd_hint;	/* Cache directory's location */
4865	tdcp->c_touch_chgtime = TRUE;
4866	tdcp->c_touch_modtime = TRUE;
4867
4868	tdcp->c_flag |= C_FORCEUPDATE;  // XXXdbg - force it out!
4869	(void) hfs_update(tdvp, 0);
4870
4871	/* Update the vnode's name now that the rename has completed. */
4872	vnode_update_identity(fvp, tdvp, tcnp->cn_nameptr, tcnp->cn_namelen,
4873			tcnp->cn_hash, (VNODE_UPDATE_PARENT | VNODE_UPDATE_NAME));
4874
4875	/*
4876	 * At this point, we may have a resource fork vnode attached to the
4877	 * 'from' vnode.  If it exists, we will want to update its name, because
4878	 * it contains the old name + _PATH_RSRCFORKSPEC. ("/..namedfork/rsrc").
4879	 *
4880	 * Note that the only thing we need to update here is the name attached to
4881	 * the vnode, since a resource fork vnode does not have a separate resource
4882	 * cnode -- it's still 'fcp'.
4883	 */
4884	if (fcp->c_rsrc_vp) {
4885		char* rsrc_path = NULL;
4886		int len;
4887
4888		/* Create a new temporary buffer that's going to hold the new name */
4889		MALLOC_ZONE (rsrc_path, caddr_t, MAXPATHLEN, M_NAMEI, M_WAITOK);
4890		len = snprintf (rsrc_path, MAXPATHLEN, "%s%s", tcnp->cn_nameptr, _PATH_RSRCFORKSPEC);
4891		len = MIN(len, MAXPATHLEN);
4892
4893		/*
4894		 * vnode_update_identity will do the following for us:
4895		 * 1) release reference on the existing rsrc vnode's name.
4896		 * 2) copy/insert new name into the name cache
4897		 * 3) attach the new name to the resource vnode
4898		 * 4) update the vnode's vid
4899		 */
4900		vnode_update_identity (fcp->c_rsrc_vp, fvp, rsrc_path, len, 0, (VNODE_UPDATE_NAME | VNODE_UPDATE_CACHE));
4901
4902		/* Free the memory associated with the resource fork's name */
4903		FREE_ZONE (rsrc_path, MAXPATHLEN, M_NAMEI);
4904	}
4905out:
4906	if (got_cookie) {
4907		cat_postflight(hfsmp, &cookie, p);
4908	}
4909	if (started_tr) {
4910	    hfs_end_transaction(hfsmp);
4911	}
4912
4913	fdcp->c_flag &= ~C_DIR_MODIFICATION;
4914	wakeup((caddr_t)&fdcp->c_flag);
4915	if (fdvp != tdvp) {
4916	    tdcp->c_flag &= ~C_DIR_MODIFICATION;
4917	    wakeup((caddr_t)&tdcp->c_flag);
4918	}
4919
4920	if (took_trunc_lock) {
4921		hfs_unlock_truncate(VTOC(tvp), HFS_LOCK_DEFAULT);
4922	}
4923
4924	hfs_unlockfour(fdcp, fcp, tdcp, tcp);
4925
4926	/* Now vnode_put the resource forks vnodes if necessary */
4927	if (tvp_rsrc) {
4928		vnode_put(tvp_rsrc);
4929		tvp_rsrc = NULL;
4930	}
4931
4932	/* After tvp is removed the only acceptable error is EIO */
4933	if (error && tvp_deleted)
4934		error = EIO;
4935
4936	if (emit_rename && is_tracked) {
4937		if (error) {
4938			resolve_nspace_item(fvp, NAMESPACE_HANDLER_RENAME_FAILED_OP | NAMESPACE_HANDLER_TRACK_EVENT);
4939		} else {
4940			resolve_nspace_item(fvp, NAMESPACE_HANDLER_RENAME_SUCCESS_OP | NAMESPACE_HANDLER_TRACK_EVENT);
4941		}
4942	}
4943
4944	return (error);
4945}
4946
4947
4948/*
4949 * Make a directory.
4950 */
4951int
4952hfs_vnop_mkdir(struct vnop_mkdir_args *ap)
4953{
4954	/***** HACK ALERT ********/
4955	ap->a_cnp->cn_flags |= MAKEENTRY;
4956	return hfs_makenode(ap->a_dvp, ap->a_vpp, ap->a_cnp, ap->a_vap, ap->a_context);
4957}
4958
4959
4960/*
4961 * Create a symbolic link.
4962 */
4963int
4964hfs_vnop_symlink(struct vnop_symlink_args *ap)
4965{
4966	struct vnode **vpp = ap->a_vpp;
4967	struct vnode *dvp = ap->a_dvp;
4968	struct vnode *vp = NULL;
4969	struct cnode *cp = NULL;
4970	struct hfsmount *hfsmp;
4971	struct filefork *fp;
4972	struct buf *bp = NULL;
4973	char *datap;
4974	int started_tr = 0;
4975	u_int32_t len;
4976	int error;
4977
4978	/* HFS standard disks don't support symbolic links */
4979	if (VTOVCB(dvp)->vcbSigWord != kHFSPlusSigWord)
4980		return (ENOTSUP);
4981
4982	/* Check for empty target name */
4983	if (ap->a_target[0] == 0)
4984		return (EINVAL);
4985
4986	hfsmp = VTOHFS(dvp);
4987	len = strlen(ap->a_target);
4988
4989	/* Check for free space */
4990	if (((u_int64_t)hfs_freeblks(hfsmp, 0) * (u_int64_t)hfsmp->blockSize) < len) {
4991		return (ENOSPC);
4992	}
4993
4994	/* Create the vnode */
4995	ap->a_vap->va_mode |= S_IFLNK;
4996	if ((error = hfs_makenode(dvp, vpp, ap->a_cnp, ap->a_vap, ap->a_context))) {
4997		goto out;
4998	}
4999	vp = *vpp;
5000	if ((error = hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT))) {
5001		goto out;
5002	}
5003	cp = VTOC(vp);
5004	fp = VTOF(vp);
5005
5006	if (cp->c_flag & (C_NOEXISTS | C_DELETED)) {
5007	    goto out;
5008	}
5009
5010#if QUOTA
5011	(void)hfs_getinoquota(cp);
5012#endif /* QUOTA */
5013
5014	if ((error = hfs_start_transaction(hfsmp)) != 0) {
5015	    goto out;
5016	}
5017	started_tr = 1;
5018
5019	/*
5020	 * Allocate space for the link.
5021	 *
5022	 * Since we're already inside a transaction,
5023	 * tell hfs_truncate to skip the ubc_setsize.
5024	 *
5025	 * Don't need truncate lock since a symlink is treated as a system file.
5026	 */
5027	error = hfs_truncate(vp, len, IO_NOZEROFILL, 1, 0, ap->a_context);
5028
5029	/* On errors, remove the symlink file */
5030	if (error) {
5031		/*
5032		 * End the transaction so we don't re-take the cnode lock
5033		 * below while inside a transaction (lock order violation).
5034		 */
5035		hfs_end_transaction(hfsmp);
5036
5037		/* hfs_removefile() requires holding the truncate lock */
5038		hfs_unlock(cp);
5039		hfs_lock_truncate(cp, HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT);
5040		hfs_lock(cp, HFS_EXCLUSIVE_LOCK, HFS_LOCK_ALLOW_NOEXISTS);
5041
5042		if (hfs_start_transaction(hfsmp) != 0) {
5043			started_tr = 0;
5044			hfs_unlock_truncate(cp, HFS_LOCK_DEFAULT);
5045			goto out;
5046		}
5047
5048		(void) hfs_removefile(dvp, vp, ap->a_cnp, 0, 0, 0, NULL, 0);
5049		hfs_unlock_truncate(cp, HFS_LOCK_DEFAULT);
5050		goto out;
5051	}
5052
5053	/* Write the link to disk */
5054	bp = buf_getblk(vp, (daddr64_t)0, roundup((int)fp->ff_size, hfsmp->hfs_physical_block_size),
5055			0, 0, BLK_META);
5056	if (hfsmp->jnl) {
5057		journal_modify_block_start(hfsmp->jnl, bp);
5058	}
5059	datap = (char *)buf_dataptr(bp);
5060	bzero(datap, buf_size(bp));
5061	bcopy(ap->a_target, datap, len);
5062
5063	if (hfsmp->jnl) {
5064		journal_modify_block_end(hfsmp->jnl, bp, NULL, NULL);
5065	} else {
5066		buf_bawrite(bp);
5067	}
5068	/*
5069	 * We defered the ubc_setsize for hfs_truncate
5070	 * since we were inside a transaction.
5071	 *
5072	 * We don't need to drop the cnode lock here
5073	 * since this is a symlink.
5074	 */
5075	ubc_setsize(vp, len);
5076out:
5077	if (started_tr)
5078	    hfs_end_transaction(hfsmp);
5079	if ((cp != NULL) && (vp != NULL)) {
5080		hfs_unlock(cp);
5081	}
5082	if (error) {
5083		if (vp) {
5084			vnode_put(vp);
5085		}
5086		*vpp = NULL;
5087	}
5088	return (error);
5089}
5090
5091
5092/* structures to hold a "." or ".." directory entry */
5093struct hfs_stddotentry {
5094	u_int32_t	d_fileno;   /* unique file number */
5095	u_int16_t	d_reclen;   /* length of this structure */
5096	u_int8_t	d_type;     /* dirent file type */
5097	u_int8_t	d_namlen;   /* len of filename */
5098	char		d_name[4];  /* "." or ".." */
5099};
5100
5101struct hfs_extdotentry {
5102	u_int64_t  d_fileno;   /* unique file number */
5103	u_int64_t  d_seekoff;  /* seek offset (optional, used by servers) */
5104	u_int16_t  d_reclen;   /* length of this structure */
5105	u_int16_t  d_namlen;   /* len of filename */
5106	u_int8_t   d_type;     /* dirent file type */
5107	u_char     d_name[3];  /* "." or ".." */
5108};
5109
5110typedef union {
5111	struct hfs_stddotentry  std;
5112	struct hfs_extdotentry  ext;
5113} hfs_dotentry_t;
5114
5115/*
5116 *  hfs_vnop_readdir reads directory entries into the buffer pointed
5117 *  to by uio, in a filesystem independent format.  Up to uio_resid
5118 *  bytes of data can be transferred.  The data in the buffer is a
5119 *  series of packed dirent structures where each one contains the
5120 *  following entries:
5121 *
5122 *	u_int32_t   d_fileno;              // file number of entry
5123 *	u_int16_t   d_reclen;              // length of this record
5124 *	u_int8_t    d_type;                // file type
5125 *	u_int8_t    d_namlen;              // length of string in d_name
5126 *	char        d_name[MAXNAMELEN+1];  // null terminated file name
5127 *
5128 *  The current position (uio_offset) refers to the next block of
5129 *  entries.  The offset can only be set to a value previously
5130 *  returned by hfs_vnop_readdir or zero.  This offset does not have
5131 *  to match the number of bytes returned (in uio_resid).
5132 *
5133 *  In fact, the offset used by HFS is essentially an index (26 bits)
5134 *  with a tag (6 bits).  The tag is for associating the next request
5135  *  with the current request.  This enables us to have multiple threads
5136 *  reading the directory while the directory is also being modified.
5137 *
5138 *  Each tag/index pair is tied to a unique directory hint.  The hint
5139 *  contains information (filename) needed to build the catalog b-tree
5140 *  key for finding the next set of entries.
5141 *
5142 * If the directory is marked as deleted-but-in-use (cp->c_flag & C_DELETED),
5143 * do NOT synthesize entries for "." and "..".
5144 */
5145int
5146hfs_vnop_readdir(ap)
5147	struct vnop_readdir_args /* {
5148		vnode_t a_vp;
5149		uio_t a_uio;
5150		int a_flags;
5151		int *a_eofflag;
5152		int *a_numdirent;
5153		vfs_context_t a_context;
5154	} */ *ap;
5155{
5156	struct vnode *vp = ap->a_vp;
5157	uio_t uio = ap->a_uio;
5158	struct cnode *cp;
5159	struct hfsmount *hfsmp;
5160	directoryhint_t *dirhint = NULL;
5161	directoryhint_t localhint;
5162	off_t offset;
5163	off_t startoffset;
5164	int error = 0;
5165	int eofflag = 0;
5166	user_addr_t user_start = 0;
5167	user_size_t user_len = 0;
5168	int index;
5169	unsigned int tag;
5170	int items;
5171	int lockflags;
5172	int extended;
5173	int nfs_cookies;
5174	cnid_t cnid_hint = 0;
5175
5176	items = 0;
5177	startoffset = offset = uio_offset(uio);
5178	extended = (ap->a_flags & VNODE_READDIR_EXTENDED);
5179	nfs_cookies = extended && (ap->a_flags & VNODE_READDIR_REQSEEKOFF);
5180
5181	/* Sanity check the uio data. */
5182	if (uio_iovcnt(uio) > 1)
5183		return (EINVAL);
5184
5185	if (VTOC(vp)->c_bsdflags & UF_COMPRESSED) {
5186		int compressed = hfs_file_is_compressed(VTOC(vp), 0);  /* 0 == take the cnode lock */
5187		if (VTOCMP(vp) != NULL && !compressed) {
5188			error = check_for_dataless_file(vp, NAMESPACE_HANDLER_READ_OP);
5189			if (error) {
5190				return error;
5191			}
5192		}
5193	}
5194
5195	cp = VTOC(vp);
5196	hfsmp = VTOHFS(vp);
5197
5198	/* Note that the dirhint calls require an exclusive lock. */
5199	if ((error = hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT)))
5200		return (error);
5201
5202	/* Pick up cnid hint (if any). */
5203	if (nfs_cookies) {
5204		cnid_hint = (cnid_t)(uio_offset(uio) >> 32);
5205		uio_setoffset(uio, uio_offset(uio) & 0x00000000ffffffffLL);
5206		if (cnid_hint == INT_MAX) { /* searching pass the last item */
5207			eofflag = 1;
5208			goto out;
5209		}
5210	}
5211	/*
5212	 * Synthesize entries for "." and "..", unless the directory has
5213	 * been deleted, but not closed yet (lazy delete in progress).
5214	 */
5215	if (offset == 0 && !(cp->c_flag & C_DELETED)) {
5216		hfs_dotentry_t  dotentry[2];
5217		size_t  uiosize;
5218
5219		if (extended) {
5220			struct hfs_extdotentry *entry = &dotentry[0].ext;
5221
5222			entry->d_fileno = cp->c_cnid;
5223			entry->d_reclen = sizeof(struct hfs_extdotentry);
5224			entry->d_type = DT_DIR;
5225			entry->d_namlen = 1;
5226			entry->d_name[0] = '.';
5227			entry->d_name[1] = '\0';
5228			entry->d_name[2] = '\0';
5229			entry->d_seekoff = 1;
5230
5231			++entry;
5232			entry->d_fileno = cp->c_parentcnid;
5233			entry->d_reclen = sizeof(struct hfs_extdotentry);
5234			entry->d_type = DT_DIR;
5235			entry->d_namlen = 2;
5236			entry->d_name[0] = '.';
5237			entry->d_name[1] = '.';
5238			entry->d_name[2] = '\0';
5239			entry->d_seekoff = 2;
5240			uiosize = 2 * sizeof(struct hfs_extdotentry);
5241		} else {
5242			struct hfs_stddotentry *entry = &dotentry[0].std;
5243
5244			entry->d_fileno = cp->c_cnid;
5245			entry->d_reclen = sizeof(struct hfs_stddotentry);
5246			entry->d_type = DT_DIR;
5247			entry->d_namlen = 1;
5248			*(int *)&entry->d_name[0] = 0;
5249			entry->d_name[0] = '.';
5250
5251			++entry;
5252			entry->d_fileno = cp->c_parentcnid;
5253			entry->d_reclen = sizeof(struct hfs_stddotentry);
5254			entry->d_type = DT_DIR;
5255			entry->d_namlen = 2;
5256			*(int *)&entry->d_name[0] = 0;
5257			entry->d_name[0] = '.';
5258			entry->d_name[1] = '.';
5259			uiosize = 2 * sizeof(struct hfs_stddotentry);
5260		}
5261		if ((error = uiomove((caddr_t)&dotentry, uiosize, uio))) {
5262			goto out;
5263		}
5264		offset += 2;
5265	}
5266
5267	/* If there are no real entries then we're done. */
5268	if (cp->c_entries == 0) {
5269		error = 0;
5270		eofflag = 1;
5271		uio_setoffset(uio, offset);
5272		goto seekoffcalc;
5273	}
5274
5275	//
5276	// We have to lock the user's buffer here so that we won't
5277	// fault on it after we've acquired a shared lock on the
5278	// catalog file.  The issue is that you can get a 3-way
5279	// deadlock if someone else starts a transaction and then
5280	// tries to lock the catalog file but can't because we're
5281	// here and we can't service our page fault because VM is
5282	// blocked trying to start a transaction as a result of
5283	// trying to free up pages for our page fault.  It's messy
5284	// but it does happen on dual-processors that are paging
5285	// heavily (see radar 3082639 for more info).  By locking
5286	// the buffer up-front we prevent ourselves from faulting
5287	// while holding the shared catalog file lock.
5288	//
5289	// Fortunately this and hfs_search() are the only two places
5290	// currently (10/30/02) that can fault on user data with a
5291	// shared lock on the catalog file.
5292	//
5293	if (hfsmp->jnl && uio_isuserspace(uio)) {
5294		user_start = uio_curriovbase(uio);
5295		user_len = uio_curriovlen(uio);
5296
5297		if ((error = vslock(user_start, user_len)) != 0) {
5298			user_start = 0;
5299			goto out;
5300		}
5301	}
5302	/* Convert offset into a catalog directory index. */
5303	index = (offset & HFS_INDEX_MASK) - 2;
5304	tag = offset & ~HFS_INDEX_MASK;
5305
5306	/* Lock catalog during cat_findname and cat_getdirentries. */
5307	lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_SHARED_LOCK);
5308
5309	/* When called from NFS, try and resolve a cnid hint. */
5310	if (nfs_cookies && cnid_hint != 0) {
5311		if (cat_findname(hfsmp, cnid_hint, &localhint.dh_desc) == 0) {
5312			if ( localhint.dh_desc.cd_parentcnid == cp->c_fileid) {
5313				localhint.dh_index = index - 1;
5314				localhint.dh_time = 0;
5315				bzero(&localhint.dh_link, sizeof(localhint.dh_link));
5316				dirhint = &localhint;  /* don't forget to release the descriptor */
5317			} else {
5318				cat_releasedesc(&localhint.dh_desc);
5319			}
5320		}
5321	}
5322
5323	/* Get a directory hint (cnode must be locked exclusive) */
5324	if (dirhint == NULL) {
5325		dirhint = hfs_getdirhint(cp, ((index - 1) & HFS_INDEX_MASK) | tag, 0);
5326
5327		/* Hide tag from catalog layer. */
5328		dirhint->dh_index &= HFS_INDEX_MASK;
5329		if (dirhint->dh_index == HFS_INDEX_MASK) {
5330			dirhint->dh_index = -1;
5331		}
5332	}
5333
5334	if (index == 0) {
5335		dirhint->dh_threadhint = cp->c_dirthreadhint;
5336	}
5337	else {
5338		/*
5339		 * If we have a non-zero index, there is a possibility that during the last
5340		 * call to hfs_vnop_readdir we hit EOF for this directory.  If that is the case
5341		 * then we don't want to return any new entries for the caller.  Just return 0
5342		 * items, mark the eofflag, and bail out.  Because we won't have done any work, the
5343		 * code at the end of the function will release the dirhint for us.
5344		 *
5345		 * Don't forget to unlock the catalog lock on the way out, too.
5346		 */
5347		if (dirhint->dh_desc.cd_flags & CD_EOF) {
5348			error = 0;
5349			eofflag = 1;
5350			uio_setoffset(uio, startoffset);
5351			hfs_systemfile_unlock (hfsmp, lockflags);
5352
5353			goto seekoffcalc;
5354		}
5355	}
5356
5357	/* Pack the buffer with dirent entries. */
5358	error = cat_getdirentries(hfsmp, cp->c_entries, dirhint, uio, ap->a_flags, &items, &eofflag);
5359
5360	if (index == 0 && error == 0) {
5361		cp->c_dirthreadhint = dirhint->dh_threadhint;
5362	}
5363
5364	hfs_systemfile_unlock(hfsmp, lockflags);
5365
5366	if (error != 0) {
5367		goto out;
5368	}
5369
5370	/* Get index to the next item */
5371	index += items;
5372
5373	if (items >= (int)cp->c_entries) {
5374		eofflag = 1;
5375	}
5376
5377	/* Convert catalog directory index back into an offset. */
5378	while (tag == 0)
5379		tag = (++cp->c_dirhinttag) << HFS_INDEX_BITS;
5380	uio_setoffset(uio, (index + 2) | tag);
5381	dirhint->dh_index |= tag;
5382
5383seekoffcalc:
5384	cp->c_touch_acctime = TRUE;
5385
5386	if (ap->a_numdirent) {
5387		if (startoffset == 0)
5388			items += 2;
5389		*ap->a_numdirent = items;
5390	}
5391
5392out:
5393	if (user_start) {
5394		vsunlock(user_start, user_len, TRUE);
5395	}
5396	/* If we didn't do anything then go ahead and dump the hint. */
5397	if ((dirhint != NULL) &&
5398	    (dirhint != &localhint) &&
5399	    (uio_offset(uio) == startoffset)) {
5400		hfs_reldirhint(cp, dirhint);
5401		eofflag = 1;
5402	}
5403	if (ap->a_eofflag) {
5404		*ap->a_eofflag = eofflag;
5405	}
5406	if (dirhint == &localhint) {
5407		cat_releasedesc(&localhint.dh_desc);
5408	}
5409	hfs_unlock(cp);
5410	return (error);
5411}
5412
5413
5414/*
5415 * Read contents of a symbolic link.
5416 */
5417int
5418hfs_vnop_readlink(ap)
5419	struct vnop_readlink_args /* {
5420		struct vnode *a_vp;
5421		struct uio *a_uio;
5422		vfs_context_t a_context;
5423	} */ *ap;
5424{
5425	struct vnode *vp = ap->a_vp;
5426	struct cnode *cp;
5427	struct filefork *fp;
5428	int error;
5429
5430	if (!vnode_islnk(vp))
5431		return (EINVAL);
5432
5433	if ((error = hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT)))
5434		return (error);
5435	cp = VTOC(vp);
5436	fp = VTOF(vp);
5437
5438	/* Zero length sym links are not allowed */
5439	if (fp->ff_size == 0 || fp->ff_size > MAXPATHLEN) {
5440		error = EINVAL;
5441		goto exit;
5442	}
5443
5444	/* Cache the path so we don't waste buffer cache resources */
5445	if (fp->ff_symlinkptr == NULL) {
5446		struct buf *bp = NULL;
5447
5448		MALLOC(fp->ff_symlinkptr, char *, fp->ff_size, M_TEMP, M_WAITOK);
5449		if (fp->ff_symlinkptr == NULL) {
5450			error = ENOMEM;
5451			goto exit;
5452		}
5453		error = (int)buf_meta_bread(vp, (daddr64_t)0,
5454		                            roundup((int)fp->ff_size, VTOHFS(vp)->hfs_physical_block_size),
5455		                            vfs_context_ucred(ap->a_context), &bp);
5456		if (error) {
5457			if (bp)
5458				buf_brelse(bp);
5459			if (fp->ff_symlinkptr) {
5460				FREE(fp->ff_symlinkptr, M_TEMP);
5461				fp->ff_symlinkptr = NULL;
5462			}
5463			goto exit;
5464		}
5465		bcopy((char *)buf_dataptr(bp), fp->ff_symlinkptr, (size_t)fp->ff_size);
5466
5467		if (VTOHFS(vp)->jnl && (buf_flags(bp) & B_LOCKED) == 0) {
5468		        buf_markinvalid(bp);		/* data no longer needed */
5469		}
5470		buf_brelse(bp);
5471	}
5472	error = uiomove((caddr_t)fp->ff_symlinkptr, (int)fp->ff_size, ap->a_uio);
5473
5474	/*
5475	 * Keep track blocks read
5476	 */
5477	if ((VTOHFS(vp)->hfc_stage == HFC_RECORDING) && (error == 0)) {
5478
5479		/*
5480		 * If this file hasn't been seen since the start of
5481		 * the current sampling period then start over.
5482		 */
5483		if (cp->c_atime < VTOHFS(vp)->hfc_timebase)
5484			VTOF(vp)->ff_bytesread = fp->ff_size;
5485		else
5486			VTOF(vp)->ff_bytesread += fp->ff_size;
5487
5488	//	if (VTOF(vp)->ff_bytesread > fp->ff_size)
5489	//		cp->c_touch_acctime = TRUE;
5490	}
5491
5492exit:
5493	hfs_unlock(cp);
5494	return (error);
5495}
5496
5497
5498/*
5499 * Get configurable pathname variables.
5500 */
5501int
5502hfs_vnop_pathconf(ap)
5503	struct vnop_pathconf_args /* {
5504		struct vnode *a_vp;
5505		int a_name;
5506		int *a_retval;
5507		vfs_context_t a_context;
5508	} */ *ap;
5509{
5510
5511	int std_hfs = (VTOHFS(ap->a_vp)->hfs_flags & HFS_STANDARD);
5512	switch (ap->a_name) {
5513	case _PC_LINK_MAX:
5514		if (std_hfs == 0){
5515			*ap->a_retval = HFS_LINK_MAX;
5516		}
5517#if CONFIG_HFS_STD
5518		else {
5519			*ap->a_retval = 1;
5520		}
5521#endif
5522		break;
5523	case _PC_NAME_MAX:
5524		if (std_hfs == 0) {
5525			*ap->a_retval = kHFSPlusMaxFileNameChars;  /* 255 */
5526		}
5527#if CONFIG_HFS_STD
5528		else {
5529			*ap->a_retval = kHFSMaxFileNameChars;  /* 31 */
5530		}
5531#endif
5532		break;
5533	case _PC_PATH_MAX:
5534		*ap->a_retval = PATH_MAX;  /* 1024 */
5535		break;
5536	case _PC_PIPE_BUF:
5537		*ap->a_retval = PIPE_BUF;
5538		break;
5539	case _PC_CHOWN_RESTRICTED:
5540		*ap->a_retval = 200112;		/* _POSIX_CHOWN_RESTRICTED */
5541		break;
5542	case _PC_NO_TRUNC:
5543		*ap->a_retval = 200112;		/* _POSIX_NO_TRUNC */
5544		break;
5545	case _PC_NAME_CHARS_MAX:
5546		if (std_hfs == 0) {
5547			*ap->a_retval = kHFSPlusMaxFileNameChars; /* 255 */
5548		}
5549#if CONFIG_HFS_STD
5550		else {
5551			*ap->a_retval = kHFSMaxFileNameChars; /* 31 */
5552		}
5553#endif
5554		break;
5555	case _PC_CASE_SENSITIVE:
5556		if (VTOHFS(ap->a_vp)->hfs_flags & HFS_CASE_SENSITIVE)
5557			*ap->a_retval = 1;
5558		else
5559			*ap->a_retval = 0;
5560		break;
5561	case _PC_CASE_PRESERVING:
5562		*ap->a_retval = 1;
5563		break;
5564	case _PC_FILESIZEBITS:
5565		/* number of bits to store max file size */
5566		if (std_hfs == 0) {
5567			*ap->a_retval = 64;
5568		}
5569#if CONFIG_HFS_STD
5570		else {
5571			*ap->a_retval = 32;
5572		}
5573#endif
5574		break;
5575	case _PC_XATTR_SIZE_BITS:
5576		/* Number of bits to store maximum extended attribute size */
5577		*ap->a_retval = HFS_XATTR_SIZE_BITS;
5578		break;
5579	default:
5580		return (EINVAL);
5581	}
5582
5583	return (0);
5584}
5585
5586
5587/*
5588 * Update a cnode's on-disk metadata.
5589 *
5590 * If waitfor is set, then wait for the disk write of
5591 * the node to complete.
5592 *
5593 * The cnode must be locked exclusive
5594 */
5595int
5596hfs_update(struct vnode *vp, __unused int waitfor)
5597{
5598	struct cnode *cp = VTOC(vp);
5599	struct proc *p;
5600	struct cat_fork *dataforkp = NULL;
5601	struct cat_fork *rsrcforkp = NULL;
5602	struct cat_fork datafork;
5603	struct cat_fork rsrcfork;
5604	struct hfsmount *hfsmp;
5605	int lockflags;
5606	int error;
5607	uint32_t tstate = 0;
5608
5609	p = current_proc();
5610	hfsmp = VTOHFS(vp);
5611
5612	if (((vnode_issystem(vp) && (cp->c_cnid < kHFSFirstUserCatalogNodeID))) ||
5613	   	hfsmp->hfs_catalog_vp == NULL){
5614		return (0);
5615	}
5616	if ((hfsmp->hfs_flags & HFS_READ_ONLY) || (cp->c_mode == 0)) {
5617		cp->c_flag &= ~C_MODIFIED;
5618		cp->c_touch_acctime = 0;
5619		cp->c_touch_chgtime = 0;
5620		cp->c_touch_modtime = 0;
5621		return (0);
5622	}
5623	if (kdebug_enable) {
5624		if (cp->c_touch_acctime)
5625			tstate |= DBG_HFS_UPDATE_ACCTIME;
5626		if (cp->c_touch_modtime)
5627			tstate |= DBG_HFS_UPDATE_MODTIME;
5628		if (cp->c_touch_chgtime)
5629			tstate |= DBG_HFS_UPDATE_CHGTIME;
5630
5631		if (cp->c_flag & C_MODIFIED)
5632			tstate |= DBG_HFS_UPDATE_MODIFIED;
5633		if (cp->c_flag & C_FORCEUPDATE)
5634			tstate |= DBG_HFS_UPDATE_FORCE;
5635		if (cp->c_flag & C_NEEDS_DATEADDED)
5636			tstate |= DBG_HFS_UPDATE_DATEADDED;
5637	}
5638	hfs_touchtimes(hfsmp, cp);
5639
5640	/* Nothing to update. */
5641	if ((cp->c_flag & (C_MODIFIED | C_FORCEUPDATE)) == 0) {
5642		return (0);
5643	}
5644
5645	if (cp->c_datafork)
5646		dataforkp = &cp->c_datafork->ff_data;
5647	if (cp->c_rsrcfork)
5648		rsrcforkp = &cp->c_rsrcfork->ff_data;
5649
5650	/*
5651	 * For delayed allocations updates are
5652	 * postponed until an fsync or the file
5653	 * gets written to disk.
5654	 *
5655	 * Deleted files can defer meta data updates until inactive.
5656	 *
5657	 * If we're ever called with the C_FORCEUPDATE flag though
5658	 * we have to do the update.
5659	 */
5660	if (ISSET(cp->c_flag, C_FORCEUPDATE) == 0 &&
5661	    (ISSET(cp->c_flag, C_DELETED) ||
5662	    (dataforkp && cp->c_datafork->ff_unallocblocks) ||
5663	    (rsrcforkp && cp->c_rsrcfork->ff_unallocblocks))) {
5664	//	cp->c_flag &= ~(C_ACCESS | C_CHANGE | C_UPDATE);
5665		cp->c_flag |= C_MODIFIED;
5666
5667		return (0);
5668	}
5669
5670	KERNEL_DEBUG_CONSTANT(0x3018000 | DBG_FUNC_START, vp, tstate, 0, 0, 0);
5671
5672	if ((error = hfs_start_transaction(hfsmp)) != 0) {
5673
5674	    KERNEL_DEBUG_CONSTANT(0x3018000 | DBG_FUNC_END, vp, tstate, error, -1, 0);
5675	    return error;
5676	}
5677
5678    /*
5679     * Modify the values passed to cat_update based on whether or not
5680     * the file has invalid ranges or borrowed blocks.
5681     */
5682    if (dataforkp) {
5683        off_t numbytes = 0;
5684
5685        /* copy the datafork into a temporary copy so we don't pollute the cnode's */
5686        bcopy(dataforkp, &datafork, sizeof(datafork));
5687        dataforkp = &datafork;
5688
5689        /*
5690         * If there are borrowed blocks, ensure that they are subtracted
5691         * from the total block count before writing the cnode entry to disk.
5692         * Only extents that have actually been marked allocated in the bitmap
5693         * should be reflected in the total block count for this fork.
5694         */
5695        if (cp->c_datafork->ff_unallocblocks != 0) {
5696            // make sure that we don't assign a negative block count
5697            if (cp->c_datafork->ff_blocks < cp->c_datafork->ff_unallocblocks) {
5698                panic("hfs: ff_blocks %d is less than unalloc blocks %d\n",
5699                        cp->c_datafork->ff_blocks, cp->c_datafork->ff_unallocblocks);
5700            }
5701
5702            /* Also cap the LEOF to the total number of bytes that are allocated. */
5703            datafork.cf_blocks = (cp->c_datafork->ff_blocks - cp->c_datafork->ff_unallocblocks);
5704            datafork.cf_size   = datafork.cf_blocks * HFSTOVCB(hfsmp)->blockSize;
5705        }
5706
5707        /*
5708         * For files with invalid ranges (holes) the on-disk
5709         * field representing the size of the file (cf_size)
5710         * must be no larger than the start of the first hole.
5711         * However, note that if the first invalid range exists
5712         * solely within borrowed blocks, then our LEOF and block
5713         * count should both be zero.  As a result, set it to the
5714         * min of the current cf_size and the start of the first
5715         * invalid range, because it may have already been reduced
5716         * to zero by the borrowed blocks check above.
5717         */
5718        if (!TAILQ_EMPTY(&cp->c_datafork->ff_invalidranges))  {
5719            numbytes = TAILQ_FIRST(&cp->c_datafork->ff_invalidranges)->rl_start;
5720            datafork.cf_size = MIN((numbytes), (datafork.cf_size));
5721        }
5722    }
5723
5724	/*
5725	 * For resource forks with delayed allocations, make sure
5726	 * the block count and file size match the number of blocks
5727	 * actually allocated to the file on disk.
5728	 */
5729	if (rsrcforkp && (cp->c_rsrcfork->ff_unallocblocks != 0)) {
5730		bcopy(rsrcforkp, &rsrcfork, sizeof(rsrcfork));
5731		rsrcfork.cf_blocks = (cp->c_rsrcfork->ff_blocks - cp->c_rsrcfork->ff_unallocblocks);
5732		rsrcfork.cf_size   = rsrcfork.cf_blocks * HFSTOVCB(hfsmp)->blockSize;
5733		rsrcforkp = &rsrcfork;
5734	}
5735	if (kdebug_enable) {
5736		long dbg_parms[NUMPARMS];
5737		int  dbg_namelen;
5738
5739		dbg_namelen = NUMPARMS * sizeof(long);
5740		vn_getpath(vp, (char *)dbg_parms, &dbg_namelen);
5741
5742		if (dbg_namelen < (int)sizeof(dbg_parms))
5743			memset((char *)dbg_parms + dbg_namelen, 0, sizeof(dbg_parms) - dbg_namelen);
5744
5745		kdebug_lookup_gen_events(dbg_parms, dbg_namelen, (void *)vp, TRUE);
5746	}
5747
5748	/*
5749	 * Lock the Catalog b-tree file.
5750	 */
5751	lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_EXCLUSIVE_LOCK);
5752
5753	/* XXX - waitfor is not enforced */
5754	error = cat_update(hfsmp, &cp->c_desc, &cp->c_attr, dataforkp, rsrcforkp);
5755
5756	hfs_systemfile_unlock(hfsmp, lockflags);
5757
5758	/* After the updates are finished, clear the flags */
5759	cp->c_flag &= ~(C_MODIFIED | C_FORCEUPDATE);
5760
5761	hfs_end_transaction(hfsmp);
5762
5763	KERNEL_DEBUG_CONSTANT(0x3018000 | DBG_FUNC_END, vp, tstate, error, 0, 0);
5764
5765	return (error);
5766}
5767
5768/*
5769 * Allocate a new node
5770 * Note - Function does not create and return a vnode for whiteout creation.
5771 */
5772int
5773hfs_makenode(struct vnode *dvp, struct vnode **vpp, struct componentname *cnp,
5774             struct vnode_attr *vap, vfs_context_t ctx)
5775{
5776	struct cnode *cp = NULL;
5777	struct cnode *dcp = NULL;
5778	struct vnode *tvp;
5779	struct hfsmount *hfsmp;
5780	struct cat_desc in_desc, out_desc;
5781	struct cat_attr attr;
5782	struct timeval tv;
5783	int lockflags;
5784	int error, started_tr = 0;
5785	enum vtype vnodetype;
5786	int mode;
5787	int newvnode_flags = 0;
5788	u_int32_t gnv_flags = 0;
5789	int protectable_target = 0;
5790	int nocache = 0;
5791
5792#if CONFIG_PROTECT
5793	struct cprotect *entry = NULL;
5794	int32_t cp_class = -1;
5795	if (VATTR_IS_ACTIVE(vap, va_dataprotect_class)) {
5796		cp_class = (int32_t)vap->va_dataprotect_class;
5797	}
5798	int protected_mount = 0;
5799#endif
5800
5801
5802	if ((error = hfs_lock(VTOC(dvp), HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT)))
5803		return (error);
5804
5805	/* set the cnode pointer only after successfully acquiring lock */
5806	dcp = VTOC(dvp);
5807
5808	/* Don't allow creation of new entries in open-unlinked directories */
5809	if ((error = hfs_checkdeleted(dcp))) {
5810		hfs_unlock(dcp);
5811		return error;
5812	}
5813
5814	dcp->c_flag |= C_DIR_MODIFICATION;
5815
5816	hfsmp = VTOHFS(dvp);
5817
5818	*vpp = NULL;
5819	tvp = NULL;
5820	out_desc.cd_flags = 0;
5821	out_desc.cd_nameptr = NULL;
5822
5823	vnodetype = vap->va_type;
5824	if (vnodetype == VNON)
5825		vnodetype = VREG;
5826	mode = MAKEIMODE(vnodetype, vap->va_mode);
5827
5828	if (S_ISDIR (mode) || S_ISREG (mode)) {
5829		protectable_target = 1;
5830	}
5831
5832
5833	/* Check if were out of usable disk space. */
5834	if ((hfs_freeblks(hfsmp, 1) == 0) && (vfs_context_suser(ctx) != 0)) {
5835		error = ENOSPC;
5836		goto exit;
5837	}
5838
5839	microtime(&tv);
5840
5841	/* Setup the default attributes */
5842	bzero(&attr, sizeof(attr));
5843	attr.ca_mode = mode;
5844	attr.ca_linkcount = 1;
5845	if (VATTR_IS_ACTIVE(vap, va_rdev)) {
5846		attr.ca_rdev = vap->va_rdev;
5847	}
5848	if (VATTR_IS_ACTIVE(vap, va_create_time)) {
5849		VATTR_SET_SUPPORTED(vap, va_create_time);
5850		attr.ca_itime = vap->va_create_time.tv_sec;
5851	} else {
5852		attr.ca_itime = tv.tv_sec;
5853	}
5854#if CONFIG_HFS_STD
5855	if ((hfsmp->hfs_flags & HFS_STANDARD) && gTimeZone.tz_dsttime) {
5856		attr.ca_itime += 3600;	/* Same as what hfs_update does */
5857	}
5858#endif
5859	attr.ca_atime = attr.ca_ctime = attr.ca_mtime = attr.ca_itime;
5860	attr.ca_atimeondisk = attr.ca_atime;
5861	if (VATTR_IS_ACTIVE(vap, va_flags)) {
5862		VATTR_SET_SUPPORTED(vap, va_flags);
5863		attr.ca_flags = vap->va_flags;
5864	}
5865
5866	/*
5867	 * HFS+ only: all files get ThreadExists
5868	 * HFSX only: dirs get HasFolderCount
5869	 */
5870	if (!(hfsmp->hfs_flags & HFS_STANDARD)) {
5871		if (vnodetype == VDIR) {
5872			if (hfsmp->hfs_flags & HFS_FOLDERCOUNT)
5873				attr.ca_recflags = kHFSHasFolderCountMask;
5874		} else {
5875			attr.ca_recflags = kHFSThreadExistsMask;
5876		}
5877	}
5878
5879#if CONFIG_PROTECT
5880	if (cp_fs_protected(hfsmp->hfs_mp)) {
5881		protected_mount = 1;
5882	}
5883	/*
5884	 * On a content-protected HFS+/HFSX filesystem, files and directories
5885	 * cannot be created without atomically setting/creating the EA that
5886	 * contains the protection class metadata and keys at the same time, in
5887	 * the same transaction.  As a result, pre-set the "EAs exist" flag
5888	 * on the cat_attr for protectable catalog record creations.  This will
5889	 * cause the cnode creation routine in hfs_getnewvnode to mark the cnode
5890	 * as having EAs.
5891	 */
5892	if ((protected_mount) && (protectable_target)) {
5893		attr.ca_recflags |= kHFSHasAttributesMask;
5894		/* delay entering in the namecache */
5895		nocache = 1;
5896	}
5897#endif
5898
5899
5900	/*
5901	 * Add the date added to the item. See above, as
5902	 * all of the dates are set to the itime.
5903	 */
5904	hfs_write_dateadded (&attr, attr.ca_atime);
5905
5906	/* Initialize the gen counter to 1 */
5907	hfs_write_gencount(&attr, (uint32_t)1);
5908
5909	attr.ca_uid = vap->va_uid;
5910	attr.ca_gid = vap->va_gid;
5911	VATTR_SET_SUPPORTED(vap, va_mode);
5912	VATTR_SET_SUPPORTED(vap, va_uid);
5913	VATTR_SET_SUPPORTED(vap, va_gid);
5914
5915#if QUOTA
5916	/* check to see if this node's creation would cause us to go over
5917	 * quota.  If so, abort this operation.
5918	 */
5919   	if (hfsmp->hfs_flags & HFS_QUOTAS) {
5920		if ((error = hfs_quotacheck(hfsmp, 1, attr.ca_uid, attr.ca_gid,
5921									vfs_context_ucred(ctx)))) {
5922			goto exit;
5923		}
5924	}
5925#endif
5926
5927
5928	/* Tag symlinks with a type and creator. */
5929	if (vnodetype == VLNK) {
5930		struct FndrFileInfo *fip;
5931
5932		fip = (struct FndrFileInfo *)&attr.ca_finderinfo;
5933		fip->fdType    = SWAP_BE32(kSymLinkFileType);
5934		fip->fdCreator = SWAP_BE32(kSymLinkCreator);
5935	}
5936	if (cnp->cn_flags & ISWHITEOUT)
5937		attr.ca_flags |= UF_OPAQUE;
5938
5939	/* Setup the descriptor */
5940	in_desc.cd_nameptr = (const u_int8_t *)cnp->cn_nameptr;
5941	in_desc.cd_namelen = cnp->cn_namelen;
5942	in_desc.cd_parentcnid = dcp->c_fileid;
5943	in_desc.cd_flags = S_ISDIR(mode) ? CD_ISDIR : 0;
5944	in_desc.cd_hint = dcp->c_childhint;
5945	in_desc.cd_encoding = 0;
5946
5947#if CONFIG_PROTECT
5948	/*
5949	 * To preserve file creation atomicity with regards to the content protection EA,
5950	 * we must create the file in the catalog and then write out its EA in the same
5951	 * transaction.
5952	 *
5953	 * We only denote the target class in this EA; key generation is not completed
5954	 * until the file has been inserted into the catalog and will be done
5955	 * in a separate transaction.
5956	 */
5957	if ((protected_mount) && (protectable_target)) {
5958		error = cp_setup_newentry(hfsmp, dcp, cp_class, attr.ca_mode, &entry);
5959		if (error) {
5960			goto exit;
5961		}
5962	}
5963#endif
5964
5965	if ((error = hfs_start_transaction(hfsmp)) != 0) {
5966	    goto exit;
5967	}
5968	started_tr = 1;
5969
5970	// have to also lock the attribute file because cat_create() needs
5971	// to check that any fileID it wants to use does not have orphaned
5972	// attributes in it.
5973	lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG | SFL_ATTRIBUTE, HFS_EXCLUSIVE_LOCK);
5974	cnid_t new_id;
5975
5976	/* Reserve some space in the Catalog file. */
5977	if ((error = cat_preflight(hfsmp, CAT_CREATE, NULL, 0))) {
5978		hfs_systemfile_unlock(hfsmp, lockflags);
5979		goto exit;
5980	}
5981
5982	if ((error = cat_acquire_cnid(hfsmp, &new_id))) {
5983		hfs_systemfile_unlock (hfsmp, lockflags);
5984		goto exit;
5985	}
5986
5987	error = cat_create(hfsmp, new_id, &in_desc, &attr, &out_desc);
5988	if (error == 0) {
5989		/* Update the parent directory */
5990		dcp->c_childhint = out_desc.cd_hint;	/* Cache directory's location */
5991		dcp->c_entries++;
5992		{
5993			struct FndrExtendedDirInfo *extinfo = (struct FndrExtendedDirInfo *)((u_int8_t*)dcp->c_finderinfo + 16);
5994			extinfo->write_gen_counter = OSSwapHostToBigInt32(OSSwapBigToHostInt32(extinfo->write_gen_counter) + 1);
5995		}
5996		if (vnodetype == VDIR) {
5997			INC_FOLDERCOUNT(hfsmp, dcp->c_attr);
5998		}
5999		dcp->c_dirchangecnt++;
6000		{
6001			struct FndrExtendedDirInfo *extinfo = (struct FndrExtendedDirInfo *)((u_int8_t*)dcp->c_finderinfo + 16);
6002			extinfo->write_gen_counter = OSSwapHostToBigInt32(OSSwapBigToHostInt32(extinfo->write_gen_counter) + 1);
6003		}
6004		dcp->c_ctime = tv.tv_sec;
6005		dcp->c_mtime = tv.tv_sec;
6006		(void) cat_update(hfsmp, &dcp->c_desc, &dcp->c_attr, NULL, NULL);
6007
6008#if CONFIG_PROTECT
6009		/*
6010		 * If we are creating a content protected file, now is when
6011		 * we create the EA. We must create it in the same transaction
6012		 * that creates the file.  We can also guarantee that the file
6013		 * MUST exist because we are still holding the catalog lock
6014		 * at this point.
6015		 */
6016		if ((attr.ca_fileid != 0) && (protected_mount) && (protectable_target)) {
6017			error = cp_setxattr (NULL, entry, hfsmp, attr.ca_fileid, XATTR_CREATE);
6018
6019			if (error) {
6020				int delete_err;
6021				/*
6022				 * If we fail the EA creation, then we need to delete the file.
6023				 * Luckily, we are still holding all of the right locks.
6024				 */
6025				delete_err = cat_delete (hfsmp, &out_desc, &attr);
6026				if (delete_err == 0) {
6027					/* Update the parent directory */
6028					if (dcp->c_entries > 0)
6029						dcp->c_entries--;
6030					dcp->c_dirchangecnt++;
6031					dcp->c_ctime = tv.tv_sec;
6032					dcp->c_mtime = tv.tv_sec;
6033					(void) cat_update(hfsmp, &dcp->c_desc, &dcp->c_attr, NULL, NULL);
6034				}
6035
6036				/* Emit EINVAL if we fail to create EA*/
6037				error = EINVAL;
6038			}
6039		}
6040#endif
6041	}
6042	hfs_systemfile_unlock(hfsmp, lockflags);
6043	if (error)
6044		goto exit;
6045
6046	/* Invalidate negative cache entries in the directory */
6047	if (dcp->c_flag & C_NEG_ENTRIES) {
6048		cache_purge_negatives(dvp);
6049		dcp->c_flag &= ~C_NEG_ENTRIES;
6050	}
6051
6052	hfs_volupdate(hfsmp, vnodetype == VDIR ? VOL_MKDIR : VOL_MKFILE,
6053		(dcp->c_cnid == kHFSRootFolderID));
6054
6055	// XXXdbg
6056	// have to end the transaction here before we call hfs_getnewvnode()
6057	// because that can cause us to try and reclaim a vnode on a different
6058	// file system which could cause us to start a transaction which can
6059	// deadlock with someone on that other file system (since we could be
6060	// holding two transaction locks as well as various vnodes and we did
6061	// not obtain the locks on them in the proper order).
6062	//
6063	// NOTE: this means that if the quota check fails or we have to update
6064	//       the change time on a block-special device that those changes
6065	//       will happen as part of independent transactions.
6066	//
6067	if (started_tr) {
6068	    hfs_end_transaction(hfsmp);
6069	    started_tr = 0;
6070	}
6071
6072#if CONFIG_PROTECT
6073	/*
6074	 * At this point, we must have encountered success with writing the EA.
6075	 * Destroy our temporary cprotect (which had no keys).
6076	 */
6077
6078	if ((attr.ca_fileid != 0) && (protected_mount) && (protectable_target))  {
6079		cp_entry_destroy (entry);
6080		entry = NULL;
6081	}
6082#endif
6083
6084	/* Do not create vnode for whiteouts */
6085	if (S_ISWHT(mode)) {
6086		goto exit;
6087	}
6088
6089	gnv_flags |= GNV_CREATE;
6090	if (nocache) {
6091		gnv_flags |= GNV_NOCACHE;
6092	}
6093
6094	/*
6095	 * Create a vnode for the object just created.
6096	 *
6097	 * NOTE: Maintaining the cnode lock on the parent directory is important,
6098	 * as it prevents race conditions where other threads want to look up entries
6099	 * in the directory and/or add things as we are in the process of creating
6100	 * the vnode below.  However, this has the potential for causing a
6101	 * double lock panic when dealing with shadow files on a HFS boot partition.
6102	 * The panic could occur if we are not cleaning up after ourselves properly
6103	 * when done with a shadow file or in the error cases.  The error would occur if we
6104	 * try to create a new vnode, and then end up reclaiming another shadow vnode to
6105	 * create the new one.  However, if everything is working properly, this should
6106	 * be a non-issue as we would never enter that reclaim codepath.
6107	 *
6108	 * The cnode is locked on successful return.
6109	 */
6110	error = hfs_getnewvnode(hfsmp, dvp, cnp, &out_desc, gnv_flags, &attr,
6111							NULL, &tvp, &newvnode_flags);
6112	if (error)
6113		goto exit;
6114
6115	cp = VTOC(tvp);
6116
6117	struct  doc_tombstone *ut;
6118	ut = get_uthread_doc_tombstone();
6119	if (   ut->t_lastop_document_id != 0
6120	    && ut->t_lastop_parent == dvp
6121	    && ut->t_lastop_parent_vid == vnode_vid(dvp)
6122	    && strcmp((char *)ut->t_lastop_filename, (char *)cp->c_desc.cd_nameptr) == 0) {
6123		struct FndrExtendedDirInfo *fip = (struct FndrExtendedDirInfo *)((char *)&cp->c_attr.ca_finderinfo + 16);
6124
6125		//printf("CREATE: preserving doc-id %lld on %s\n", ut->t_lastop_document_id, ut->t_lastop_filename);
6126		fip->document_id = (uint32_t)(ut->t_lastop_document_id & 0xffffffff);
6127
6128		cp->c_bsdflags |= UF_TRACKED;
6129		// mark the cnode dirty
6130		cp->c_flag |= C_MODIFIED | C_FORCEUPDATE;
6131
6132		if ((error = hfs_start_transaction(hfsmp)) == 0) {
6133			lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_EXCLUSIVE_LOCK);
6134
6135			(void) cat_update(hfsmp, &cp->c_desc, &cp->c_attr, NULL, NULL);
6136
6137			hfs_systemfile_unlock (hfsmp, lockflags);
6138			(void) hfs_end_transaction(hfsmp);
6139		}
6140
6141		clear_tombstone_docid(ut, hfsmp, cp);       // will send the docid-changed fsevent
6142	} else if (ut->t_lastop_document_id != 0) {
6143		int len = cnp->cn_namelen;
6144		if (len == 0) {
6145			len = strlen(cnp->cn_nameptr);
6146		}
6147
6148		if (is_ignorable_temp_name(cnp->cn_nameptr, cnp->cn_namelen)) {
6149			// printf("CREATE: not clearing tombstone because %s is a temp name.\n", cnp->cn_nameptr);
6150		} else {
6151			// Clear the tombstone because the thread is not recreating the same path
6152			// printf("CREATE: clearing tombstone because %s is NOT a temp name.\n", cnp->cn_nameptr);
6153			clear_tombstone_docid(ut, hfsmp, NULL);
6154		}
6155	}
6156
6157	*vpp = tvp;
6158
6159#if CONFIG_PROTECT
6160	/*
6161	 * Now that we have a vnode-in-hand, generate keys for this namespace item.
6162	 * If we fail to create the keys, then attempt to delete the item from the
6163	 * namespace.  If we can't delete the item, that's not desirable but also not fatal..
6164	 *  All of the places which deal with restoring/unwrapping keys must also be
6165	 * prepared to encounter an entry that does not have keys.
6166	 */
6167	if ((protectable_target) && (protected_mount)) {
6168		struct cprotect *keyed_entry = NULL;
6169
6170		if (cp->c_cpentry == NULL) {
6171			panic ("hfs_makenode: no cpentry for cnode (%p)", cp);
6172		}
6173
6174		error = cp_generate_keys (hfsmp, cp, cp->c_cpentry->cp_pclass, &keyed_entry);
6175		if (error == 0) {
6176			/*
6177			 * Upon success, the keys were generated and written out.
6178			 * Update the cp pointer in the cnode.
6179			 */
6180			cp_replace_entry (cp, keyed_entry);
6181			if (nocache) {
6182				cache_enter (dvp, tvp, cnp);
6183			}
6184		}
6185		else {
6186			/* If key creation OR the setxattr failed, emit EPERM to userland */
6187			error = EPERM;
6188
6189			/*
6190			 * Beware! This slightly violates the lock ordering for the
6191			 * cnode/vnode 'tvp'.  Ordinarily, you must acquire the truncate lock
6192			 * which guards file size changes before acquiring the normal cnode lock
6193			 * and calling hfs_removefile on an item.
6194			 *
6195			 * However, in this case, we are still holding the directory lock so
6196			 * 'tvp' is not lookup-able and it was a newly created vnode so it
6197			 * cannot have any content yet. The only reason we are initiating
6198			 * the removefile is because we could not generate content protection keys
6199			 * for this namespace item. Note also that we pass a '1' in the allow_dirs
6200			 * argument for hfs_removefile because we may be creating a directory here.
6201			 *
6202			 * All this to say that while it is technically a violation it is
6203			 * impossible to race with another thread for this cnode so it is safe.
6204			 */
6205			int err = hfs_removefile (dvp, tvp, cnp, 0, 0, 1, NULL, 0);
6206			if (err) {
6207				printf("hfs_makenode: removefile failed (%d) for CP entry %p\n", err, tvp);
6208			}
6209
6210			/* Release the cnode lock and mark the vnode for termination */
6211			hfs_unlock (cp);
6212			err = vnode_recycle (tvp);
6213			if (err) {
6214				printf("hfs_makenode: vnode_recycle failed (%d) for CP entry %p\n", err, tvp);
6215			}
6216
6217			/* Drop the iocount on the new vnode to force reclamation/recycling */
6218			vnode_put (tvp);
6219			cp = NULL;
6220			*vpp = NULL;
6221		}
6222	}
6223#endif
6224
6225#if QUOTA
6226	/*
6227	 * Once we create this vnode, we need to initialize its quota data
6228	 * structures, if necessary.  We know that it is OK to just go ahead and
6229	 * initialize because we've already validated earlier (through the hfs_quotacheck
6230	 * function) to see if creating this cnode/vnode would cause us to go over quota.
6231	 */
6232	if (hfsmp->hfs_flags & HFS_QUOTAS) {
6233		if (cp) {
6234			/* cp could have been zeroed earlier */
6235			(void) hfs_getinoquota(cp);
6236		}
6237	}
6238#endif
6239
6240exit:
6241	cat_releasedesc(&out_desc);
6242
6243#if CONFIG_PROTECT
6244	/*
6245	 * We may have jumped here in error-handling various situations above.
6246	 * If we haven't already dumped the temporary CP used to initialize
6247	 * the file atomically, then free it now. cp_entry_destroy should null
6248	 * out the pointer if it was called already.
6249	 */
6250	if (entry) {
6251		cp_entry_destroy (entry);
6252		entry = NULL;
6253	}
6254#endif
6255
6256	/*
6257	 * Make sure we release cnode lock on dcp.
6258	 */
6259	if (dcp) {
6260		dcp->c_flag &= ~C_DIR_MODIFICATION;
6261		wakeup((caddr_t)&dcp->c_flag);
6262
6263		hfs_unlock(dcp);
6264	}
6265	if (error == 0 && cp != NULL) {
6266		hfs_unlock(cp);
6267	}
6268	if (started_tr) {
6269	    hfs_end_transaction(hfsmp);
6270	    started_tr = 0;
6271	}
6272
6273	return (error);
6274}
6275
6276
6277/*
6278 * hfs_vgetrsrc acquires a resource fork vnode corresponding to the cnode that is
6279 * found in 'vp'.  The rsrc fork vnode is returned with the cnode locked and iocount
6280 * on the rsrc vnode.
6281 *
6282 * *rvpp is an output argument for returning the pointer to the resource fork vnode.
6283 * In most cases, the resource fork vnode will not be set if we return an error.
6284 * However, if error_on_unlinked is set, we may have already acquired the resource fork vnode
6285 * before we discover the error (the file has gone open-unlinked).  In this case only,
6286 * we may return a vnode in the output argument despite an error.
6287 *
6288 * If can_drop_lock is set, then it is safe for this function to temporarily drop
6289 * and then re-acquire the cnode lock.  We may need to do this, for example, in order to
6290 * acquire an iocount or promote our lock.
6291 *
6292 * error_on_unlinked is an argument which indicates that we are to return an error if we
6293 * discover that the cnode has gone into an open-unlinked state ( C_DELETED or C_NOEXISTS)
6294 * is set in the cnode flags.  This is only necessary if can_drop_lock is true, otherwise
6295 * there's really no reason to double-check for errors on the cnode.
6296 */
6297
6298int
6299hfs_vgetrsrc(struct hfsmount *hfsmp, struct vnode *vp, struct vnode **rvpp,
6300		int can_drop_lock, int error_on_unlinked)
6301{
6302	struct vnode *rvp;
6303	struct vnode *dvp = NULLVP;
6304	struct cnode *cp = VTOC(vp);
6305	int error;
6306	int vid;
6307	int delete_status = 0;
6308
6309	if (vnode_vtype(vp) == VDIR) {
6310		return EINVAL;
6311	}
6312
6313	/*
6314	 * Need to check the status of the cnode to validate it hasn't gone
6315	 * open-unlinked on us before we can actually do work with it.
6316	 */
6317	delete_status = hfs_checkdeleted(cp);
6318	if ((delete_status) && (error_on_unlinked)) {
6319		return delete_status;
6320	}
6321
6322restart:
6323	/* Attempt to use existing vnode */
6324	if ((rvp = cp->c_rsrc_vp)) {
6325	        vid = vnode_vid(rvp);
6326
6327		/*
6328		 * It is not safe to hold the cnode lock when calling vnode_getwithvid()
6329		 * for the alternate fork -- vnode_getwithvid() could deadlock waiting
6330		 * for a VL_WANTTERM while another thread has an iocount on the alternate
6331		 * fork vnode and is attempting to acquire the common cnode lock.
6332		 *
6333		 * But it's also not safe to drop the cnode lock when we're holding
6334		 * multiple cnode locks, like during a hfs_removefile() operation
6335		 * since we could lock out of order when re-acquiring the cnode lock.
6336		 *
6337		 * So we can only drop the lock here if its safe to drop it -- which is
6338		 * most of the time with the exception being hfs_removefile().
6339		 */
6340		if (can_drop_lock)
6341			hfs_unlock(cp);
6342
6343		error = vnode_getwithvid(rvp, vid);
6344
6345		if (can_drop_lock) {
6346			(void) hfs_lock(cp, HFS_EXCLUSIVE_LOCK, HFS_LOCK_ALLOW_NOEXISTS);
6347
6348			/*
6349			 * When we relinquished our cnode lock, the cnode could have raced
6350			 * with a delete and gotten deleted.  If the caller did not want
6351			 * us to ignore open-unlinked files, then re-check the C_DELETED
6352			 * state and see if we need to return an ENOENT here because the item
6353			 * got deleted in the intervening time.
6354			 */
6355			if (error_on_unlinked) {
6356				if ((delete_status = hfs_checkdeleted(cp))) {
6357					/*
6358					 * If error == 0, this means that we succeeded in acquiring an iocount on the
6359					 * rsrc fork vnode.  However, if we're in this block of code, that means that we noticed
6360					 * that the cnode has gone open-unlinked.  In this case, the caller requested that we
6361					 * not do any other work and return an errno.  The caller will be responsible for
6362					 * dropping the iocount we just acquired because we can't do it until we've released
6363					 * the cnode lock.
6364					 */
6365					if (error == 0) {
6366						*rvpp = rvp;
6367					}
6368					return delete_status;
6369				}
6370			}
6371
6372			/*
6373			 * When our lock was relinquished, the resource fork
6374			 * could have been recycled.  Check for this and try
6375			 * again.
6376			 */
6377			if (error == ENOENT)
6378				goto restart;
6379		}
6380		if (error) {
6381			const char * name = (const char *)VTOC(vp)->c_desc.cd_nameptr;
6382
6383			if (name)
6384				printf("hfs_vgetrsrc: couldn't get resource"
6385				       " fork for %s, vol=%s, err=%d\n", name, hfsmp->vcbVN, error);
6386			return (error);
6387		}
6388	} else {
6389		struct cat_fork rsrcfork;
6390		struct componentname cn;
6391		struct cat_desc *descptr = NULL;
6392		struct cat_desc to_desc;
6393		char delname[32];
6394		int lockflags;
6395		int newvnode_flags = 0;
6396
6397		/*
6398		 * Make sure cnode lock is exclusive, if not upgrade it.
6399		 *
6400		 * We assume that we were called from a read-only VNOP (getattr)
6401		 * and that its safe to have the cnode lock dropped and reacquired.
6402		 */
6403		if (cp->c_lockowner != current_thread()) {
6404			if (!can_drop_lock) {
6405				return (EINVAL);
6406			}
6407			/*
6408			 * If the upgrade fails we lose the lock and
6409			 * have to take the exclusive lock on our own.
6410			 */
6411			if (lck_rw_lock_shared_to_exclusive(&cp->c_rwlock) == FALSE)
6412				lck_rw_lock_exclusive(&cp->c_rwlock);
6413			cp->c_lockowner = current_thread();
6414		}
6415
6416		/*
6417		 * hfs_vgetsrc may be invoked for a cnode that has already been marked
6418		 * C_DELETED.  This is because we need to continue to provide rsrc
6419		 * fork access to open-unlinked files.  In this case, build a fake descriptor
6420		 * like in hfs_removefile.  If we don't do this, buildkey will fail in
6421		 * cat_lookup because this cnode has no name in its descriptor. However,
6422		 * only do this if the caller did not specify that they wanted us to
6423		 * error out upon encountering open-unlinked files.
6424		 */
6425
6426		if ((error_on_unlinked) && (can_drop_lock)) {
6427			if ((error = hfs_checkdeleted(cp))) {
6428				return error;
6429			}
6430		}
6431
6432		if ((cp->c_flag & C_DELETED ) && (cp->c_desc.cd_namelen == 0)) {
6433			bzero (&to_desc, sizeof(to_desc));
6434			bzero (delname, 32);
6435			MAKE_DELETED_NAME(delname, sizeof(delname), cp->c_fileid);
6436			to_desc.cd_nameptr = (const u_int8_t*) delname;
6437			to_desc.cd_namelen = strlen(delname);
6438			to_desc.cd_parentcnid = hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid;
6439			to_desc.cd_flags = 0;
6440			to_desc.cd_cnid = cp->c_cnid;
6441
6442			descptr = &to_desc;
6443		}
6444		else {
6445			descptr = &cp->c_desc;
6446		}
6447
6448
6449		lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_SHARED_LOCK);
6450
6451		/*
6452		 * We call cat_idlookup (instead of cat_lookup) below because we can't
6453		 * trust the descriptor in the provided cnode for lookups at this point.
6454		 * Between the time of the original lookup of this vnode and now, the
6455		 * descriptor could have gotten swapped or replaced.  If this occurred,
6456		 * the parent/name combo originally desired may not necessarily be provided
6457		 * if we use the descriptor.  Even worse, if the vnode represents
6458		 * a hardlink, we could have removed one of the links from the namespace
6459		 * but left the descriptor alone, since hfs_unlink does not invalidate
6460		 * the descriptor in the cnode if other links still point to the inode.
6461		 *
6462		 * Consider the following (slightly contrived) scenario:
6463		 * /tmp/a <--> /tmp/b (hardlinks).
6464		 * 1. Thread A: open rsrc fork on /tmp/b.
6465		 * 1a. Thread A: does lookup, goes out to lunch right before calling getnamedstream.
6466		 * 2. Thread B does 'mv /foo/b /tmp/b'
6467		 * 2. Thread B succeeds.
6468		 * 3. Thread A comes back and wants rsrc fork info for /tmp/b.
6469		 *
6470		 * Even though the hardlink backing /tmp/b is now eliminated, the descriptor
6471		 * is not removed/updated during the unlink process.  So, if you were to
6472		 * do a lookup on /tmp/b, you'd acquire an entirely different record's resource
6473		 * fork.
6474		 *
6475		 * As a result, we use the fileid, which should be invariant for the lifetime
6476		 * of the cnode (possibly barring calls to exchangedata).
6477		 *
6478		 * Addendum: We can't do the above for HFS standard since we aren't guaranteed to
6479		 * have thread records for files.  They were only required for directories.  So
6480		 * we need to do the lookup with the catalog name. This is OK since hardlinks were
6481		 * never allowed on HFS standard.
6482		 */
6483
6484		/* Get resource fork data */
6485		if ((hfsmp->hfs_flags & HFS_STANDARD) == 0) {
6486			error = cat_idlookup (hfsmp, cp->c_fileid, 0, 1, NULL, NULL, &rsrcfork);
6487		}
6488#if CONFIG_HFS_STD
6489		else {
6490			/*
6491			 * HFS standard only:
6492			 *
6493			 * Get the resource fork for this item with a cat_lookup call, but do not
6494			 * force a case lookup since HFS standard is case-insensitive only. We
6495			 * don't want the descriptor; just the fork data here. If we tried to
6496			 * do a ID lookup (via thread record -> catalog record), then we might fail
6497			 * prematurely since, as noted above, thread records were not strictly required
6498			 * on files in HFS.
6499			 */
6500			error = cat_lookup (hfsmp, descptr, 1, 0, (struct cat_desc*)NULL,
6501					(struct cat_attr*)NULL, &rsrcfork, NULL);
6502		}
6503#endif
6504
6505		hfs_systemfile_unlock(hfsmp, lockflags);
6506		if (error) {
6507			return (error);
6508		}
6509		/*
6510		 * Supply hfs_getnewvnode with a component name.
6511		 */
6512		cn.cn_pnbuf = NULL;
6513		if (descptr->cd_nameptr) {
6514			MALLOC_ZONE(cn.cn_pnbuf, caddr_t, MAXPATHLEN, M_NAMEI, M_WAITOK);
6515			cn.cn_nameiop = LOOKUP;
6516			cn.cn_flags = ISLASTCN | HASBUF;
6517			cn.cn_context = NULL;
6518			cn.cn_pnlen = MAXPATHLEN;
6519			cn.cn_nameptr = cn.cn_pnbuf;
6520			cn.cn_hash = 0;
6521			cn.cn_consume = 0;
6522			cn.cn_namelen = snprintf(cn.cn_nameptr, MAXPATHLEN,
6523						 "%s%s", descptr->cd_nameptr,
6524						 _PATH_RSRCFORKSPEC);
6525		}
6526		dvp = vnode_getparent(vp);
6527		error = hfs_getnewvnode(hfsmp, dvp, cn.cn_pnbuf ? &cn : NULL,
6528		                        descptr, GNV_WANTRSRC | GNV_SKIPLOCK, &cp->c_attr,
6529		                        &rsrcfork, &rvp, &newvnode_flags);
6530		if (dvp)
6531			vnode_put(dvp);
6532		if (cn.cn_pnbuf)
6533			FREE_ZONE(cn.cn_pnbuf, cn.cn_pnlen, M_NAMEI);
6534		if (error)
6535			return (error);
6536	}
6537
6538	*rvpp = rvp;
6539	return (0);
6540}
6541
6542/*
6543 * Wrapper for special device reads
6544 */
6545int
6546hfsspec_read(ap)
6547	struct vnop_read_args /* {
6548		struct vnode *a_vp;
6549		struct uio *a_uio;
6550		int  a_ioflag;
6551		vfs_context_t a_context;
6552	} */ *ap;
6553{
6554	/*
6555	 * Set access flag.
6556	 */
6557	VTOC(ap->a_vp)->c_touch_acctime = TRUE;
6558	return (VOCALL (spec_vnodeop_p, VOFFSET(vnop_read), ap));
6559}
6560
6561/*
6562 * Wrapper for special device writes
6563 */
6564int
6565hfsspec_write(ap)
6566	struct vnop_write_args /* {
6567		struct vnode *a_vp;
6568		struct uio *a_uio;
6569		int  a_ioflag;
6570		vfs_context_t a_context;
6571	} */ *ap;
6572{
6573	/*
6574	 * Set update and change flags.
6575	 */
6576	VTOC(ap->a_vp)->c_touch_chgtime = TRUE;
6577	VTOC(ap->a_vp)->c_touch_modtime = TRUE;
6578	return (VOCALL (spec_vnodeop_p, VOFFSET(vnop_write), ap));
6579}
6580
6581/*
6582 * Wrapper for special device close
6583 *
6584 * Update the times on the cnode then do device close.
6585 */
6586int
6587hfsspec_close(ap)
6588	struct vnop_close_args /* {
6589		struct vnode *a_vp;
6590		int  a_fflag;
6591		vfs_context_t a_context;
6592	} */ *ap;
6593{
6594	struct vnode *vp = ap->a_vp;
6595	struct cnode *cp;
6596
6597	if (vnode_isinuse(ap->a_vp, 0)) {
6598		if (hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT) == 0) {
6599			cp = VTOC(vp);
6600			hfs_touchtimes(VTOHFS(vp), cp);
6601			hfs_unlock(cp);
6602		}
6603	}
6604	return (VOCALL (spec_vnodeop_p, VOFFSET(vnop_close), ap));
6605}
6606
6607#if FIFO
6608/*
6609 * Wrapper for fifo reads
6610 */
6611static int
6612hfsfifo_read(ap)
6613	struct vnop_read_args /* {
6614		struct vnode *a_vp;
6615		struct uio *a_uio;
6616		int  a_ioflag;
6617		vfs_context_t a_context;
6618	} */ *ap;
6619{
6620	/*
6621	 * Set access flag.
6622	 */
6623	VTOC(ap->a_vp)->c_touch_acctime = TRUE;
6624	return (VOCALL (fifo_vnodeop_p, VOFFSET(vnop_read), ap));
6625}
6626
6627/*
6628 * Wrapper for fifo writes
6629 */
6630static int
6631hfsfifo_write(ap)
6632	struct vnop_write_args /* {
6633		struct vnode *a_vp;
6634		struct uio *a_uio;
6635		int  a_ioflag;
6636		vfs_context_t a_context;
6637	} */ *ap;
6638{
6639	/*
6640	 * Set update and change flags.
6641	 */
6642	VTOC(ap->a_vp)->c_touch_chgtime = TRUE;
6643	VTOC(ap->a_vp)->c_touch_modtime = TRUE;
6644	return (VOCALL (fifo_vnodeop_p, VOFFSET(vnop_write), ap));
6645}
6646
6647/*
6648 * Wrapper for fifo close
6649 *
6650 * Update the times on the cnode then do device close.
6651 */
6652static int
6653hfsfifo_close(ap)
6654	struct vnop_close_args /* {
6655		struct vnode *a_vp;
6656		int  a_fflag;
6657		vfs_context_t a_context;
6658	} */ *ap;
6659{
6660	struct vnode *vp = ap->a_vp;
6661	struct cnode *cp;
6662
6663	if (vnode_isinuse(ap->a_vp, 1)) {
6664		if (hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT) == 0) {
6665			cp = VTOC(vp);
6666			hfs_touchtimes(VTOHFS(vp), cp);
6667			hfs_unlock(cp);
6668		}
6669	}
6670	return (VOCALL (fifo_vnodeop_p, VOFFSET(vnop_close), ap));
6671}
6672
6673
6674#endif /* FIFO */
6675
6676/*
6677 * Getter for the document_id
6678 * the document_id is stored in FndrExtendedFileInfo/FndrExtendedDirInfo
6679 */
6680static u_int32_t
6681hfs_get_document_id_internal(const uint8_t *finderinfo, mode_t mode)
6682{
6683	u_int8_t *finfo = NULL;
6684	u_int32_t doc_id = 0;
6685
6686	/* overlay the FinderInfo to the correct pointer, and advance */
6687	finfo = ((uint8_t *)finderinfo) + 16;
6688
6689	if (S_ISDIR(mode) || S_ISREG(mode)) {
6690		struct FndrExtendedFileInfo *extinfo = (struct FndrExtendedFileInfo *)finfo;
6691		doc_id = extinfo->document_id;
6692	} else if (S_ISDIR(mode)) {
6693		struct FndrExtendedDirInfo *extinfo = (struct FndrExtendedDirInfo *)((u_int8_t*)finderinfo + 16);
6694		doc_id = extinfo->document_id;
6695	}
6696
6697	return doc_id;
6698}
6699
6700
6701/* getter(s) for document id */
6702u_int32_t
6703hfs_get_document_id(struct cnode *cp)
6704{
6705	return (hfs_get_document_id_internal((u_int8_t*)cp->c_finderinfo,
6706	    cp->c_attr.ca_mode));
6707}
6708
6709/* If you have finderinfo and mode, you can use this */
6710u_int32_t
6711hfs_get_document_id_from_blob(const uint8_t *finderinfo, mode_t mode)
6712{
6713	return (hfs_get_document_id_internal(finderinfo, mode));
6714}
6715
6716/*
6717 * Synchronize a file's in-core state with that on disk.
6718 */
6719int
6720hfs_vnop_fsync(ap)
6721	struct vnop_fsync_args /* {
6722		struct vnode *a_vp;
6723		int a_waitfor;
6724		vfs_context_t a_context;
6725	} */ *ap;
6726{
6727	struct vnode* vp = ap->a_vp;
6728	int error;
6729
6730	/* Note: We check hfs flags instead of vfs mount flag because during
6731	 * read-write update, hfs marks itself read-write much earlier than
6732	 * the vfs, and hence won't result in skipping of certain writes like
6733	 * zero'ing out of unused nodes, creation of hotfiles btree, etc.
6734	 */
6735	if (VTOHFS(vp)->hfs_flags & HFS_READ_ONLY) {
6736		return 0;
6737	}
6738
6739#if CONFIG_PROTECT
6740	if ((error = cp_handle_vnop(vp, CP_WRITE_ACCESS, 0)) != 0) {
6741		return (error);
6742	}
6743#endif /* CONFIG_PROTECT */
6744
6745	/*
6746	 * We need to allow ENOENT lock errors since unlink
6747	 * systenm call can call VNOP_FSYNC during vclean.
6748	 */
6749	error = hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT);
6750	if (error)
6751		return (0);
6752
6753	error = hfs_fsync(vp, ap->a_waitfor, 0, vfs_context_proc(ap->a_context));
6754
6755	hfs_unlock(VTOC(vp));
6756	return (error);
6757}
6758
6759
6760int
6761hfs_vnop_whiteout(ap)
6762	struct vnop_whiteout_args /* {
6763		struct vnode *a_dvp;
6764		struct componentname *a_cnp;
6765		int a_flags;
6766		vfs_context_t a_context;
6767	} */ *ap;
6768{
6769	int error = 0;
6770	struct vnode *vp = NULL;
6771	struct vnode_attr va;
6772	struct vnop_lookup_args lookup_args;
6773	struct vnop_remove_args remove_args;
6774	struct hfsmount *hfsmp;
6775
6776	hfsmp = VTOHFS(ap->a_dvp);
6777	if (hfsmp->hfs_flags & HFS_STANDARD) {
6778		error = ENOTSUP;
6779		goto exit;
6780	}
6781
6782	switch (ap->a_flags) {
6783		case LOOKUP:
6784			error = 0;
6785			break;
6786
6787		case CREATE:
6788			VATTR_INIT(&va);
6789			VATTR_SET(&va, va_type, VREG);
6790			VATTR_SET(&va, va_mode, S_IFWHT);
6791			VATTR_SET(&va, va_uid, 0);
6792			VATTR_SET(&va, va_gid, 0);
6793
6794			error = hfs_makenode(ap->a_dvp, &vp, ap->a_cnp, &va, ap->a_context);
6795			/* No need to release the vnode as no vnode is created for whiteouts */
6796			break;
6797
6798		case DELETE:
6799			lookup_args.a_dvp = ap->a_dvp;
6800			lookup_args.a_vpp = &vp;
6801			lookup_args.a_cnp = ap->a_cnp;
6802			lookup_args.a_context = ap->a_context;
6803
6804			error = hfs_vnop_lookup(&lookup_args);
6805			if (error) {
6806				break;
6807			}
6808
6809			remove_args.a_dvp = ap->a_dvp;
6810			remove_args.a_vp = vp;
6811			remove_args.a_cnp = ap->a_cnp;
6812			remove_args.a_flags = 0;
6813			remove_args.a_context = ap->a_context;
6814
6815			error = hfs_vnop_remove(&remove_args);
6816			vnode_put(vp);
6817			break;
6818
6819		default:
6820			panic("hfs_vnop_whiteout: unknown operation (flag = %x)\n", ap->a_flags);
6821	};
6822
6823exit:
6824	return (error);
6825}
6826
6827int (**hfs_vnodeop_p)(void *);
6828
6829#define VOPFUNC int (*)(void *)
6830
6831
6832#if CONFIG_HFS_STD
6833int (**hfs_std_vnodeop_p) (void *);
6834static int hfs_readonly_op (__unused void* ap) { return (EROFS); }
6835
6836/*
6837 * In 10.6 and forward, HFS Standard is read-only and deprecated.  The vnop table below
6838 * is for use with HFS standard to block out operations that would modify the file system
6839 */
6840
6841struct vnodeopv_entry_desc hfs_standard_vnodeop_entries[] = {
6842    { &vnop_default_desc, (VOPFUNC)vn_default_error },
6843    { &vnop_lookup_desc, (VOPFUNC)hfs_vnop_lookup },		/* lookup */
6844    { &vnop_create_desc, (VOPFUNC)hfs_readonly_op },		/* create (READONLY) */
6845    { &vnop_mknod_desc, (VOPFUNC)hfs_readonly_op },             /* mknod (READONLY) */
6846    { &vnop_open_desc, (VOPFUNC)hfs_vnop_open },			/* open */
6847    { &vnop_close_desc, (VOPFUNC)hfs_vnop_close },		/* close */
6848    { &vnop_getattr_desc, (VOPFUNC)hfs_vnop_getattr },		/* getattr */
6849    { &vnop_setattr_desc, (VOPFUNC)hfs_readonly_op },		/* setattr */
6850    { &vnop_read_desc, (VOPFUNC)hfs_vnop_read },			/* read */
6851    { &vnop_write_desc, (VOPFUNC)hfs_readonly_op },		/* write (READONLY) */
6852    { &vnop_ioctl_desc, (VOPFUNC)hfs_vnop_ioctl },		/* ioctl */
6853    { &vnop_select_desc, (VOPFUNC)hfs_vnop_select },		/* select */
6854    { &vnop_revoke_desc, (VOPFUNC)nop_revoke },			/* revoke */
6855    { &vnop_exchange_desc, (VOPFUNC)hfs_readonly_op },		/* exchange  (READONLY)*/
6856    { &vnop_mmap_desc, (VOPFUNC)err_mmap },			/* mmap */
6857    { &vnop_fsync_desc, (VOPFUNC)hfs_readonly_op},		/* fsync (READONLY) */
6858    { &vnop_remove_desc, (VOPFUNC)hfs_readonly_op },		/* remove (READONLY) */
6859    { &vnop_link_desc, (VOPFUNC)hfs_readonly_op },			/* link ( READONLLY) */
6860    { &vnop_rename_desc, (VOPFUNC)hfs_readonly_op },		/* rename (READONLY)*/
6861    { &vnop_mkdir_desc, (VOPFUNC)hfs_readonly_op },             /* mkdir (READONLY) */
6862    { &vnop_rmdir_desc, (VOPFUNC)hfs_readonly_op },		/* rmdir (READONLY) */
6863    { &vnop_symlink_desc, (VOPFUNC)hfs_readonly_op },         /* symlink (READONLY) */
6864    { &vnop_readdir_desc, (VOPFUNC)hfs_vnop_readdir },		/* readdir */
6865    { &vnop_readdirattr_desc, (VOPFUNC)hfs_vnop_readdirattr },	/* readdirattr */
6866    { &vnop_readlink_desc, (VOPFUNC)hfs_vnop_readlink },		/* readlink */
6867    { &vnop_inactive_desc, (VOPFUNC)hfs_vnop_inactive },		/* inactive */
6868    { &vnop_reclaim_desc, (VOPFUNC)hfs_vnop_reclaim },		/* reclaim */
6869    { &vnop_strategy_desc, (VOPFUNC)hfs_vnop_strategy },		/* strategy */
6870    { &vnop_pathconf_desc, (VOPFUNC)hfs_vnop_pathconf },		/* pathconf */
6871    { &vnop_advlock_desc, (VOPFUNC)err_advlock },		/* advlock */
6872    { &vnop_allocate_desc, (VOPFUNC)hfs_readonly_op },		/* allocate (READONLY) */
6873#if CONFIG_SEARCHFS
6874    { &vnop_searchfs_desc, (VOPFUNC)hfs_vnop_search },		/* search fs */
6875#else
6876    { &vnop_searchfs_desc, (VOPFUNC)err_searchfs },		/* search fs */
6877#endif
6878    { &vnop_bwrite_desc, (VOPFUNC)hfs_readonly_op },		/* bwrite (READONLY) */
6879    { &vnop_pagein_desc, (VOPFUNC)hfs_vnop_pagein },		/* pagein */
6880    { &vnop_pageout_desc,(VOPFUNC) hfs_readonly_op },		/* pageout (READONLY)  */
6881    { &vnop_copyfile_desc, (VOPFUNC)hfs_readonly_op },		/* copyfile (READONLY)*/
6882    { &vnop_blktooff_desc, (VOPFUNC)hfs_vnop_blktooff },		/* blktooff */
6883    { &vnop_offtoblk_desc, (VOPFUNC)hfs_vnop_offtoblk },		/* offtoblk */
6884    { &vnop_blockmap_desc, (VOPFUNC)hfs_vnop_blockmap },			/* blockmap */
6885    { &vnop_getxattr_desc, (VOPFUNC)hfs_vnop_getxattr},
6886    { &vnop_setxattr_desc, (VOPFUNC)hfs_readonly_op},         /* set xattr (READONLY) */
6887    { &vnop_removexattr_desc, (VOPFUNC)hfs_readonly_op},      /* remove xattr (READONLY) */
6888    { &vnop_listxattr_desc, (VOPFUNC)hfs_vnop_listxattr},
6889    { &vnop_whiteout_desc, (VOPFUNC)hfs_readonly_op},       /* whiteout (READONLY) */
6890#if NAMEDSTREAMS
6891    { &vnop_getnamedstream_desc, (VOPFUNC)hfs_vnop_getnamedstream },
6892    { &vnop_makenamedstream_desc, (VOPFUNC)hfs_readonly_op },
6893    { &vnop_removenamedstream_desc, (VOPFUNC)hfs_readonly_op },
6894#endif
6895    { NULL, (VOPFUNC)NULL }
6896};
6897
6898struct vnodeopv_desc hfs_std_vnodeop_opv_desc =
6899{ &hfs_std_vnodeop_p, hfs_standard_vnodeop_entries };
6900#endif
6901
6902/* VNOP table for HFS+ */
6903struct vnodeopv_entry_desc hfs_vnodeop_entries[] = {
6904    { &vnop_default_desc, (VOPFUNC)vn_default_error },
6905    { &vnop_lookup_desc, (VOPFUNC)hfs_vnop_lookup },		/* lookup */
6906    { &vnop_create_desc, (VOPFUNC)hfs_vnop_create },		/* create */
6907    { &vnop_mknod_desc, (VOPFUNC)hfs_vnop_mknod },             /* mknod */
6908    { &vnop_open_desc, (VOPFUNC)hfs_vnop_open },			/* open */
6909    { &vnop_close_desc, (VOPFUNC)hfs_vnop_close },		/* close */
6910    { &vnop_getattr_desc, (VOPFUNC)hfs_vnop_getattr },		/* getattr */
6911    { &vnop_setattr_desc, (VOPFUNC)hfs_vnop_setattr },		/* setattr */
6912    { &vnop_read_desc, (VOPFUNC)hfs_vnop_read },			/* read */
6913    { &vnop_write_desc, (VOPFUNC)hfs_vnop_write },		/* write */
6914    { &vnop_ioctl_desc, (VOPFUNC)hfs_vnop_ioctl },		/* ioctl */
6915    { &vnop_select_desc, (VOPFUNC)hfs_vnop_select },		/* select */
6916    { &vnop_revoke_desc, (VOPFUNC)nop_revoke },			/* revoke */
6917    { &vnop_exchange_desc, (VOPFUNC)hfs_vnop_exchange },		/* exchange */
6918    { &vnop_mmap_desc, (VOPFUNC)hfs_vnop_mmap },			/* mmap */
6919    { &vnop_fsync_desc, (VOPFUNC)hfs_vnop_fsync },		/* fsync */
6920    { &vnop_remove_desc, (VOPFUNC)hfs_vnop_remove },		/* remove */
6921    { &vnop_link_desc, (VOPFUNC)hfs_vnop_link },			/* link */
6922    { &vnop_rename_desc, (VOPFUNC)hfs_vnop_rename },		/* rename */
6923    { &vnop_mkdir_desc, (VOPFUNC)hfs_vnop_mkdir },             /* mkdir */
6924    { &vnop_rmdir_desc, (VOPFUNC)hfs_vnop_rmdir },		/* rmdir */
6925    { &vnop_symlink_desc, (VOPFUNC)hfs_vnop_symlink },         /* symlink */
6926    { &vnop_readdir_desc, (VOPFUNC)hfs_vnop_readdir },		/* readdir */
6927    { &vnop_readdirattr_desc, (VOPFUNC)hfs_vnop_readdirattr },	/* readdirattr */
6928    { &vnop_readlink_desc, (VOPFUNC)hfs_vnop_readlink },		/* readlink */
6929    { &vnop_inactive_desc, (VOPFUNC)hfs_vnop_inactive },		/* inactive */
6930    { &vnop_reclaim_desc, (VOPFUNC)hfs_vnop_reclaim },		/* reclaim */
6931    { &vnop_strategy_desc, (VOPFUNC)hfs_vnop_strategy },		/* strategy */
6932    { &vnop_pathconf_desc, (VOPFUNC)hfs_vnop_pathconf },		/* pathconf */
6933    { &vnop_advlock_desc, (VOPFUNC)err_advlock },		/* advlock */
6934    { &vnop_allocate_desc, (VOPFUNC)hfs_vnop_allocate },		/* allocate */
6935#if CONFIG_SEARCHFS
6936    { &vnop_searchfs_desc, (VOPFUNC)hfs_vnop_search },		/* search fs */
6937#else
6938    { &vnop_searchfs_desc, (VOPFUNC)err_searchfs },		/* search fs */
6939#endif
6940    { &vnop_bwrite_desc, (VOPFUNC)hfs_vnop_bwrite },		/* bwrite */
6941    { &vnop_pagein_desc, (VOPFUNC)hfs_vnop_pagein },		/* pagein */
6942    { &vnop_pageout_desc,(VOPFUNC) hfs_vnop_pageout },		/* pageout */
6943    { &vnop_copyfile_desc, (VOPFUNC)err_copyfile },		/* copyfile */
6944    { &vnop_blktooff_desc, (VOPFUNC)hfs_vnop_blktooff },		/* blktooff */
6945    { &vnop_offtoblk_desc, (VOPFUNC)hfs_vnop_offtoblk },		/* offtoblk */
6946    { &vnop_blockmap_desc, (VOPFUNC)hfs_vnop_blockmap },			/* blockmap */
6947    { &vnop_getxattr_desc, (VOPFUNC)hfs_vnop_getxattr},
6948    { &vnop_setxattr_desc, (VOPFUNC)hfs_vnop_setxattr},
6949    { &vnop_removexattr_desc, (VOPFUNC)hfs_vnop_removexattr},
6950    { &vnop_listxattr_desc, (VOPFUNC)hfs_vnop_listxattr},
6951    { &vnop_whiteout_desc, (VOPFUNC)hfs_vnop_whiteout},
6952#if NAMEDSTREAMS
6953    { &vnop_getnamedstream_desc, (VOPFUNC)hfs_vnop_getnamedstream },
6954    { &vnop_makenamedstream_desc, (VOPFUNC)hfs_vnop_makenamedstream },
6955    { &vnop_removenamedstream_desc, (VOPFUNC)hfs_vnop_removenamedstream },
6956#endif
6957    { NULL, (VOPFUNC)NULL }
6958};
6959
6960struct vnodeopv_desc hfs_vnodeop_opv_desc =
6961{ &hfs_vnodeop_p, hfs_vnodeop_entries };
6962
6963
6964/* Spec Op vnop table for HFS+ */
6965int (**hfs_specop_p)(void *);
6966struct vnodeopv_entry_desc hfs_specop_entries[] = {
6967	{ &vnop_default_desc, (VOPFUNC)vn_default_error },
6968	{ &vnop_lookup_desc, (VOPFUNC)spec_lookup },		/* lookup */
6969	{ &vnop_create_desc, (VOPFUNC)spec_create },		/* create */
6970	{ &vnop_mknod_desc, (VOPFUNC)spec_mknod },              /* mknod */
6971	{ &vnop_open_desc, (VOPFUNC)spec_open },			/* open */
6972	{ &vnop_close_desc, (VOPFUNC)hfsspec_close },		/* close */
6973	{ &vnop_getattr_desc, (VOPFUNC)hfs_vnop_getattr },	/* getattr */
6974	{ &vnop_setattr_desc, (VOPFUNC)hfs_vnop_setattr },	/* setattr */
6975	{ &vnop_read_desc, (VOPFUNC)hfsspec_read },		/* read */
6976	{ &vnop_write_desc, (VOPFUNC)hfsspec_write },		/* write */
6977	{ &vnop_ioctl_desc, (VOPFUNC)spec_ioctl },		/* ioctl */
6978	{ &vnop_select_desc, (VOPFUNC)spec_select },		/* select */
6979	{ &vnop_revoke_desc, (VOPFUNC)spec_revoke },		/* revoke */
6980	{ &vnop_mmap_desc, (VOPFUNC)spec_mmap },			/* mmap */
6981	{ &vnop_fsync_desc, (VOPFUNC)hfs_vnop_fsync },		/* fsync */
6982	{ &vnop_remove_desc, (VOPFUNC)spec_remove },		/* remove */
6983	{ &vnop_link_desc, (VOPFUNC)spec_link },			/* link */
6984	{ &vnop_rename_desc, (VOPFUNC)spec_rename },		/* rename */
6985	{ &vnop_mkdir_desc, (VOPFUNC)spec_mkdir },              /* mkdir */
6986	{ &vnop_rmdir_desc, (VOPFUNC)spec_rmdir },		/* rmdir */
6987	{ &vnop_symlink_desc, (VOPFUNC)spec_symlink },          /* symlink */
6988	{ &vnop_readdir_desc, (VOPFUNC)spec_readdir },		/* readdir */
6989	{ &vnop_readlink_desc, (VOPFUNC)spec_readlink },		/* readlink */
6990	{ &vnop_inactive_desc, (VOPFUNC)hfs_vnop_inactive },	/* inactive */
6991	{ &vnop_reclaim_desc, (VOPFUNC)hfs_vnop_reclaim },	/* reclaim */
6992	{ &vnop_strategy_desc, (VOPFUNC)spec_strategy },		/* strategy */
6993	{ &vnop_pathconf_desc, (VOPFUNC)spec_pathconf },		/* pathconf */
6994	{ &vnop_advlock_desc, (VOPFUNC)err_advlock },		/* advlock */
6995	{ &vnop_bwrite_desc, (VOPFUNC)hfs_vnop_bwrite },
6996	{ &vnop_pagein_desc, (VOPFUNC)hfs_vnop_pagein },		/* Pagein */
6997	{ &vnop_pageout_desc, (VOPFUNC)hfs_vnop_pageout },	/* Pageout */
6998    { &vnop_copyfile_desc, (VOPFUNC)err_copyfile },		/* copyfile */
6999	{ &vnop_blktooff_desc, (VOPFUNC)hfs_vnop_blktooff },	/* blktooff */
7000	{ &vnop_offtoblk_desc, (VOPFUNC)hfs_vnop_offtoblk },	/* offtoblk */
7001	{ &vnop_getxattr_desc, (VOPFUNC)hfs_vnop_getxattr},
7002	{ &vnop_setxattr_desc, (VOPFUNC)hfs_vnop_setxattr},
7003	{ &vnop_removexattr_desc, (VOPFUNC)hfs_vnop_removexattr},
7004	{ &vnop_listxattr_desc, (VOPFUNC)hfs_vnop_listxattr},
7005	{ (struct vnodeop_desc*)NULL, (VOPFUNC)NULL }
7006};
7007struct vnodeopv_desc hfs_specop_opv_desc =
7008	{ &hfs_specop_p, hfs_specop_entries };
7009
7010#if FIFO
7011/* HFS+ FIFO VNOP table  */
7012int (**hfs_fifoop_p)(void *);
7013struct vnodeopv_entry_desc hfs_fifoop_entries[] = {
7014	{ &vnop_default_desc, (VOPFUNC)vn_default_error },
7015	{ &vnop_lookup_desc, (VOPFUNC)fifo_lookup },		/* lookup */
7016	{ &vnop_create_desc, (VOPFUNC)fifo_create },		/* create */
7017	{ &vnop_mknod_desc, (VOPFUNC)fifo_mknod },              /* mknod */
7018	{ &vnop_open_desc, (VOPFUNC)fifo_open },			/* open */
7019	{ &vnop_close_desc, (VOPFUNC)hfsfifo_close },		/* close */
7020	{ &vnop_getattr_desc, (VOPFUNC)hfs_vnop_getattr },	/* getattr */
7021	{ &vnop_setattr_desc, (VOPFUNC)hfs_vnop_setattr },	/* setattr */
7022	{ &vnop_read_desc, (VOPFUNC)hfsfifo_read },		/* read */
7023	{ &vnop_write_desc, (VOPFUNC)hfsfifo_write },		/* write */
7024	{ &vnop_ioctl_desc, (VOPFUNC)fifo_ioctl },		/* ioctl */
7025	{ &vnop_select_desc, (VOPFUNC)fifo_select },		/* select */
7026	{ &vnop_revoke_desc, (VOPFUNC)fifo_revoke },		/* revoke */
7027	{ &vnop_mmap_desc, (VOPFUNC)fifo_mmap },			/* mmap */
7028	{ &vnop_fsync_desc, (VOPFUNC)hfs_vnop_fsync },		/* fsync */
7029	{ &vnop_remove_desc, (VOPFUNC)fifo_remove },		/* remove */
7030	{ &vnop_link_desc, (VOPFUNC)fifo_link },			/* link */
7031	{ &vnop_rename_desc, (VOPFUNC)fifo_rename },		/* rename */
7032	{ &vnop_mkdir_desc, (VOPFUNC)fifo_mkdir },              /* mkdir */
7033	{ &vnop_rmdir_desc, (VOPFUNC)fifo_rmdir },		/* rmdir */
7034	{ &vnop_symlink_desc, (VOPFUNC)fifo_symlink },          /* symlink */
7035	{ &vnop_readdir_desc, (VOPFUNC)fifo_readdir },		/* readdir */
7036	{ &vnop_readlink_desc, (VOPFUNC)fifo_readlink },		/* readlink */
7037	{ &vnop_inactive_desc, (VOPFUNC)hfs_vnop_inactive },	/* inactive */
7038	{ &vnop_reclaim_desc, (VOPFUNC)hfs_vnop_reclaim },	/* reclaim */
7039	{ &vnop_strategy_desc, (VOPFUNC)fifo_strategy },		/* strategy */
7040	{ &vnop_pathconf_desc, (VOPFUNC)fifo_pathconf },		/* pathconf */
7041	{ &vnop_advlock_desc, (VOPFUNC)err_advlock },		/* advlock */
7042	{ &vnop_bwrite_desc, (VOPFUNC)hfs_vnop_bwrite },
7043	{ &vnop_pagein_desc, (VOPFUNC)hfs_vnop_pagein },		/* Pagein */
7044	{ &vnop_pageout_desc, (VOPFUNC)hfs_vnop_pageout },	/* Pageout */
7045	{ &vnop_copyfile_desc, (VOPFUNC)err_copyfile }, 		/* copyfile */
7046	{ &vnop_blktooff_desc, (VOPFUNC)hfs_vnop_blktooff },	/* blktooff */
7047	{ &vnop_offtoblk_desc, (VOPFUNC)hfs_vnop_offtoblk },	/* offtoblk */
7048  	{ &vnop_blockmap_desc, (VOPFUNC)hfs_vnop_blockmap },		/* blockmap */
7049	{ &vnop_getxattr_desc, (VOPFUNC)hfs_vnop_getxattr},
7050	{ &vnop_setxattr_desc, (VOPFUNC)hfs_vnop_setxattr},
7051	{ &vnop_removexattr_desc, (VOPFUNC)hfs_vnop_removexattr},
7052	{ &vnop_listxattr_desc, (VOPFUNC)hfs_vnop_listxattr},
7053	{ (struct vnodeop_desc*)NULL, (VOPFUNC)NULL }
7054};
7055struct vnodeopv_desc hfs_fifoop_opv_desc =
7056	{ &hfs_fifoop_p, hfs_fifoop_entries };
7057#endif /* FIFO */
7058
7059
7060
7061