1/*
2 * Copyright (c) 2000-2022 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/* Copyright (c) 1995 NeXT Computer, Inc. All Rights Reserved */
29/*
30 * Copyright (c) 1989, 1993
31 * The Regents of the University of California. All rights reserved.
32 * (c) UNIX System Laboratories, Inc.
33 * All or some portions of this file are derived from material licensed
34 * to the University of California by American Telephone and Telegraph
35 * Co. or Unix System Laboratories, Inc. and are reproduced herein with
36 * the permission of UNIX System Laboratories, Inc.
37 *
38 * Redistribution and use in source and binary forms, with or without
39 * modification, are permitted provided that the following conditions
40 * are met:
41 * 1. Redistributions of source code must retain the above copyright
42 * notice, this list of conditions and the following disclaimer.
43 * 2. Redistributions in binary form must reproduce the above copyright
44 * notice, this list of conditions and the following disclaimer in the
45 * documentation and/or other materials provided with the distribution.
46 * 3. All advertising materials mentioning features or use of this software
47 * must display the following acknowledgement:
48 * This product includes software developed by the University of
49 * California, Berkeley and its contributors.
50 * 4. Neither the name of the University nor the names of its contributors
51 * may be used to endorse or promote products derived from this software
52 * without specific prior written permission.
53 *
54 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
55 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
56 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
57 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
58 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
59 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
60 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
61 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
62 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
63 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
64 * SUCH DAMAGE.
65 *
66 * @(#)kpi_vfs.c
67 */
68/*
69 * NOTICE: This file was modified by SPARTA, Inc. in 2005 to introduce
70 * support for mandatory and extensible security protections. This notice
71 * is included in support of clause 2.2 (b) of the Apple Public License,
72 * Version 2.0.
73 */
74
75/*
76 * External virtual filesystem routines
77 */
78
79
80#include <sys/param.h>
81#include <sys/systm.h>
82#include <sys/proc_internal.h>
83#include <sys/kauth.h>
84#include <sys/mount.h>
85#include <sys/mount_internal.h>
86#include <sys/time.h>
87#include <sys/disk.h>
88#include <sys/vnode_internal.h>
89#include <sys/stat.h>
90#include <sys/namei.h>
91#include <sys/ucred.h>
92#include <sys/buf.h>
93#include <sys/errno.h>
94#include <kern/kalloc.h>
95#include <sys/domain.h>
96#include <sys/mbuf.h>
97#include <sys/syslog.h>
98#include <sys/ubc.h>
99#include <sys/vm.h>
100#include <sys/sysctl.h>
101#include <sys/filedesc.h>
102#include <sys/event.h>
103#include <sys/fsevents.h>
104#include <sys/user.h>
105#include <sys/lockf.h>
106#include <sys/xattr.h>
107#include <sys/kdebug.h>
108
109#include <kern/assert.h>
110#include <kern/zalloc.h>
111#include <kern/task.h>
112#include <kern/policy_internal.h>
113
114#include <libkern/OSByteOrder.h>
115
116#include <miscfs/specfs/specdev.h>
117
118#include <mach/mach_types.h>
119#include <mach/memory_object_types.h>
120#include <mach/task.h>
121
122#if CONFIG_MACF
123#include <security/mac_framework.h>
124#endif
125
126#if NULLFS
127#include <miscfs/nullfs/nullfs.h>
128#endif
129
130#include <sys/sdt.h>
131
132#define ESUCCESS 0
133#undef mount_t
134#undef vnode_t
135
136#define COMPAT_ONLY
137
138#define NATIVE_XATTR(VP) \
139 ((VP)->v_mount ? (VP)->v_mount->mnt_kern_flag & MNTK_EXTENDED_ATTRS : 0)
140
141#if CONFIG_APPLEDOUBLE
142static void xattrfile_remove(vnode_t dvp, const char *basename,
143 vfs_context_t ctx, int force);
144static void xattrfile_setattr(vnode_t dvp, const char * basename,
145 struct vnode_attr * vap, vfs_context_t ctx);
146#endif /* CONFIG_APPLEDOUBLE */
147
148extern lck_rw_t rootvnode_rw_lock;
149
150static errno_t post_rename(vnode_t fdvp, vnode_t fvp, vnode_t tdvp, vnode_t tvp);
151
152KALLOC_TYPE_DEFINE(KT_VFS_CONTEXT, struct vfs_context, KT_PRIV_ACCT);
153
154extern int fstypenumstart;
155char vfs_typenum_arr[13];
156
157LCK_GRP_DECLARE(typenum_arr_grp, "typenum array group");
158LCK_MTX_DECLARE(vfs_typenum_mtx, &typenum_arr_grp);
159/*
160 * vnode_setneedinactive
161 *
162 * Description: Indicate that when the last iocount on this vnode goes away,
163 * and the usecount is also zero, we should inform the filesystem
164 * via VNOP_INACTIVE.
165 *
166 * Parameters: vnode_t vnode to mark
167 *
168 * Returns: Nothing
169 *
170 * Notes: Notably used when we're deleting a file--we need not have a
171 * usecount, so VNOP_INACTIVE may not get called by anyone. We
172 * want it called when we drop our iocount.
173 */
174void
175vnode_setneedinactive(vnode_t vp)
176{
177 cache_purge(vp);
178
179 vnode_lock_spin(vp);
180 vp->v_lflag |= VL_NEEDINACTIVE;
181 vnode_unlock(vp);
182}
183
184
185/* ====================================================================== */
186/* ************ EXTERNAL KERNEL APIS ********************************** */
187/* ====================================================================== */
188
189/*
190 * implementations of exported VFS operations
191 */
192int
193VFS_MOUNT(mount_t mp, vnode_t devvp, user_addr_t data, vfs_context_t ctx)
194{
195 int error;
196
197 if ((mp == dead_mountp) || (mp->mnt_op->vfs_mount == 0)) {
198 return ENOTSUP;
199 }
200
201 if (vfs_context_is64bit(ctx)) {
202 if (vfs_64bitready(mp)) {
203 error = (*mp->mnt_op->vfs_mount)(mp, devvp, data, ctx);
204 } else {
205 error = ENOTSUP;
206 }
207 } else {
208 error = (*mp->mnt_op->vfs_mount)(mp, devvp, data, ctx);
209 }
210
211 return error;
212}
213
214int
215VFS_START(mount_t mp, int flags, vfs_context_t ctx)
216{
217 int error;
218
219 if ((mp == dead_mountp) || (mp->mnt_op->vfs_start == 0)) {
220 return ENOTSUP;
221 }
222
223 error = (*mp->mnt_op->vfs_start)(mp, flags, ctx);
224
225 return error;
226}
227
228int
229VFS_UNMOUNT(mount_t mp, int flags, vfs_context_t ctx)
230{
231 int error;
232
233 if ((mp == dead_mountp) || (mp->mnt_op->vfs_unmount == 0)) {
234 return ENOTSUP;
235 }
236
237 error = (*mp->mnt_op->vfs_unmount)(mp, flags, ctx);
238
239 return error;
240}
241
242/*
243 * Returns: 0 Success
244 * ENOTSUP Not supported
245 * <vfs_root>:ENOENT
246 * <vfs_root>:???
247 *
248 * Note: The return codes from the underlying VFS's root routine can't
249 * be fully enumerated here, since third party VFS authors may not
250 * limit their error returns to the ones documented here, even
251 * though this may result in some programs functioning incorrectly.
252 *
253 * The return codes documented above are those which may currently
254 * be returned by HFS from hfs_vfs_root, which is a simple wrapper
255 * for a call to hfs_vget on the volume mount point, not including
256 * additional error codes which may be propagated from underlying
257 * routines called by hfs_vget.
258 */
259int
260VFS_ROOT(mount_t mp, struct vnode ** vpp, vfs_context_t ctx)
261{
262 int error;
263
264 if ((mp == dead_mountp) || (mp->mnt_op->vfs_root == 0)) {
265 return ENOTSUP;
266 }
267
268 if (ctx == NULL) {
269 ctx = vfs_context_current();
270 }
271
272 error = (*mp->mnt_op->vfs_root)(mp, vpp, ctx);
273
274 return error;
275}
276
277int
278VFS_QUOTACTL(mount_t mp, int cmd, uid_t uid, caddr_t datap, vfs_context_t ctx)
279{
280 int error;
281
282 if ((mp == dead_mountp) || (mp->mnt_op->vfs_quotactl == 0)) {
283 return ENOTSUP;
284 }
285
286 error = (*mp->mnt_op->vfs_quotactl)(mp, cmd, uid, datap, ctx);
287
288 return error;
289}
290
291int
292VFS_GETATTR(mount_t mp, struct vfs_attr *vfa, vfs_context_t ctx)
293{
294 int error;
295
296 if ((mp == dead_mountp) || (mp->mnt_op->vfs_getattr == 0)) {
297 return ENOTSUP;
298 }
299
300 if (ctx == NULL) {
301 ctx = vfs_context_current();
302 }
303
304 error = (*mp->mnt_op->vfs_getattr)(mp, vfa, ctx);
305
306 return error;
307}
308
309int
310VFS_SETATTR(mount_t mp, struct vfs_attr *vfa, vfs_context_t ctx)
311{
312 int error;
313
314 if ((mp == dead_mountp) || (mp->mnt_op->vfs_setattr == 0)) {
315 return ENOTSUP;
316 }
317
318 if (ctx == NULL) {
319 ctx = vfs_context_current();
320 }
321
322 error = (*mp->mnt_op->vfs_setattr)(mp, vfa, ctx);
323
324 return error;
325}
326
327int
328VFS_SYNC(mount_t mp, int flags, vfs_context_t ctx)
329{
330 int error;
331
332 if ((mp == dead_mountp) || (mp->mnt_op->vfs_sync == 0)) {
333 return ENOTSUP;
334 }
335
336 if (ctx == NULL) {
337 ctx = vfs_context_current();
338 }
339
340 error = (*mp->mnt_op->vfs_sync)(mp, flags, ctx);
341
342 return error;
343}
344
345int
346VFS_VGET(mount_t mp, ino64_t ino, struct vnode **vpp, vfs_context_t ctx)
347{
348 int error;
349
350 if ((mp == dead_mountp) || (mp->mnt_op->vfs_vget == 0)) {
351 return ENOTSUP;
352 }
353
354 if (ctx == NULL) {
355 ctx = vfs_context_current();
356 }
357
358 error = (*mp->mnt_op->vfs_vget)(mp, ino, vpp, ctx);
359
360 return error;
361}
362
363int
364VFS_FHTOVP(mount_t mp, int fhlen, unsigned char *fhp, vnode_t *vpp, vfs_context_t ctx)
365{
366 int error;
367
368 if ((mp == dead_mountp) || (mp->mnt_op->vfs_fhtovp == 0)) {
369 return ENOTSUP;
370 }
371
372 if (ctx == NULL) {
373 ctx = vfs_context_current();
374 }
375
376 error = (*mp->mnt_op->vfs_fhtovp)(mp, fhlen, fhp, vpp, ctx);
377
378 return error;
379}
380
381int
382VFS_VPTOFH(struct vnode *vp, int *fhlenp, unsigned char *fhp, vfs_context_t ctx)
383{
384 int error;
385
386 if ((vp->v_mount == dead_mountp) || (vp->v_mount->mnt_op->vfs_vptofh == 0)) {
387 return ENOTSUP;
388 }
389
390 if (ctx == NULL) {
391 ctx = vfs_context_current();
392 }
393
394 error = (*vp->v_mount->mnt_op->vfs_vptofh)(vp, fhlenp, fhp, ctx);
395
396 return error;
397}
398
399int
400VFS_IOCTL(struct mount *mp, u_long command, caddr_t data,
401 int flags, vfs_context_t context)
402{
403 if (mp == dead_mountp || !mp->mnt_op->vfs_ioctl) {
404 return ENOTSUP;
405 }
406
407 return mp->mnt_op->vfs_ioctl(mp, command, data, flags,
408 context ?: vfs_context_current());
409}
410
411int
412VFS_VGET_SNAPDIR(mount_t mp, vnode_t *vpp, vfs_context_t ctx)
413{
414 int error;
415
416 if ((mp == dead_mountp) || (mp->mnt_op->vfs_vget_snapdir == 0)) {
417 return ENOTSUP;
418 }
419
420 if (ctx == NULL) {
421 ctx = vfs_context_current();
422 }
423
424 error = (*mp->mnt_op->vfs_vget_snapdir)(mp, vpp, ctx);
425
426 return error;
427}
428
429/* returns the cached throttle mask for the mount_t */
430uint64_t
431vfs_throttle_mask(mount_t mp)
432{
433 return mp->mnt_throttle_mask;
434}
435
436/* returns a copy of vfs type name for the mount_t */
437void
438vfs_name(mount_t mp, char *buffer)
439{
440 strncpy(buffer, mp->mnt_vtable->vfc_name, MFSNAMELEN);
441}
442
443/* returns vfs type number for the mount_t */
444int
445vfs_typenum(mount_t mp)
446{
447 return mp->mnt_vtable->vfc_typenum;
448}
449
450/* Safe to cast to "struct label*"; returns "void*" to limit dependence of mount.h on security headers. */
451void*
452vfs_mntlabel(mount_t mp)
453{
454 return (void*)mac_mount_label(mp);
455}
456
457uint64_t
458vfs_mount_id(mount_t mp)
459{
460 return mp->mnt_mount_id;
461}
462
463/* returns command modifier flags of mount_t ie. MNT_CMDFLAGS */
464uint64_t
465vfs_flags(mount_t mp)
466{
467 return (uint64_t)(mp->mnt_flag & (MNT_CMDFLAGS | MNT_VISFLAGMASK));
468}
469
470/* set any of the command modifier flags(MNT_CMDFLAGS) in mount_t */
471void
472vfs_setflags(mount_t mp, uint64_t flags)
473{
474 uint32_t lflags = (uint32_t)(flags & (MNT_CMDFLAGS | MNT_VISFLAGMASK));
475
476 mount_lock(mp);
477 mp->mnt_flag |= lflags;
478 mount_unlock(mp);
479}
480
481/* clear any of the command modifier flags(MNT_CMDFLAGS) in mount_t */
482void
483vfs_clearflags(mount_t mp, uint64_t flags)
484{
485 uint32_t lflags = (uint32_t)(flags & (MNT_CMDFLAGS | MNT_VISFLAGMASK));
486
487 mount_lock(mp);
488 mp->mnt_flag &= ~lflags;
489 mount_unlock(mp);
490}
491
492/* Is the mount_t ronly and upgrade read/write requested? */
493int
494vfs_iswriteupgrade(mount_t mp) /* ronly && MNTK_WANTRDWR */
495{
496 return (mp->mnt_flag & MNT_RDONLY) && (mp->mnt_kern_flag & MNTK_WANTRDWR);
497}
498
499
500/* Is the mount_t mounted ronly */
501int
502vfs_isrdonly(mount_t mp)
503{
504 return mp->mnt_flag & MNT_RDONLY;
505}
506
507/* Is the mount_t mounted for filesystem synchronous writes? */
508int
509vfs_issynchronous(mount_t mp)
510{
511 return mp->mnt_flag & MNT_SYNCHRONOUS;
512}
513
514/* Is the mount_t mounted read/write? */
515int
516vfs_isrdwr(mount_t mp)
517{
518 return (mp->mnt_flag & MNT_RDONLY) == 0;
519}
520
521
522/* Is mount_t marked for update (ie MNT_UPDATE) */
523int
524vfs_isupdate(mount_t mp)
525{
526 return mp->mnt_flag & MNT_UPDATE;
527}
528
529
530/* Is mount_t marked for reload (ie MNT_RELOAD) */
531int
532vfs_isreload(mount_t mp)
533{
534 return (mp->mnt_flag & MNT_UPDATE) && (mp->mnt_flag & MNT_RELOAD);
535}
536
537/* Is mount_t marked for forced unmount (ie MNT_FORCE or MNTK_FRCUNMOUNT) */
538int
539vfs_isforce(mount_t mp)
540{
541 if (mp->mnt_lflag & MNT_LFORCE) {
542 return 1;
543 } else {
544 return 0;
545 }
546}
547
548int
549vfs_isunmount(mount_t mp)
550{
551 if ((mp->mnt_lflag & MNT_LUNMOUNT)) {
552 return 1;
553 } else {
554 return 0;
555 }
556}
557
558int
559vfs_64bitready(mount_t mp)
560{
561 if ((mp->mnt_vtable->vfc_vfsflags & VFC_VFS64BITREADY)) {
562 return 1;
563 } else {
564 return 0;
565 }
566}
567
568
569int
570vfs_authcache_ttl(mount_t mp)
571{
572 if ((mp->mnt_kern_flag & (MNTK_AUTH_OPAQUE | MNTK_AUTH_CACHE_TTL))) {
573 return mp->mnt_authcache_ttl;
574 } else {
575 return CACHED_RIGHT_INFINITE_TTL;
576 }
577}
578
579void
580vfs_setauthcache_ttl(mount_t mp, int ttl)
581{
582 mount_lock(mp);
583 mp->mnt_kern_flag |= MNTK_AUTH_CACHE_TTL;
584 mp->mnt_authcache_ttl = ttl;
585 mount_unlock(mp);
586}
587
588void
589vfs_clearauthcache_ttl(mount_t mp)
590{
591 mount_lock(mp);
592 mp->mnt_kern_flag &= ~MNTK_AUTH_CACHE_TTL;
593 /*
594 * back to the default TTL value in case
595 * MNTK_AUTH_OPAQUE is set on this mount
596 */
597 mp->mnt_authcache_ttl = CACHED_LOOKUP_RIGHT_TTL;
598 mount_unlock(mp);
599}
600
601int
602vfs_authopaque(mount_t mp)
603{
604 if ((mp->mnt_kern_flag & MNTK_AUTH_OPAQUE)) {
605 return 1;
606 } else {
607 return 0;
608 }
609}
610
611int
612vfs_authopaqueaccess(mount_t mp)
613{
614 if ((mp->mnt_kern_flag & MNTK_AUTH_OPAQUE_ACCESS)) {
615 return 1;
616 } else {
617 return 0;
618 }
619}
620
621void
622vfs_setauthopaque(mount_t mp)
623{
624 mount_lock(mp);
625 mp->mnt_kern_flag |= MNTK_AUTH_OPAQUE;
626 mount_unlock(mp);
627}
628
629void
630vfs_setauthopaqueaccess(mount_t mp)
631{
632 mount_lock(mp);
633 mp->mnt_kern_flag |= MNTK_AUTH_OPAQUE_ACCESS;
634 mount_unlock(mp);
635}
636
637void
638vfs_clearauthopaque(mount_t mp)
639{
640 mount_lock(mp);
641 mp->mnt_kern_flag &= ~MNTK_AUTH_OPAQUE;
642 mount_unlock(mp);
643}
644
645void
646vfs_clearauthopaqueaccess(mount_t mp)
647{
648 mount_lock(mp);
649 mp->mnt_kern_flag &= ~MNTK_AUTH_OPAQUE_ACCESS;
650 mount_unlock(mp);
651}
652
653void
654vfs_setextendedsecurity(mount_t mp)
655{
656 mount_lock(mp);
657 mp->mnt_kern_flag |= MNTK_EXTENDED_SECURITY;
658 mount_unlock(mp);
659}
660
661void
662vfs_setmntsystem(mount_t mp)
663{
664 mount_lock(mp);
665 mp->mnt_kern_flag |= MNTK_SYSTEM;
666 mount_unlock(mp);
667}
668
669void
670vfs_setmntsystemdata(mount_t mp)
671{
672 mount_lock(mp);
673 mp->mnt_kern_flag |= MNTK_SYSTEMDATA;
674 mount_unlock(mp);
675}
676
677void
678vfs_setmntswap(mount_t mp)
679{
680 mount_lock(mp);
681 mp->mnt_kern_flag |= (MNTK_SYSTEM | MNTK_SWAP_MOUNT);
682 mount_unlock(mp);
683}
684
685void
686vfs_clearextendedsecurity(mount_t mp)
687{
688 mount_lock(mp);
689 mp->mnt_kern_flag &= ~MNTK_EXTENDED_SECURITY;
690 mount_unlock(mp);
691}
692
693void
694vfs_setnoswap(mount_t mp)
695{
696 mount_lock(mp);
697 mp->mnt_kern_flag |= MNTK_NOSWAP;
698 mount_unlock(mp);
699}
700
701void
702vfs_clearnoswap(mount_t mp)
703{
704 mount_lock(mp);
705 mp->mnt_kern_flag &= ~MNTK_NOSWAP;
706 mount_unlock(mp);
707}
708
709int
710vfs_extendedsecurity(mount_t mp)
711{
712 return mp->mnt_kern_flag & MNTK_EXTENDED_SECURITY;
713}
714
715/* returns the max size of short symlink in this mount_t */
716uint32_t
717vfs_maxsymlen(mount_t mp)
718{
719 return mp->mnt_maxsymlinklen;
720}
721
722/* set max size of short symlink on mount_t */
723void
724vfs_setmaxsymlen(mount_t mp, uint32_t symlen)
725{
726 mp->mnt_maxsymlinklen = symlen;
727}
728
729boolean_t
730vfs_is_basesystem(mount_t mp)
731{
732 return ((mp->mnt_supl_kern_flag & MNTK_SUPL_BASESYSTEM) == 0) ? false : true;
733}
734
735/* return a pointer to the RO vfs_statfs associated with mount_t */
736struct vfsstatfs *
737vfs_statfs(mount_t mp)
738{
739 return &mp->mnt_vfsstat;
740}
741
742int
743vfs_getattr(mount_t mp, struct vfs_attr *vfa, vfs_context_t ctx)
744{
745 int error;
746
747 if ((error = VFS_GETATTR(mp, vfa, ctx)) != 0) {
748 return error;
749 }
750
751 /*
752 * If we have a filesystem create time, use it to default some others.
753 */
754 if (VFSATTR_IS_SUPPORTED(vfa, f_create_time)) {
755 if (VFSATTR_IS_ACTIVE(vfa, f_modify_time) && !VFSATTR_IS_SUPPORTED(vfa, f_modify_time)) {
756 VFSATTR_RETURN(vfa, f_modify_time, vfa->f_create_time);
757 }
758 }
759
760 return 0;
761}
762
763int
764vfs_setattr(mount_t mp, struct vfs_attr *vfa, vfs_context_t ctx)
765{
766 int error;
767
768 /*
769 * with a read-only system volume, we need to allow rename of the root volume
770 * even if it's read-only. Don't return EROFS here if setattr changes only
771 * the volume name
772 */
773 if (vfs_isrdonly(mp) &&
774 !((strcmp(s1: mp->mnt_vfsstat.f_fstypename, s2: "apfs") == 0) && (vfa->f_active == VFSATTR_f_vol_name))) {
775 return EROFS;
776 }
777
778 error = VFS_SETATTR(mp, vfa, ctx);
779
780 /*
781 * If we had alternate ways of setting vfs attributes, we'd
782 * fall back here.
783 */
784
785 return error;
786}
787
788/* return the private data handle stored in mount_t */
789void *
790vfs_fsprivate(mount_t mp)
791{
792 return mp->mnt_data;
793}
794
795/* set the private data handle in mount_t */
796void
797vfs_setfsprivate(mount_t mp, void *mntdata)
798{
799 mount_lock(mp);
800 mp->mnt_data = mntdata;
801 mount_unlock(mp);
802}
803
804/* query whether the mount point supports native EAs */
805int
806vfs_nativexattrs(mount_t mp)
807{
808 return mp->mnt_kern_flag & MNTK_EXTENDED_ATTRS;
809}
810
811/*
812 * return the block size of the underlying
813 * device associated with mount_t
814 */
815int
816vfs_devblocksize(mount_t mp)
817{
818 return mp->mnt_devblocksize;
819}
820
821/*
822 * Returns vnode with an iocount that must be released with vnode_put()
823 */
824vnode_t
825vfs_vnodecovered(mount_t mp)
826{
827 vnode_t vp = mp->mnt_vnodecovered;
828 if ((vp == NULL) || (vnode_getwithref(vp) != 0)) {
829 return NULL;
830 } else {
831 return vp;
832 }
833}
834
835/*
836 * Returns device vnode backing a mountpoint with an iocount (if valid vnode exists).
837 * The iocount must be released with vnode_put(). Note that this KPI is subtle
838 * with respect to the validity of using this device vnode for anything substantial
839 * (which is discouraged). If commands are sent to the device driver without
840 * taking proper steps to ensure that the device is still open, chaos may ensue.
841 * Similarly, this routine should only be called if there is some guarantee that
842 * the mount itself is still valid.
843 */
844vnode_t
845vfs_devvp(mount_t mp)
846{
847 vnode_t vp = mp->mnt_devvp;
848
849 if ((vp != NULLVP) && (vnode_get(vp) == 0)) {
850 return vp;
851 }
852
853 return NULLVP;
854}
855
856/*
857 * return the io attributes associated with mount_t
858 */
859void
860vfs_ioattr(mount_t mp, struct vfsioattr *ioattrp)
861{
862 ioattrp->io_reserved[0] = NULL;
863 ioattrp->io_reserved[1] = NULL;
864 if (mp == NULL) {
865 ioattrp->io_maxreadcnt = MAXPHYS;
866 ioattrp->io_maxwritecnt = MAXPHYS;
867 ioattrp->io_segreadcnt = 32;
868 ioattrp->io_segwritecnt = 32;
869 ioattrp->io_maxsegreadsize = MAXPHYS;
870 ioattrp->io_maxsegwritesize = MAXPHYS;
871 ioattrp->io_devblocksize = DEV_BSIZE;
872 ioattrp->io_flags = 0;
873 ioattrp->io_max_swappin_available = 0;
874 } else {
875 ioattrp->io_maxreadcnt = mp->mnt_maxreadcnt;
876 ioattrp->io_maxwritecnt = mp->mnt_maxwritecnt;
877 ioattrp->io_segreadcnt = mp->mnt_segreadcnt;
878 ioattrp->io_segwritecnt = mp->mnt_segwritecnt;
879 ioattrp->io_maxsegreadsize = mp->mnt_maxsegreadsize;
880 ioattrp->io_maxsegwritesize = mp->mnt_maxsegwritesize;
881 ioattrp->io_devblocksize = mp->mnt_devblocksize;
882 ioattrp->io_flags = mp->mnt_ioflags;
883 ioattrp->io_max_swappin_available = mp->mnt_max_swappin_available;
884 }
885}
886
887
888/*
889 * set the IO attributes associated with mount_t
890 */
891void
892vfs_setioattr(mount_t mp, struct vfsioattr * ioattrp)
893{
894 if (mp == NULL) {
895 return;
896 }
897 mp->mnt_maxreadcnt = ioattrp->io_maxreadcnt;
898 mp->mnt_maxwritecnt = ioattrp->io_maxwritecnt;
899 mp->mnt_segreadcnt = ioattrp->io_segreadcnt;
900 mp->mnt_segwritecnt = ioattrp->io_segwritecnt;
901 mp->mnt_maxsegreadsize = ioattrp->io_maxsegreadsize;
902 mp->mnt_maxsegwritesize = ioattrp->io_maxsegwritesize;
903 mp->mnt_devblocksize = ioattrp->io_devblocksize;
904 mp->mnt_ioflags = ioattrp->io_flags;
905 mp->mnt_max_swappin_available = ioattrp->io_max_swappin_available;
906}
907
908/*
909 * Add a new filesystem into the kernel specified in passed in
910 * vfstable structure. It fills in the vnode
911 * dispatch vector that is to be passed to when vnodes are created.
912 * It returns a handle which is to be used to when the FS is to be removed
913 */
914typedef int (*PFI)(void *);
915extern int vfs_opv_numops;
916errno_t
917vfs_fsadd(struct vfs_fsentry *vfe, vfstable_t *handle)
918{
919 struct vfstable *newvfstbl = NULL;
920 int i, j;
921 int(***opv_desc_vector_p)(void *);
922 int(**opv_desc_vector)(void *);
923 const struct vnodeopv_entry_desc *opve_descp;
924 int desccount;
925 int descsize;
926 PFI *descptr;
927
928 /*
929 * This routine is responsible for all the initialization that would
930 * ordinarily be done as part of the system startup;
931 */
932
933 if (vfe == (struct vfs_fsentry *)0) {
934 return EINVAL;
935 }
936
937 desccount = vfe->vfe_vopcnt;
938 if ((desccount <= 0) || ((desccount > 8)) || (vfe->vfe_vfsops == (struct vfsops *)NULL)
939 || (vfe->vfe_opvdescs == (struct vnodeopv_desc **)NULL)) {
940 return EINVAL;
941 }
942
943 /* Non-threadsafe filesystems are not supported */
944 if ((vfe->vfe_flags & (VFS_TBLTHREADSAFE | VFS_TBLFSNODELOCK)) == 0) {
945 return EINVAL;
946 }
947
948 newvfstbl = kalloc_type(struct vfstable, Z_WAITOK | Z_ZERO);
949 newvfstbl->vfc_vfsops = vfe->vfe_vfsops;
950 strncpy(&newvfstbl->vfc_name[0], vfe->vfe_fsname, MFSNAMELEN);
951 if ((vfe->vfe_flags & VFS_TBLNOTYPENUM)) {
952 int tmp;
953 int found = 0;
954 lck_mtx_lock(lck: &vfs_typenum_mtx);
955 for (tmp = fstypenumstart; tmp < OID_AUTO_START; tmp++) {
956 if (isclr(vfs_typenum_arr, tmp)) {
957 newvfstbl->vfc_typenum = tmp;
958 setbit(vfs_typenum_arr, tmp);
959 found = 1;
960 break;
961 }
962 }
963 if (!found) {
964 lck_mtx_unlock(lck: &vfs_typenum_mtx);
965 return EINVAL;
966 }
967 if (maxvfstypenum < OID_AUTO_START) {
968 /* getvfsbyname checks up to but not including maxvfstypenum */
969 maxvfstypenum = newvfstbl->vfc_typenum + 1;
970 }
971 lck_mtx_unlock(lck: &vfs_typenum_mtx);
972 } else {
973 newvfstbl->vfc_typenum = vfe->vfe_fstypenum;
974 lck_mtx_lock(lck: &vfs_typenum_mtx);
975 setbit(vfs_typenum_arr, newvfstbl->vfc_typenum);
976 if (newvfstbl->vfc_typenum >= maxvfstypenum) {
977 maxvfstypenum = newvfstbl->vfc_typenum + 1;
978 }
979 lck_mtx_unlock(lck: &vfs_typenum_mtx);
980 }
981
982
983 newvfstbl->vfc_refcount = 0;
984 newvfstbl->vfc_flags = 0;
985 newvfstbl->vfc_mountroot = NULL;
986 newvfstbl->vfc_next = NULL;
987 newvfstbl->vfc_vfsflags = 0;
988 if (vfe->vfe_flags & VFS_TBL64BITREADY) {
989 newvfstbl->vfc_vfsflags |= VFC_VFS64BITREADY;
990 }
991 if (vfe->vfe_flags & VFS_TBLVNOP_PAGEINV2) {
992 newvfstbl->vfc_vfsflags |= VFC_VFSVNOP_PAGEINV2;
993 }
994 if (vfe->vfe_flags & VFS_TBLVNOP_PAGEOUTV2) {
995 newvfstbl->vfc_vfsflags |= VFC_VFSVNOP_PAGEOUTV2;
996 }
997 if ((vfe->vfe_flags & VFS_TBLLOCALVOL) == VFS_TBLLOCALVOL) {
998 newvfstbl->vfc_flags |= MNT_LOCAL;
999 }
1000 if ((vfe->vfe_flags & VFS_TBLLOCALVOL) && (vfe->vfe_flags & VFS_TBLGENERICMNTARGS) == 0) {
1001 newvfstbl->vfc_vfsflags |= VFC_VFSLOCALARGS;
1002 } else {
1003 newvfstbl->vfc_vfsflags |= VFC_VFSGENERICARGS;
1004 }
1005
1006 if (vfe->vfe_flags & VFS_TBLNATIVEXATTR) {
1007 newvfstbl->vfc_vfsflags |= VFC_VFSNATIVEXATTR;
1008 }
1009 if (vfe->vfe_flags & VFS_TBLUNMOUNT_PREFLIGHT) {
1010 newvfstbl->vfc_vfsflags |= VFC_VFSPREFLIGHT;
1011 }
1012 if (vfe->vfe_flags & VFS_TBLREADDIR_EXTENDED) {
1013 newvfstbl->vfc_vfsflags |= VFC_VFSREADDIR_EXTENDED;
1014 }
1015 if (vfe->vfe_flags & VFS_TBLNOMACLABEL) {
1016 newvfstbl->vfc_vfsflags |= VFC_VFSNOMACLABEL;
1017 }
1018 if (vfe->vfe_flags & VFS_TBLVNOP_NOUPDATEID_RENAME) {
1019 newvfstbl->vfc_vfsflags |= VFC_VFSVNOP_NOUPDATEID_RENAME;
1020 }
1021 if (vfe->vfe_flags & VFS_TBLVNOP_SECLUDE_RENAME) {
1022 newvfstbl->vfc_vfsflags |= VFC_VFSVNOP_SECLUDE_RENAME;
1023 }
1024 if (vfe->vfe_flags & VFS_TBLCANMOUNTROOT) {
1025 newvfstbl->vfc_vfsflags |= VFC_VFSCANMOUNTROOT;
1026 }
1027
1028 /*
1029 * Allocate and init the vectors.
1030 * Also handle backwards compatibility.
1031 *
1032 * We allocate one large block to hold all <desccount>
1033 * vnode operation vectors stored contiguously.
1034 */
1035 /* XXX - shouldn't be M_TEMP */
1036
1037 descsize = desccount * vfs_opv_numops;
1038 descptr = kalloc_type(PFI, descsize, Z_WAITOK | Z_ZERO);
1039
1040 newvfstbl->vfc_descptr = descptr;
1041 newvfstbl->vfc_descsize = descsize;
1042
1043 newvfstbl->vfc_sysctl = NULL;
1044
1045 for (i = 0; i < desccount; i++) {
1046 opv_desc_vector_p = vfe->vfe_opvdescs[i]->opv_desc_vector_p;
1047 /*
1048 * Fill in the caller's pointer to the start of the i'th vector.
1049 * They'll need to supply it when calling vnode_create.
1050 */
1051 opv_desc_vector = descptr + i * vfs_opv_numops;
1052 *opv_desc_vector_p = opv_desc_vector;
1053
1054 for (j = 0; vfe->vfe_opvdescs[i]->opv_desc_ops[j].opve_op; j++) {
1055 opve_descp = &(vfe->vfe_opvdescs[i]->opv_desc_ops[j]);
1056
1057 /* Silently skip known-disabled operations */
1058 if (opve_descp->opve_op->vdesc_flags & VDESC_DISABLED) {
1059 printf("vfs_fsadd: Ignoring reference in %p to disabled operation %s.\n",
1060 vfe->vfe_opvdescs[i], opve_descp->opve_op->vdesc_name);
1061 continue;
1062 }
1063
1064 /*
1065 * Sanity check: is this operation listed
1066 * in the list of operations? We check this
1067 * by seeing if its offset is zero. Since
1068 * the default routine should always be listed
1069 * first, it should be the only one with a zero
1070 * offset. Any other operation with a zero
1071 * offset is probably not listed in
1072 * vfs_op_descs, and so is probably an error.
1073 *
1074 * A panic here means the layer programmer
1075 * has committed the all-too common bug
1076 * of adding a new operation to the layer's
1077 * list of vnode operations but
1078 * not adding the operation to the system-wide
1079 * list of supported operations.
1080 */
1081 if (opve_descp->opve_op->vdesc_offset == 0 &&
1082 opve_descp->opve_op != VDESC(vnop_default)) {
1083 printf("vfs_fsadd: operation %s not listed in %s.\n",
1084 opve_descp->opve_op->vdesc_name,
1085 "vfs_op_descs");
1086 panic("vfs_fsadd: bad operation");
1087 }
1088 /*
1089 * Fill in this entry.
1090 */
1091 opv_desc_vector[opve_descp->opve_op->vdesc_offset] =
1092 opve_descp->opve_impl;
1093 }
1094
1095 /*
1096 * Finally, go back and replace unfilled routines
1097 * with their default. (Sigh, an O(n^3) algorithm. I
1098 * could make it better, but that'd be work, and n is small.)
1099 */
1100 opv_desc_vector_p = vfe->vfe_opvdescs[i]->opv_desc_vector_p;
1101
1102 /*
1103 * Force every operations vector to have a default routine.
1104 */
1105 opv_desc_vector = *opv_desc_vector_p;
1106 if (opv_desc_vector[VOFFSET(vnop_default)] == NULL) {
1107 panic("vfs_fsadd: operation vector without default routine.");
1108 }
1109 for (j = 0; j < vfs_opv_numops; j++) {
1110 if (opv_desc_vector[j] == NULL) {
1111 opv_desc_vector[j] =
1112 opv_desc_vector[VOFFSET(vnop_default)];
1113 }
1114 }
1115 } /* end of each vnodeopv_desc parsing */
1116
1117 *handle = vfstable_add(newvfstbl);
1118
1119 if (newvfstbl->vfc_vfsops->vfs_init) {
1120 struct vfsconf vfsc;
1121 bzero(s: &vfsc, n: sizeof(struct vfsconf));
1122 vfsc.vfc_reserved1 = 0;
1123 bcopy(src: (*handle)->vfc_name, dst: vfsc.vfc_name, n: sizeof(vfsc.vfc_name));
1124 vfsc.vfc_typenum = (*handle)->vfc_typenum;
1125 vfsc.vfc_refcount = (*handle)->vfc_refcount;
1126 vfsc.vfc_flags = (*handle)->vfc_flags;
1127 vfsc.vfc_reserved2 = 0;
1128 vfsc.vfc_reserved3 = 0;
1129
1130 (*newvfstbl->vfc_vfsops->vfs_init)(&vfsc);
1131 }
1132
1133 kfree_type(struct vfstable, newvfstbl);
1134
1135 return 0;
1136}
1137
1138/*
1139 * Removes the filesystem from kernel.
1140 * The argument passed in is the handle that was given when
1141 * file system was added
1142 */
1143errno_t
1144vfs_fsremove(vfstable_t handle)
1145{
1146 struct vfstable * vfstbl = (struct vfstable *)handle;
1147 void *old_desc = NULL;
1148 size_t descsize = 0;
1149 errno_t err;
1150
1151 /* Preflight check for any mounts */
1152 mount_list_lock();
1153 if (vfstbl->vfc_refcount != 0) {
1154 mount_list_unlock();
1155 return EBUSY;
1156 }
1157
1158 /* Free the spot in vfs_typenum_arr */
1159 lck_mtx_lock(lck: &vfs_typenum_mtx);
1160 clrbit(vfs_typenum_arr, handle->vfc_typenum);
1161 if (maxvfstypenum == handle->vfc_typenum) {
1162 maxvfstypenum--;
1163 }
1164 lck_mtx_unlock(lck: &vfs_typenum_mtx);
1165
1166 /*
1167 * save the old descriptor; the free cannot occur unconditionally,
1168 * since vfstable_del() may fail.
1169 */
1170 if (vfstbl->vfc_descptr && vfstbl->vfc_descsize) {
1171 old_desc = vfstbl->vfc_descptr;
1172 descsize = vfstbl->vfc_descsize;
1173 }
1174 err = vfstable_del(vfstbl);
1175
1176 mount_list_unlock();
1177
1178 /* free the descriptor if the delete was successful */
1179 if (err == 0) {
1180 kfree_type(PFI, descsize, old_desc);
1181 }
1182
1183 return err;
1184}
1185
1186void
1187vfs_setowner(mount_t mp, uid_t uid, gid_t gid)
1188{
1189 mp->mnt_fsowner = uid;
1190 mp->mnt_fsgroup = gid;
1191}
1192
1193/*
1194 * Callers should be careful how they use this; accessing
1195 * mnt_last_write_completed_timestamp is not thread-safe. Writing to
1196 * it isn't either. Point is: be prepared to deal with strange values
1197 * being returned.
1198 */
1199uint64_t
1200vfs_idle_time(mount_t mp)
1201{
1202 if (mp->mnt_pending_write_size) {
1203 return 0;
1204 }
1205
1206 struct timeval now;
1207
1208 microuptime(tv: &now);
1209
1210 return (now.tv_sec
1211 - mp->mnt_last_write_completed_timestamp.tv_sec) * 1000000
1212 + now.tv_usec - mp->mnt_last_write_completed_timestamp.tv_usec;
1213}
1214
1215/*
1216 * vfs_context_create_with_proc() takes a reference on an arbitrary
1217 * thread in the process. To distinguish this reference-counted thread
1218 * from the usual non-reference-counted thread, we set the least significant
1219 * bit of of vc_thread.
1220 */
1221#define VFS_CONTEXT_THREAD_IS_REFERENCED(ctx) \
1222 (!!(((uintptr_t)(ctx)->vc_thread) & 1UL))
1223
1224#define VFS_CONTEXT_SET_REFERENCED_THREAD(ctx, thr) \
1225 (ctx)->vc_thread = (thread_t)(((uintptr_t)(thr)) | 1UL)
1226
1227#define VFS_CONTEXT_GET_THREAD(ctx) \
1228 ((thread_t)(((uintptr_t)(ctx)->vc_thread) & ~1UL))
1229
1230int
1231vfs_context_pid(vfs_context_t ctx)
1232{
1233 return proc_pid(vfs_context_proc(ctx));
1234}
1235
1236int
1237vfs_context_copy_audit_token(vfs_context_t ctx, audit_token_t *token)
1238{
1239 kern_return_t err;
1240 task_t task;
1241 mach_msg_type_number_t info_size = TASK_AUDIT_TOKEN_COUNT;
1242
1243 task = vfs_context_task(ctx);
1244
1245 if (task == NULL) {
1246 // Not sure how this would happen; we are supposed to be
1247 // in the middle of using the context. Regardless, don't
1248 // wander off a NULL pointer.
1249 return ESRCH;
1250 }
1251
1252 err = task_info(target_task: task, TASK_AUDIT_TOKEN, task_info_out: (integer_t *)token, task_info_outCnt: &info_size);
1253 return (err) ? ESRCH : 0;
1254}
1255
1256int
1257vfs_context_suser(vfs_context_t ctx)
1258{
1259 return suser(cred: ctx->vc_ucred, NULL);
1260}
1261
1262/*
1263 * Return bit field of signals posted to all threads in the context's process.
1264 *
1265 * XXX Signals should be tied to threads, not processes, for most uses of this
1266 * XXX call.
1267 */
1268int
1269vfs_context_issignal(vfs_context_t ctx, sigset_t mask)
1270{
1271 proc_t p = vfs_context_proc(ctx);
1272 if (p) {
1273 return proc_pendingsignals(p, mask);
1274 }
1275 return 0;
1276}
1277
1278int
1279vfs_context_is64bit(vfs_context_t ctx)
1280{
1281 uthread_t uth;
1282 thread_t t;
1283
1284 if (ctx != NULL && (t = VFS_CONTEXT_GET_THREAD(ctx)) != NULL) {
1285 uth = get_bsdthread_info(t);
1286 } else {
1287 uth = current_uthread();
1288 }
1289 return uthread_is64bit(uth);
1290}
1291
1292boolean_t
1293vfs_context_can_resolve_triggers(vfs_context_t ctx)
1294{
1295 proc_t proc = vfs_context_proc(ctx);
1296
1297 if (proc) {
1298 if (proc->p_vfs_iopolicy &
1299 P_VFS_IOPOLICY_TRIGGER_RESOLVE_DISABLE) {
1300 return false;
1301 }
1302 return true;
1303 }
1304 return false;
1305}
1306
1307boolean_t
1308vfs_context_can_break_leases(vfs_context_t ctx)
1309{
1310 proc_t proc = vfs_context_proc(ctx);
1311
1312 if (proc) {
1313 /*
1314 * We do not have a separate I/O policy for this,
1315 * because the scenarios where we would not want
1316 * local file lease breaks are currently exactly
1317 * the same as where we would not want dataless
1318 * file materialization (mainly, system daemons
1319 * passively snooping file activity).
1320 */
1321 if (proc->p_vfs_iopolicy &
1322 P_VFS_IOPOLICY_MATERIALIZE_DATALESS_FILES) {
1323 return true;
1324 }
1325 return false;
1326 }
1327 return true;
1328}
1329
1330boolean_t
1331vfs_context_allow_fs_blksize_nocache_write(vfs_context_t ctx)
1332{
1333 uthread_t uth;
1334 thread_t t;
1335 proc_t p;
1336
1337 if ((ctx == NULL) || (t = VFS_CONTEXT_GET_THREAD(ctx)) == NULL) {
1338 return FALSE;
1339 }
1340
1341 uth = get_bsdthread_info(t);
1342 if (uth && (uth->uu_flag & UT_FS_BLKSIZE_NOCACHE_WRITES)) {
1343 return TRUE;
1344 }
1345
1346 p = (proc_t)get_bsdthreadtask_info(t);
1347 if (p && (os_atomic_load(&p->p_vfs_iopolicy, relaxed) & P_VFS_IOPOLICY_NOCACHE_WRITE_FS_BLKSIZE)) {
1348 return TRUE;
1349 }
1350
1351 return FALSE;
1352}
1353
1354/*
1355 * vfs_context_proc
1356 *
1357 * Description: Given a vfs_context_t, return the proc_t associated with it.
1358 *
1359 * Parameters: vfs_context_t The context to use
1360 *
1361 * Returns: proc_t The process for this context
1362 *
1363 * Notes: This function will return the current_proc() if any of the
1364 * following conditions are true:
1365 *
1366 * o The supplied context pointer is NULL
1367 * o There is no Mach thread associated with the context
1368 * o There is no Mach task associated with the Mach thread
1369 * o There is no proc_t associated with the Mach task
1370 * o The proc_t has no per process open file table
1371 *
1372 * This causes this function to return a value matching as
1373 * closely as possible the previous behaviour.
1374 */
1375proc_t
1376vfs_context_proc(vfs_context_t ctx)
1377{
1378 proc_t proc = NULL;
1379 thread_t t;
1380
1381 if (ctx != NULL && (t = VFS_CONTEXT_GET_THREAD(ctx)) != NULL) {
1382 proc = (proc_t)get_bsdthreadtask_info(t);
1383 }
1384
1385 return proc == NULL ? current_proc() : proc;
1386}
1387
1388/*
1389 * vfs_context_get_special_port
1390 *
1391 * Description: Return the requested special port from the task associated
1392 * with the given context.
1393 *
1394 * Parameters: vfs_context_t The context to use
1395 * int Index of special port
1396 * ipc_port_t * Pointer to returned port
1397 *
1398 * Returns: kern_return_t see task_get_special_port()
1399 */
1400kern_return_t
1401vfs_context_get_special_port(vfs_context_t ctx, int which, ipc_port_t *portp)
1402{
1403 return task_get_special_port(task: vfs_context_task(ctx), which_port: which, special_port: portp);
1404}
1405
1406/*
1407 * vfs_context_set_special_port
1408 *
1409 * Description: Set the requested special port in the task associated
1410 * with the given context.
1411 *
1412 * Parameters: vfs_context_t The context to use
1413 * int Index of special port
1414 * ipc_port_t New special port
1415 *
1416 * Returns: kern_return_t see task_set_special_port_internal()
1417 */
1418kern_return_t
1419vfs_context_set_special_port(vfs_context_t ctx, int which, ipc_port_t port)
1420{
1421 return task_set_special_port_internal(task: vfs_context_task(ctx),
1422 which, port);
1423}
1424
1425/*
1426 * vfs_context_thread
1427 *
1428 * Description: Return the Mach thread associated with a vfs_context_t
1429 *
1430 * Parameters: vfs_context_t The context to use
1431 *
1432 * Returns: thread_t The thread for this context, or
1433 * NULL, if there is not one.
1434 *
1435 * Notes: NULL thread_t's are legal, but discouraged. They occur only
1436 * as a result of a static vfs_context_t declaration in a function
1437 * and will result in this function returning NULL.
1438 *
1439 * This is intentional; this function should NOT return the
1440 * current_thread() in this case.
1441 */
1442thread_t
1443vfs_context_thread(vfs_context_t ctx)
1444{
1445 return VFS_CONTEXT_GET_THREAD(ctx);
1446}
1447
1448/*
1449 * vfs_context_task
1450 *
1451 * Description: Return the Mach task associated with a vfs_context_t
1452 *
1453 * Parameters: vfs_context_t The context to use
1454 *
1455 * Returns: task_t The task for this context, or
1456 * NULL, if there is not one.
1457 *
1458 * Notes: NULL task_t's are legal, but discouraged. They occur only
1459 * as a result of a static vfs_context_t declaration in a function
1460 * and will result in this function returning NULL.
1461 *
1462 * This is intentional; this function should NOT return the
1463 * task associated with current_thread() in this case.
1464 */
1465task_t
1466vfs_context_task(vfs_context_t ctx)
1467{
1468 task_t task = NULL;
1469 thread_t t;
1470
1471 if (ctx != NULL && (t = VFS_CONTEXT_GET_THREAD(ctx)) != NULL) {
1472 task = get_threadtask(t);
1473 }
1474
1475 return task;
1476}
1477
1478/*
1479 * vfs_context_cwd
1480 *
1481 * Description: Returns a reference on the vnode for the current working
1482 * directory for the supplied context
1483 *
1484 * Parameters: vfs_context_t The context to use
1485 *
1486 * Returns: vnode_t The current working directory
1487 * for this context
1488 *
1489 * Notes: The function first attempts to obtain the current directory
1490 * from the thread, and if it is not present there, falls back
1491 * to obtaining it from the process instead. If it can't be
1492 * obtained from either place, we return NULLVP.
1493 */
1494vnode_t
1495vfs_context_cwd(vfs_context_t ctx)
1496{
1497 vnode_t cwd = NULLVP;
1498 thread_t t;
1499
1500 if (ctx != NULL && (t = VFS_CONTEXT_GET_THREAD(ctx)) != NULL) {
1501 uthread_t uth = get_bsdthread_info(t);
1502 proc_t proc;
1503
1504 /*
1505 * Get the cwd from the thread; if there isn't one, get it
1506 * from the process, instead.
1507 */
1508 if ((cwd = uth->uu_cdir) == NULLVP &&
1509 (proc = (proc_t)get_bsdthreadtask_info(t)) != NULL) {
1510 cwd = proc->p_fd.fd_cdir;
1511 }
1512 }
1513
1514 return cwd;
1515}
1516
1517/*
1518 * vfs_context_create
1519 *
1520 * Description: Allocate and initialize a new context.
1521 *
1522 * Parameters: vfs_context_t: Context to copy, or NULL for new
1523 *
1524 * Returns: Pointer to new context
1525 *
1526 * Notes: Copy cred and thread from argument, if available; else
1527 * initialize with current thread and new cred. Returns
1528 * with a reference held on the credential.
1529 */
1530vfs_context_t
1531vfs_context_create(vfs_context_t ctx)
1532{
1533 vfs_context_t newcontext;
1534
1535 newcontext = zalloc_flags(KT_VFS_CONTEXT, Z_WAITOK | Z_ZERO | Z_NOFAIL);
1536
1537 if (ctx == NULL) {
1538 ctx = vfs_context_current();
1539 }
1540 *newcontext = *ctx;
1541 if (IS_VALID_CRED(ctx->vc_ucred)) {
1542 kauth_cred_ref(cred: ctx->vc_ucred);
1543 }
1544
1545 return newcontext;
1546}
1547
1548/*
1549 * vfs_context_create_with_proc
1550 *
1551 * Description: Create a new context with credentials taken from
1552 * the specified proc.
1553 *
1554 * Parameters: proc_t: The process whose crendials to use.
1555 *
1556 * Returns: Pointer to new context.
1557 *
1558 * Notes: The context will also take a reference on an arbitrary
1559 * thread in the process as well as the process's credentials.
1560 */
1561vfs_context_t
1562vfs_context_create_with_proc(proc_t p)
1563{
1564 vfs_context_t newcontext;
1565 thread_t thread;
1566 kauth_cred_t cred;
1567
1568 if (p == current_proc()) {
1569 return vfs_context_create(NULL);
1570 }
1571
1572 newcontext = zalloc_flags(KT_VFS_CONTEXT, Z_WAITOK | Z_ZERO | Z_NOFAIL);
1573
1574 proc_lock(p);
1575 thread = proc_thread(p); /* XXX */
1576 if (thread != NULL) {
1577 thread_reference(thread);
1578 }
1579 proc_unlock(p);
1580
1581 cred = kauth_cred_proc_ref(procp: p);
1582
1583 VFS_CONTEXT_SET_REFERENCED_THREAD(newcontext, thread);
1584 newcontext->vc_ucred = cred;
1585
1586 return newcontext;
1587}
1588
1589vfs_context_t
1590vfs_context_current(void)
1591{
1592 static_assert(offsetof(struct thread_ro, tro_owner) ==
1593 offsetof(struct vfs_context, vc_thread));
1594 static_assert(offsetof(struct thread_ro, tro_cred) ==
1595 offsetof(struct vfs_context, vc_ucred));
1596
1597 return (vfs_context_t)current_thread_ro();
1598}
1599
1600vfs_context_t
1601vfs_context_kernel(void)
1602{
1603 return &vfs_context0;
1604}
1605
1606int
1607vfs_context_rele(vfs_context_t ctx)
1608{
1609 if (ctx) {
1610 if (IS_VALID_CRED(ctx->vc_ucred)) {
1611 kauth_cred_unref(&ctx->vc_ucred);
1612 }
1613 if (VFS_CONTEXT_THREAD_IS_REFERENCED(ctx)) {
1614 assert(VFS_CONTEXT_GET_THREAD(ctx) != NULL);
1615 thread_deallocate(VFS_CONTEXT_GET_THREAD(ctx));
1616 }
1617 zfree(KT_VFS_CONTEXT, ctx);
1618 }
1619 return 0;
1620}
1621
1622
1623kauth_cred_t
1624vfs_context_ucred(vfs_context_t ctx)
1625{
1626 return ctx->vc_ucred;
1627}
1628
1629/*
1630 * Return true if the context is owned by the superuser.
1631 */
1632int
1633vfs_context_issuser(vfs_context_t ctx)
1634{
1635 return kauth_cred_issuser(cred: vfs_context_ucred(ctx));
1636}
1637
1638int
1639vfs_context_iskernel(vfs_context_t ctx)
1640{
1641 return ctx == &vfs_context0;
1642}
1643
1644/*
1645 * Given a context, for all fields of vfs_context_t which
1646 * are not held with a reference, set those fields to the
1647 * values for the current execution context.
1648 *
1649 * Returns: 0 for success, nonzero for failure
1650 *
1651 * The intended use is:
1652 * 1. vfs_context_create() gets the caller a context
1653 * 2. vfs_context_bind() sets the unrefcounted data
1654 * 3. vfs_context_rele() releases the context
1655 *
1656 */
1657int
1658vfs_context_bind(vfs_context_t ctx)
1659{
1660 assert(!VFS_CONTEXT_THREAD_IS_REFERENCED(ctx));
1661 ctx->vc_thread = current_thread();
1662 return 0;
1663}
1664
1665int
1666vfs_set_thread_fs_private(uint8_t tag, uint64_t fs_private)
1667{
1668 struct uthread *ut;
1669
1670 if (tag != FS_PRIVATE_TAG_APFS) {
1671 return ENOTSUP;
1672 }
1673
1674 ut = current_uthread();
1675 ut->t_fs_private = fs_private;
1676
1677 return 0;
1678}
1679
1680int
1681vfs_get_thread_fs_private(uint8_t tag, uint64_t *fs_private)
1682{
1683 struct uthread *ut;
1684
1685 if (tag != FS_PRIVATE_TAG_APFS) {
1686 return ENOTSUP;
1687 }
1688
1689 ut = current_uthread();
1690 *fs_private = ut->t_fs_private;
1691
1692 return 0;
1693}
1694
1695int
1696vfs_isswapmount(mount_t mnt)
1697{
1698 return mnt && ISSET(mnt->mnt_kern_flag, MNTK_SWAP_MOUNT) ? 1 : 0;
1699}
1700
1701/* XXXXXXXXXXXXXX VNODE KAPIS XXXXXXXXXXXXXXXXXXXXXXXXX */
1702
1703
1704/*
1705 * Convert between vnode types and inode formats (since POSIX.1
1706 * defines mode word of stat structure in terms of inode formats).
1707 */
1708enum vtype
1709vnode_iftovt(int mode)
1710{
1711 return iftovt_tab[((mode) & S_IFMT) >> 12];
1712}
1713
1714int
1715vnode_vttoif(enum vtype indx)
1716{
1717 return vttoif_tab[(int)(indx)];
1718}
1719
1720int
1721vnode_makeimode(int indx, int mode)
1722{
1723 return (int)(VTTOIF(indx) | (mode));
1724}
1725
1726
1727/*
1728 * vnode manipulation functions.
1729 */
1730
1731/* returns system root vnode iocount; It should be released using vnode_put() */
1732vnode_t
1733vfs_rootvnode(void)
1734{
1735 vnode_t vp = NULLVP;
1736
1737 if (rootvnode) {
1738 lck_rw_lock_shared(lck: &rootvnode_rw_lock);
1739 vp = rootvnode;
1740 if (vp && (vnode_get(vp) != 0)) {
1741 vp = NULLVP;
1742 }
1743 lck_rw_unlock_shared(lck: &rootvnode_rw_lock);
1744 }
1745
1746 return vp;
1747}
1748
1749uint32_t
1750vnode_vid(vnode_t vp)
1751{
1752 return (uint32_t)(vp->v_id);
1753}
1754
1755mount_t
1756vnode_mount(vnode_t vp)
1757{
1758 return vp->v_mount;
1759}
1760
1761#if CONFIG_IOSCHED
1762vnode_t
1763vnode_mountdevvp(vnode_t vp)
1764{
1765 if (vp->v_mount) {
1766 return vp->v_mount->mnt_devvp;
1767 } else {
1768 return (vnode_t)0;
1769 }
1770}
1771#endif
1772
1773boolean_t
1774vnode_isonexternalstorage(vnode_t vp)
1775{
1776 if (vp) {
1777 if (vp->v_mount) {
1778 if (vp->v_mount->mnt_ioflags & MNT_IOFLAGS_PERIPHERAL_DRIVE) {
1779 return TRUE;
1780 }
1781 }
1782 }
1783 return FALSE;
1784}
1785
1786boolean_t
1787vnode_isonssd(vnode_t vp)
1788{
1789 if (vp) {
1790 if (vp->v_mount) {
1791 if (vp->v_mount->mnt_kern_flag & MNTK_SSD) {
1792 return TRUE;
1793 }
1794 }
1795 }
1796 return FALSE;
1797}
1798
1799mount_t
1800vnode_mountedhere(vnode_t vp)
1801{
1802 mount_t mp;
1803
1804 if ((vp->v_type == VDIR) && ((mp = vp->v_mountedhere) != NULL) &&
1805 (mp->mnt_vnodecovered == vp)) {
1806 return mp;
1807 } else {
1808 return (mount_t)NULL;
1809 }
1810}
1811
1812/* returns vnode type of vnode_t */
1813enum vtype
1814vnode_vtype(vnode_t vp)
1815{
1816 return vp->v_type;
1817}
1818
1819/* returns FS specific node saved in vnode */
1820void *
1821vnode_fsnode(vnode_t vp)
1822{
1823 return vp->v_data;
1824}
1825
1826void
1827vnode_clearfsnode(vnode_t vp)
1828{
1829 vp->v_data = NULL;
1830}
1831
1832dev_t
1833vnode_specrdev(vnode_t vp)
1834{
1835 return vp->v_rdev;
1836}
1837
1838
1839/* Accessor functions */
1840/* is vnode_t a root vnode */
1841int
1842vnode_isvroot(vnode_t vp)
1843{
1844 return (vp->v_flag & VROOT)? 1 : 0;
1845}
1846
1847/* is vnode_t a system vnode */
1848int
1849vnode_issystem(vnode_t vp)
1850{
1851 return (vp->v_flag & VSYSTEM)? 1 : 0;
1852}
1853
1854/* is vnode_t a swap file vnode */
1855int
1856vnode_isswap(vnode_t vp)
1857{
1858 return (vp->v_flag & VSWAP)? 1 : 0;
1859}
1860
1861/* is vnode_t a tty */
1862int
1863vnode_istty(vnode_t vp)
1864{
1865 return (vp->v_flag & VISTTY) ? 1 : 0;
1866}
1867
1868/* if vnode_t mount operation in progress */
1869int
1870vnode_ismount(vnode_t vp)
1871{
1872 return (vp->v_flag & VMOUNT)? 1 : 0;
1873}
1874
1875/* is this vnode under recyle now */
1876int
1877vnode_isrecycled(vnode_t vp)
1878{
1879 int ret;
1880
1881 vnode_lock_spin(vp);
1882 ret = (vp->v_lflag & (VL_TERMINATE | VL_DEAD))? 1 : 0;
1883 vnode_unlock(vp);
1884 return ret;
1885}
1886
1887/* is this vnode marked for termination */
1888int
1889vnode_willberecycled(vnode_t vp)
1890{
1891 return (vp->v_lflag & VL_MARKTERM) ? 1 : 0;
1892}
1893
1894
1895/* vnode was created by background task requesting rapid aging
1896 * and has not since been referenced by a normal task */
1897int
1898vnode_israge(vnode_t vp)
1899{
1900 return (vp->v_flag & VRAGE)? 1 : 0;
1901}
1902
1903int
1904vnode_needssnapshots(__unused vnode_t vp)
1905{
1906 return 0;
1907}
1908
1909
1910/* Check the process/thread to see if we should skip atime updates */
1911int
1912vfs_ctx_skipatime(vfs_context_t ctx)
1913{
1914 struct uthread *ut;
1915 proc_t proc;
1916 thread_t thr;
1917
1918 proc = vfs_context_proc(ctx);
1919 thr = vfs_context_thread(ctx);
1920
1921 /* Validate pointers in case we were invoked via a kernel context */
1922 if (thr && proc) {
1923 ut = get_bsdthread_info(thr);
1924
1925 if (proc->p_lflag & P_LRAGE_VNODES) {
1926 return 1;
1927 }
1928
1929 if (ut) {
1930 if (ut->uu_flag & (UT_RAGE_VNODES | UT_ATIME_UPDATE)) {
1931 return 1;
1932 }
1933 }
1934
1935 if (proc->p_vfs_iopolicy & P_VFS_IOPOLICY_ATIME_UPDATES) {
1936 return 1;
1937 }
1938 }
1939 return 0;
1940}
1941
1942/* is vnode_t marked to not keep data cached once it's been consumed */
1943int
1944vnode_isnocache(vnode_t vp)
1945{
1946 return (vp->v_flag & VNOCACHE_DATA)? 1 : 0;
1947}
1948
1949/*
1950 * has sequential readahead been disabled on this vnode
1951 */
1952int
1953vnode_isnoreadahead(vnode_t vp)
1954{
1955 return (vp->v_flag & VRAOFF)? 1 : 0;
1956}
1957
1958int
1959vnode_is_openevt(vnode_t vp)
1960{
1961 return (vp->v_flag & VOPENEVT)? 1 : 0;
1962}
1963
1964/* is vnode_t a standard one? */
1965int
1966vnode_isstandard(vnode_t vp)
1967{
1968 return (vp->v_flag & VSTANDARD)? 1 : 0;
1969}
1970
1971/* don't vflush() if SKIPSYSTEM */
1972int
1973vnode_isnoflush(vnode_t vp)
1974{
1975 return (vp->v_flag & VNOFLUSH)? 1 : 0;
1976}
1977
1978/* is vnode_t a regular file */
1979int
1980vnode_isreg(vnode_t vp)
1981{
1982 return (vp->v_type == VREG)? 1 : 0;
1983}
1984
1985/* is vnode_t a directory? */
1986int
1987vnode_isdir(vnode_t vp)
1988{
1989 return (vp->v_type == VDIR)? 1 : 0;
1990}
1991
1992/* is vnode_t a symbolic link ? */
1993int
1994vnode_islnk(vnode_t vp)
1995{
1996 return (vp->v_type == VLNK)? 1 : 0;
1997}
1998
1999int
2000vnode_lookup_continue_needed(vnode_t vp, struct componentname *cnp)
2001{
2002 struct nameidata *ndp = cnp->cn_ndp;
2003
2004 if (ndp == NULL) {
2005 panic("vnode_lookup_continue_needed(): cnp->cn_ndp is NULL");
2006 }
2007
2008 if (vnode_isdir(vp)) {
2009 if (vp->v_mountedhere != NULL) {
2010 goto yes;
2011 }
2012
2013#if CONFIG_TRIGGERS
2014 if (vp->v_resolve) {
2015 goto yes;
2016 }
2017#endif /* CONFIG_TRIGGERS */
2018 }
2019
2020
2021 if (vnode_islnk(vp)) {
2022 /* From lookup(): || *ndp->ni_next == '/') No need for this, we know we're NULL-terminated here */
2023 if (cnp->cn_flags & FOLLOW) {
2024 goto yes;
2025 }
2026 if (ndp->ni_flag & NAMEI_TRAILINGSLASH) {
2027 goto yes;
2028 }
2029 }
2030
2031 return 0;
2032
2033yes:
2034 ndp->ni_flag |= NAMEI_CONTLOOKUP;
2035 return EKEEPLOOKING;
2036}
2037
2038/* is vnode_t a fifo ? */
2039int
2040vnode_isfifo(vnode_t vp)
2041{
2042 return (vp->v_type == VFIFO)? 1 : 0;
2043}
2044
2045/* is vnode_t a block device? */
2046int
2047vnode_isblk(vnode_t vp)
2048{
2049 return (vp->v_type == VBLK)? 1 : 0;
2050}
2051
2052int
2053vnode_isspec(vnode_t vp)
2054{
2055 return ((vp->v_type == VCHR) || (vp->v_type == VBLK)) ? 1 : 0;
2056}
2057
2058/* is vnode_t a char device? */
2059int
2060vnode_ischr(vnode_t vp)
2061{
2062 return (vp->v_type == VCHR)? 1 : 0;
2063}
2064
2065/* is vnode_t a socket? */
2066int
2067vnode_issock(vnode_t vp)
2068{
2069 return (vp->v_type == VSOCK)? 1 : 0;
2070}
2071
2072/* is vnode_t a device with multiple active vnodes referring to it? */
2073int
2074vnode_isaliased(vnode_t vp)
2075{
2076 enum vtype vt = vp->v_type;
2077 if (!((vt == VCHR) || (vt == VBLK))) {
2078 return 0;
2079 } else {
2080 return vp->v_specflags & SI_ALIASED;
2081 }
2082}
2083
2084/* is vnode_t a named stream? */
2085int
2086vnode_isnamedstream(
2087#if NAMEDSTREAMS
2088 vnode_t vp
2089#else
2090 __unused vnode_t vp
2091#endif
2092 )
2093{
2094#if NAMEDSTREAMS
2095 return (vp->v_flag & VISNAMEDSTREAM) ? 1 : 0;
2096#else
2097 return 0;
2098#endif
2099}
2100
2101int
2102vnode_isshadow(
2103#if NAMEDSTREAMS
2104 vnode_t vp
2105#else
2106 __unused vnode_t vp
2107#endif
2108 )
2109{
2110#if NAMEDSTREAMS
2111 return (vp->v_flag & VISSHADOW) ? 1 : 0;
2112#else
2113 return 0;
2114#endif
2115}
2116
2117/* does vnode have associated named stream vnodes ? */
2118int
2119vnode_hasnamedstreams(
2120#if NAMEDSTREAMS
2121 vnode_t vp
2122#else
2123 __unused vnode_t vp
2124#endif
2125 )
2126{
2127#if NAMEDSTREAMS
2128 return (vp->v_lflag & VL_HASSTREAMS) ? 1 : 0;
2129#else
2130 return 0;
2131#endif
2132}
2133/* TBD: set vnode_t to not cache data after it is consumed once; used for quota */
2134void
2135vnode_setnocache(vnode_t vp)
2136{
2137 vnode_lock_spin(vp);
2138 vp->v_flag |= VNOCACHE_DATA;
2139 vnode_unlock(vp);
2140}
2141
2142void
2143vnode_clearnocache(vnode_t vp)
2144{
2145 vnode_lock_spin(vp);
2146 vp->v_flag &= ~VNOCACHE_DATA;
2147 vnode_unlock(vp);
2148}
2149
2150void
2151vnode_set_openevt(vnode_t vp)
2152{
2153 vnode_lock_spin(vp);
2154 vp->v_flag |= VOPENEVT;
2155 vnode_unlock(vp);
2156}
2157
2158void
2159vnode_clear_openevt(vnode_t vp)
2160{
2161 vnode_lock_spin(vp);
2162 vp->v_flag &= ~VOPENEVT;
2163 vnode_unlock(vp);
2164}
2165
2166
2167void
2168vnode_setnoreadahead(vnode_t vp)
2169{
2170 vnode_lock_spin(vp);
2171 vp->v_flag |= VRAOFF;
2172 vnode_unlock(vp);
2173}
2174
2175void
2176vnode_clearnoreadahead(vnode_t vp)
2177{
2178 vnode_lock_spin(vp);
2179 vp->v_flag &= ~VRAOFF;
2180 vnode_unlock(vp);
2181}
2182
2183int
2184vnode_isfastdevicecandidate(vnode_t vp)
2185{
2186 return (vp->v_flag & VFASTDEVCANDIDATE)? 1 : 0;
2187}
2188
2189void
2190vnode_setfastdevicecandidate(vnode_t vp)
2191{
2192 vnode_lock_spin(vp);
2193 vp->v_flag |= VFASTDEVCANDIDATE;
2194 vnode_unlock(vp);
2195}
2196
2197void
2198vnode_clearfastdevicecandidate(vnode_t vp)
2199{
2200 vnode_lock_spin(vp);
2201 vp->v_flag &= ~VFASTDEVCANDIDATE;
2202 vnode_unlock(vp);
2203}
2204
2205int
2206vnode_isautocandidate(vnode_t vp)
2207{
2208 return (vp->v_flag & VAUTOCANDIDATE)? 1 : 0;
2209}
2210
2211void
2212vnode_setautocandidate(vnode_t vp)
2213{
2214 vnode_lock_spin(vp);
2215 vp->v_flag |= VAUTOCANDIDATE;
2216 vnode_unlock(vp);
2217}
2218
2219void
2220vnode_clearautocandidate(vnode_t vp)
2221{
2222 vnode_lock_spin(vp);
2223 vp->v_flag &= ~VAUTOCANDIDATE;
2224 vnode_unlock(vp);
2225}
2226
2227
2228
2229
2230/* mark vnode_t to skip vflush() is SKIPSYSTEM */
2231void
2232vnode_setnoflush(vnode_t vp)
2233{
2234 vnode_lock_spin(vp);
2235 vp->v_flag |= VNOFLUSH;
2236 vnode_unlock(vp);
2237}
2238
2239void
2240vnode_clearnoflush(vnode_t vp)
2241{
2242 vnode_lock_spin(vp);
2243 vp->v_flag &= ~VNOFLUSH;
2244 vnode_unlock(vp);
2245}
2246
2247
2248/* is vnode_t a blkdevice and has a FS mounted on it */
2249int
2250vnode_ismountedon(vnode_t vp)
2251{
2252 return (vp->v_specflags & SI_MOUNTEDON)? 1 : 0;
2253}
2254
2255void
2256vnode_setmountedon(vnode_t vp)
2257{
2258 vnode_lock_spin(vp);
2259 vp->v_specflags |= SI_MOUNTEDON;
2260 vnode_unlock(vp);
2261}
2262
2263void
2264vnode_clearmountedon(vnode_t vp)
2265{
2266 vnode_lock_spin(vp);
2267 vp->v_specflags &= ~SI_MOUNTEDON;
2268 vnode_unlock(vp);
2269}
2270
2271
2272void
2273vnode_settag(vnode_t vp, int tag)
2274{
2275 /*
2276 * We only assign enum values to v_tag, but add an assert to make sure we
2277 * catch it in dev/debug builds if this ever change.
2278 */
2279 assert(tag >= SHRT_MIN && tag <= SHRT_MAX);
2280 vp->v_tag = (uint16_t)tag;
2281}
2282
2283int
2284vnode_tag(vnode_t vp)
2285{
2286 return vp->v_tag;
2287}
2288
2289vnode_t
2290vnode_parent(vnode_t vp)
2291{
2292 return vp->v_parent;
2293}
2294
2295void
2296vnode_setparent(vnode_t vp, vnode_t dvp)
2297{
2298 vp->v_parent = dvp;
2299}
2300
2301void
2302vnode_setname(vnode_t vp, char * name)
2303{
2304 vp->v_name = name;
2305}
2306
2307/* return the registered FS name when adding the FS to kernel */
2308void
2309vnode_vfsname(vnode_t vp, char * buf)
2310{
2311 strlcpy(dst: buf, src: vp->v_mount->mnt_vtable->vfc_name, MFSNAMELEN);
2312}
2313
2314/* return the FS type number */
2315int
2316vnode_vfstypenum(vnode_t vp)
2317{
2318 return vp->v_mount->mnt_vtable->vfc_typenum;
2319}
2320
2321int
2322vnode_vfs64bitready(vnode_t vp)
2323{
2324 /*
2325 * Checking for dead_mountp is a bit of a hack for SnowLeopard: <rdar://problem/6269051>
2326 */
2327 if ((vp->v_mount != dead_mountp) && (vp->v_mount->mnt_vtable->vfc_vfsflags & VFC_VFS64BITREADY)) {
2328 return 1;
2329 } else {
2330 return 0;
2331 }
2332}
2333
2334
2335
2336/* return the visible flags on associated mount point of vnode_t */
2337uint32_t
2338vnode_vfsvisflags(vnode_t vp)
2339{
2340 return vp->v_mount->mnt_flag & MNT_VISFLAGMASK;
2341}
2342
2343/* return the command modifier flags on associated mount point of vnode_t */
2344uint32_t
2345vnode_vfscmdflags(vnode_t vp)
2346{
2347 return vp->v_mount->mnt_flag & MNT_CMDFLAGS;
2348}
2349
2350/* return the max symlink of short links of vnode_t */
2351uint32_t
2352vnode_vfsmaxsymlen(vnode_t vp)
2353{
2354 return vp->v_mount->mnt_maxsymlinklen;
2355}
2356
2357/* return a pointer to the RO vfs_statfs associated with vnode_t's mount point */
2358struct vfsstatfs *
2359vnode_vfsstatfs(vnode_t vp)
2360{
2361 return &vp->v_mount->mnt_vfsstat;
2362}
2363
2364/* return a handle to the FSs specific private handle associated with vnode_t's mount point */
2365void *
2366vnode_vfsfsprivate(vnode_t vp)
2367{
2368 return vp->v_mount->mnt_data;
2369}
2370
2371/* is vnode_t in a rdonly mounted FS */
2372int
2373vnode_vfsisrdonly(vnode_t vp)
2374{
2375 return (vp->v_mount->mnt_flag & MNT_RDONLY)? 1 : 0;
2376}
2377
2378int
2379vnode_compound_rename_available(vnode_t vp)
2380{
2381 return vnode_compound_op_available(vp, COMPOUND_VNOP_RENAME);
2382}
2383int
2384vnode_compound_rmdir_available(vnode_t vp)
2385{
2386 return vnode_compound_op_available(vp, COMPOUND_VNOP_RMDIR);
2387}
2388int
2389vnode_compound_mkdir_available(vnode_t vp)
2390{
2391 return vnode_compound_op_available(vp, COMPOUND_VNOP_MKDIR);
2392}
2393int
2394vnode_compound_remove_available(vnode_t vp)
2395{
2396 return vnode_compound_op_available(vp, COMPOUND_VNOP_REMOVE);
2397}
2398int
2399vnode_compound_open_available(vnode_t vp)
2400{
2401 return vnode_compound_op_available(vp, COMPOUND_VNOP_OPEN);
2402}
2403
2404int
2405vnode_compound_op_available(vnode_t vp, compound_vnop_id_t opid)
2406{
2407 return (vp->v_mount->mnt_compound_ops & opid) != 0;
2408}
2409
2410/*
2411 * Returns vnode ref to current working directory; if a per-thread current
2412 * working directory is in effect, return that instead of the per process one.
2413 *
2414 * XXX Published, but not used.
2415 */
2416vnode_t
2417current_workingdir(void)
2418{
2419 return vfs_context_cwd(ctx: vfs_context_current());
2420}
2421
2422/*
2423 * Get a filesec and optional acl contents from an extended attribute.
2424 * Function will attempt to retrive ACL, UUID, and GUID information using a
2425 * read of a named extended attribute (KAUTH_FILESEC_XATTR).
2426 *
2427 * Parameters: vp The vnode on which to operate.
2428 * fsecp The filesec (and ACL, if any) being
2429 * retrieved.
2430 * ctx The vnode context in which the
2431 * operation is to be attempted.
2432 *
2433 * Returns: 0 Success
2434 * !0 errno value
2435 *
2436 * Notes: The kauth_filesec_t in '*fsecp', if retrieved, will be in
2437 * host byte order, as will be the ACL contents, if any.
2438 * Internally, we will cannonize these values from network (PPC)
2439 * byte order after we retrieve them so that the on-disk contents
2440 * of the extended attribute are identical for both PPC and Intel
2441 * (if we were not being required to provide this service via
2442 * fallback, this would be the job of the filesystem
2443 * 'VNOP_GETATTR' call).
2444 *
2445 * We use ntohl() because it has a transitive property on Intel
2446 * machines and no effect on PPC mancines. This guarantees us
2447 *
2448 * XXX: Deleting rather than ignoreing a corrupt security structure is
2449 * probably the only way to reset it without assistance from an
2450 * file system integrity checking tool. Right now we ignore it.
2451 *
2452 * XXX: We should enummerate the possible errno values here, and where
2453 * in the code they originated.
2454 */
2455static int
2456vnode_get_filesec(vnode_t vp, kauth_filesec_t *fsecp, vfs_context_t ctx)
2457{
2458 kauth_filesec_t fsec;
2459 uio_t fsec_uio;
2460 size_t fsec_size;
2461 size_t xsize, rsize;
2462 int error;
2463 uint32_t host_fsec_magic;
2464 uint32_t host_acl_entrycount;
2465
2466 fsec = NULL;
2467 fsec_uio = NULL;
2468
2469 /* find out how big the EA is */
2470 error = vn_getxattr(vp, KAUTH_FILESEC_XATTR, NULL, &xsize, XATTR_NOSECURITY, ctx);
2471 if (error != 0) {
2472 /* no EA, no filesec */
2473 if ((error == ENOATTR) || (error == ENOENT) || (error == EJUSTRETURN)) {
2474 error = 0;
2475 }
2476 /* either way, we are done */
2477 goto out;
2478 }
2479
2480 /*
2481 * To be valid, a kauth_filesec_t must be large enough to hold a zero
2482 * ACE entrly ACL, and if it's larger than that, it must have the right
2483 * number of bytes such that it contains an atomic number of ACEs,
2484 * rather than partial entries. Otherwise, we ignore it.
2485 */
2486 if (!KAUTH_FILESEC_VALID(xsize)) {
2487 KAUTH_DEBUG(" ERROR - Bogus kauth_fiilesec_t: %ld bytes", xsize);
2488 error = 0;
2489 goto out;
2490 }
2491
2492 /* how many entries would fit? */
2493 fsec_size = KAUTH_FILESEC_COUNT(xsize);
2494 if (fsec_size > KAUTH_ACL_MAX_ENTRIES) {
2495 KAUTH_DEBUG(" ERROR - Bogus (too large) kauth_fiilesec_t: %ld bytes", xsize);
2496 error = 0;
2497 goto out;
2498 }
2499
2500 /* get buffer and uio */
2501 if (((fsec = kauth_filesec_alloc(size: (int)fsec_size)) == NULL) ||
2502 ((fsec_uio = uio_create(a_iovcount: 1, a_offset: 0, a_spacetype: UIO_SYSSPACE, a_iodirection: UIO_READ)) == NULL) ||
2503 uio_addiov(a_uio: fsec_uio, CAST_USER_ADDR_T(fsec), a_length: xsize)) {
2504 KAUTH_DEBUG(" ERROR - could not allocate iov to read ACL");
2505 error = ENOMEM;
2506 goto out;
2507 }
2508
2509 /* read security attribute */
2510 rsize = xsize;
2511 if ((error = vn_getxattr(vp,
2512 KAUTH_FILESEC_XATTR,
2513 fsec_uio,
2514 &rsize,
2515 XATTR_NOSECURITY,
2516 ctx)) != 0) {
2517 /* no attribute - no security data */
2518 if ((error == ENOATTR) || (error == ENOENT) || (error == EJUSTRETURN)) {
2519 error = 0;
2520 }
2521 /* either way, we are done */
2522 goto out;
2523 }
2524
2525 /*
2526 * Validate security structure; the validation must take place in host
2527 * byte order. If it's corrupt, we will just ignore it.
2528 */
2529
2530 /* Validate the size before trying to convert it */
2531 if (rsize < KAUTH_FILESEC_SIZE(0)) {
2532 KAUTH_DEBUG("ACL - DATA TOO SMALL (%d)", rsize);
2533 goto out;
2534 }
2535
2536 /* Validate the magic number before trying to convert it */
2537 host_fsec_magic = ntohl(KAUTH_FILESEC_MAGIC);
2538 if (fsec->fsec_magic != host_fsec_magic) {
2539 KAUTH_DEBUG("ACL - BAD MAGIC %x", host_fsec_magic);
2540 goto out;
2541 }
2542
2543 /* Validate the entry count before trying to convert it. */
2544 host_acl_entrycount = ntohl(fsec->fsec_acl.acl_entrycount);
2545 if (host_acl_entrycount != KAUTH_FILESEC_NOACL) {
2546 if (host_acl_entrycount > KAUTH_ACL_MAX_ENTRIES) {
2547 KAUTH_DEBUG("ACL - BAD ENTRYCOUNT %x", host_acl_entrycount);
2548 goto out;
2549 }
2550 if (KAUTH_FILESEC_SIZE(host_acl_entrycount) > rsize) {
2551 KAUTH_DEBUG("ACL - BUFFER OVERFLOW (%d entries too big for %d)", host_acl_entrycount, rsize);
2552 goto out;
2553 }
2554 }
2555
2556 kauth_filesec_acl_setendian(KAUTH_ENDIAN_HOST, fsec, NULL);
2557
2558 *fsecp = fsec;
2559 fsec = NULL;
2560 error = 0;
2561out:
2562 if (fsec != NULL) {
2563 kauth_filesec_free(fsp: fsec);
2564 }
2565 if (fsec_uio != NULL) {
2566 uio_free(a_uio: fsec_uio);
2567 }
2568 if (error) {
2569 *fsecp = NULL;
2570 }
2571 return error;
2572}
2573
2574/*
2575 * Set a filesec and optional acl contents into an extended attribute.
2576 * function will attempt to store ACL, UUID, and GUID information using a
2577 * write to a named extended attribute (KAUTH_FILESEC_XATTR). The 'acl'
2578 * may or may not point to the `fsec->fsec_acl`, depending on whether the
2579 * original caller supplied an acl.
2580 *
2581 * Parameters: vp The vnode on which to operate.
2582 * fsec The filesec being set.
2583 * acl The acl to be associated with 'fsec'.
2584 * ctx The vnode context in which the
2585 * operation is to be attempted.
2586 *
2587 * Returns: 0 Success
2588 * !0 errno value
2589 *
2590 * Notes: Both the fsec and the acl are always valid.
2591 *
2592 * The kauth_filesec_t in 'fsec', if any, is in host byte order,
2593 * as are the acl contents, if they are used. Internally, we will
2594 * cannonize these values into network (PPC) byte order before we
2595 * attempt to write them so that the on-disk contents of the
2596 * extended attribute are identical for both PPC and Intel (if we
2597 * were not being required to provide this service via fallback,
2598 * this would be the job of the filesystem 'VNOP_SETATTR' call).
2599 * We reverse this process on the way out, so we leave with the
2600 * same byte order we started with.
2601 *
2602 * XXX: We should enummerate the possible errno values here, and where
2603 * in the code they originated.
2604 */
2605static int
2606vnode_set_filesec(vnode_t vp, kauth_filesec_t fsec, kauth_acl_t acl, vfs_context_t ctx)
2607{
2608 uio_t fsec_uio;
2609 int error;
2610 uint32_t saved_acl_copysize;
2611
2612 fsec_uio = NULL;
2613
2614 if ((fsec_uio = uio_create(a_iovcount: 2, a_offset: 0, a_spacetype: UIO_SYSSPACE, a_iodirection: UIO_WRITE)) == NULL) {
2615 KAUTH_DEBUG(" ERROR - could not allocate iov to write ACL");
2616 error = ENOMEM;
2617 goto out;
2618 }
2619 /*
2620 * Save the pre-converted ACL copysize, because it gets swapped too
2621 * if we are running with the wrong endianness.
2622 */
2623 saved_acl_copysize = KAUTH_ACL_COPYSIZE(acl);
2624
2625 kauth_filesec_acl_setendian(KAUTH_ENDIAN_DISK, fsec, acl);
2626
2627 uio_addiov(a_uio: fsec_uio, CAST_USER_ADDR_T(fsec), KAUTH_FILESEC_SIZE(0) - KAUTH_ACL_SIZE(KAUTH_FILESEC_NOACL));
2628 uio_addiov(a_uio: fsec_uio, CAST_USER_ADDR_T(acl), a_length: saved_acl_copysize);
2629 error = vn_setxattr(vp,
2630 KAUTH_FILESEC_XATTR,
2631 fsec_uio,
2632 XATTR_NOSECURITY, /* we have auth'ed already */
2633 ctx);
2634 VFS_DEBUG(ctx, vp, "SETATTR - set ACL returning %d", error);
2635
2636 kauth_filesec_acl_setendian(KAUTH_ENDIAN_HOST, fsec, acl);
2637
2638out:
2639 if (fsec_uio != NULL) {
2640 uio_free(a_uio: fsec_uio);
2641 }
2642 return error;
2643}
2644
2645/*
2646 * Handle uid/gid == 99 and MNT_IGNORE_OWNERSHIP here.
2647 */
2648void
2649vnode_attr_handle_uid_and_gid(struct vnode_attr *vap, mount_t mp, vfs_context_t ctx)
2650{
2651 uid_t nuid;
2652 gid_t ngid;
2653 bool is_suser = vfs_context_issuser(ctx) ? true : false;
2654
2655 if (VATTR_IS_ACTIVE(vap, va_uid)) {
2656 if (is_suser && VATTR_IS_SUPPORTED(vap, va_uid)) {
2657 nuid = vap->va_uid;
2658 } else if (mp->mnt_flag & MNT_IGNORE_OWNERSHIP) {
2659 nuid = mp->mnt_fsowner;
2660 if (nuid == KAUTH_UID_NONE) {
2661 nuid = 99;
2662 }
2663 } else if (VATTR_IS_SUPPORTED(vap, va_uid)) {
2664 nuid = vap->va_uid;
2665 } else {
2666 /* this will always be something sensible */
2667 nuid = mp->mnt_fsowner;
2668 }
2669 if ((nuid == 99) && !is_suser) {
2670 nuid = kauth_cred_getuid(cred: vfs_context_ucred(ctx));
2671 }
2672 VATTR_RETURN(vap, va_uid, nuid);
2673 }
2674 if (VATTR_IS_ACTIVE(vap, va_gid)) {
2675 if (is_suser && VATTR_IS_SUPPORTED(vap, va_gid)) {
2676 ngid = vap->va_gid;
2677 } else if (mp->mnt_flag & MNT_IGNORE_OWNERSHIP) {
2678 ngid = mp->mnt_fsgroup;
2679 if (ngid == KAUTH_GID_NONE) {
2680 ngid = 99;
2681 }
2682 } else if (VATTR_IS_SUPPORTED(vap, va_gid)) {
2683 ngid = vap->va_gid;
2684 } else {
2685 /* this will always be something sensible */
2686 ngid = mp->mnt_fsgroup;
2687 }
2688 if ((ngid == 99) && !is_suser) {
2689 ngid = kauth_cred_getgid(cred: vfs_context_ucred(ctx));
2690 }
2691 VATTR_RETURN(vap, va_gid, ngid);
2692 }
2693}
2694
2695/*
2696 * Returns: 0 Success
2697 * ENOMEM Not enough space [only if has filesec]
2698 * EINVAL Requested unknown attributes
2699 * VNOP_GETATTR: ???
2700 * vnode_get_filesec: ???
2701 * kauth_cred_guid2uid: ???
2702 * kauth_cred_guid2gid: ???
2703 * vfs_update_vfsstat: ???
2704 */
2705int
2706vnode_getattr(vnode_t vp, struct vnode_attr *vap, vfs_context_t ctx)
2707{
2708 kauth_filesec_t fsec;
2709 kauth_acl_t facl;
2710 int error;
2711
2712 /*
2713 * Reject attempts to fetch unknown attributes.
2714 */
2715 if (vap->va_active & ~VNODE_ATTR_ALL) {
2716 return EINVAL;
2717 }
2718
2719 /* don't ask for extended security data if the filesystem doesn't support it */
2720 if (!vfs_extendedsecurity(mp: vnode_mount(vp))) {
2721 VATTR_CLEAR_ACTIVE(vap, va_acl);
2722 VATTR_CLEAR_ACTIVE(vap, va_uuuid);
2723 VATTR_CLEAR_ACTIVE(vap, va_guuid);
2724 }
2725
2726 /*
2727 * If the caller wants size values we might have to synthesise, give the
2728 * filesystem the opportunity to supply better intermediate results.
2729 */
2730 if (VATTR_IS_ACTIVE(vap, va_data_alloc) ||
2731 VATTR_IS_ACTIVE(vap, va_total_size) ||
2732 VATTR_IS_ACTIVE(vap, va_total_alloc)) {
2733 VATTR_SET_ACTIVE(vap, va_data_size);
2734 VATTR_SET_ACTIVE(vap, va_data_alloc);
2735 VATTR_SET_ACTIVE(vap, va_total_size);
2736 VATTR_SET_ACTIVE(vap, va_total_alloc);
2737 }
2738
2739 vap->va_vaflags &= ~VA_USEFSID;
2740
2741 error = VNOP_GETATTR(vp, vap, ctx);
2742 if (error) {
2743 KAUTH_DEBUG("ERROR - returning %d", error);
2744 goto out;
2745 }
2746
2747 /*
2748 * If extended security data was requested but not returned, try the fallback
2749 * path.
2750 */
2751 if (VATTR_NOT_RETURNED(vap, va_acl) || VATTR_NOT_RETURNED(vap, va_uuuid) || VATTR_NOT_RETURNED(vap, va_guuid)) {
2752 fsec = NULL;
2753
2754 if (XATTR_VNODE_SUPPORTED(vp)) {
2755 /* try to get the filesec */
2756 if ((error = vnode_get_filesec(vp, fsecp: &fsec, ctx)) != 0) {
2757 goto out;
2758 }
2759 }
2760 /* if no filesec, no attributes */
2761 if (fsec == NULL) {
2762 VATTR_RETURN(vap, va_acl, NULL);
2763 VATTR_RETURN(vap, va_uuuid, kauth_null_guid);
2764 VATTR_RETURN(vap, va_guuid, kauth_null_guid);
2765 } else {
2766 /* looks good, try to return what we were asked for */
2767 VATTR_RETURN(vap, va_uuuid, fsec->fsec_owner);
2768 VATTR_RETURN(vap, va_guuid, fsec->fsec_group);
2769
2770 /* only return the ACL if we were actually asked for it */
2771 if (VATTR_IS_ACTIVE(vap, va_acl)) {
2772 if (fsec->fsec_acl.acl_entrycount == KAUTH_FILESEC_NOACL) {
2773 VATTR_RETURN(vap, va_acl, NULL);
2774 } else {
2775 facl = kauth_acl_alloc(size: fsec->fsec_acl.acl_entrycount);
2776 if (facl == NULL) {
2777 kauth_filesec_free(fsp: fsec);
2778 error = ENOMEM;
2779 goto out;
2780 }
2781 __nochk_bcopy(src: &fsec->fsec_acl, dst: facl, KAUTH_ACL_COPYSIZE(&fsec->fsec_acl));
2782 VATTR_RETURN(vap, va_acl, facl);
2783 }
2784 }
2785 kauth_filesec_free(fsp: fsec);
2786 }
2787 }
2788 /*
2789 * If someone gave us an unsolicited filesec, toss it. We promise that
2790 * we're OK with a filesystem giving us anything back, but our callers
2791 * only expect what they asked for.
2792 */
2793 if (VATTR_IS_SUPPORTED(vap, va_acl) && !VATTR_IS_ACTIVE(vap, va_acl)) {
2794 if (vap->va_acl != NULL) {
2795 kauth_acl_free(fsp: vap->va_acl);
2796 }
2797 VATTR_CLEAR_SUPPORTED(vap, va_acl);
2798 }
2799
2800#if 0 /* enable when we have a filesystem only supporting UUIDs */
2801 /*
2802 * Handle the case where we need a UID/GID, but only have extended
2803 * security information.
2804 */
2805 if (VATTR_NOT_RETURNED(vap, va_uid) &&
2806 VATTR_IS_SUPPORTED(vap, va_uuuid) &&
2807 !kauth_guid_equal(&vap->va_uuuid, &kauth_null_guid)) {
2808 if ((error = kauth_cred_guid2uid(&vap->va_uuuid, &nuid)) == 0) {
2809 VATTR_RETURN(vap, va_uid, nuid);
2810 }
2811 }
2812 if (VATTR_NOT_RETURNED(vap, va_gid) &&
2813 VATTR_IS_SUPPORTED(vap, va_guuid) &&
2814 !kauth_guid_equal(&vap->va_guuid, &kauth_null_guid)) {
2815 if ((error = kauth_cred_guid2gid(&vap->va_guuid, &ngid)) == 0) {
2816 VATTR_RETURN(vap, va_gid, ngid);
2817 }
2818 }
2819#endif
2820
2821 vnode_attr_handle_uid_and_gid(vap, mp: vp->v_mount, ctx);
2822
2823 /*
2824 * Synthesise some values that can be reasonably guessed.
2825 */
2826 if (!VATTR_IS_SUPPORTED(vap, va_iosize)) {
2827 assert(vp->v_mount->mnt_vfsstat.f_iosize <= UINT32_MAX);
2828 VATTR_RETURN(vap, va_iosize, (uint32_t)vp->v_mount->mnt_vfsstat.f_iosize);
2829 }
2830
2831 if (!VATTR_IS_SUPPORTED(vap, va_flags)) {
2832 VATTR_RETURN(vap, va_flags, 0);
2833 }
2834
2835 if (!VATTR_IS_SUPPORTED(vap, va_filerev)) {
2836 VATTR_RETURN(vap, va_filerev, 0);
2837 }
2838
2839 if (!VATTR_IS_SUPPORTED(vap, va_gen)) {
2840 VATTR_RETURN(vap, va_gen, 0);
2841 }
2842
2843 /*
2844 * Default sizes. Ordering here is important, as later defaults build on earlier ones.
2845 */
2846 if (!VATTR_IS_SUPPORTED(vap, va_data_size)) {
2847 VATTR_RETURN(vap, va_data_size, 0);
2848 }
2849
2850 /* do we want any of the possibly-computed values? */
2851 if (VATTR_IS_ACTIVE(vap, va_data_alloc) ||
2852 VATTR_IS_ACTIVE(vap, va_total_size) ||
2853 VATTR_IS_ACTIVE(vap, va_total_alloc)) {
2854 /* make sure f_bsize is valid */
2855 if (vp->v_mount->mnt_vfsstat.f_bsize == 0) {
2856 if ((error = vfs_update_vfsstat(mp: vp->v_mount, ctx, VFS_KERNEL_EVENT)) != 0) {
2857 goto out;
2858 }
2859 }
2860
2861 /* default va_data_alloc from va_data_size */
2862 if (!VATTR_IS_SUPPORTED(vap, va_data_alloc)) {
2863 VATTR_RETURN(vap, va_data_alloc, roundup(vap->va_data_size, vp->v_mount->mnt_vfsstat.f_bsize));
2864 }
2865
2866 /* default va_total_size from va_data_size */
2867 if (!VATTR_IS_SUPPORTED(vap, va_total_size)) {
2868 VATTR_RETURN(vap, va_total_size, vap->va_data_size);
2869 }
2870
2871 /* default va_total_alloc from va_total_size which is guaranteed at this point */
2872 if (!VATTR_IS_SUPPORTED(vap, va_total_alloc)) {
2873 VATTR_RETURN(vap, va_total_alloc, roundup(vap->va_total_size, vp->v_mount->mnt_vfsstat.f_bsize));
2874 }
2875 }
2876
2877 /*
2878 * If we don't have a change time, pull it from the modtime.
2879 */
2880 if (!VATTR_IS_SUPPORTED(vap, va_change_time) && VATTR_IS_SUPPORTED(vap, va_modify_time)) {
2881 VATTR_RETURN(vap, va_change_time, vap->va_modify_time);
2882 }
2883
2884 /*
2885 * This is really only supported for the creation VNOPs, but since the field is there
2886 * we should populate it correctly.
2887 */
2888 VATTR_RETURN(vap, va_type, vp->v_type);
2889
2890 /*
2891 * The fsid can be obtained from the mountpoint directly.
2892 */
2893 if (VATTR_IS_ACTIVE(vap, va_fsid) &&
2894 (!VATTR_IS_SUPPORTED(vap, va_fsid) ||
2895 vap->va_vaflags & VA_REALFSID || !(vap->va_vaflags & VA_USEFSID))) {
2896 VATTR_RETURN(vap, va_fsid, vp->v_mount->mnt_vfsstat.f_fsid.val[0]);
2897 }
2898
2899out:
2900 vap->va_vaflags &= ~VA_USEFSID;
2901
2902 return error;
2903}
2904
2905/*
2906 * Choose 32 bit or 64 bit fsid
2907 */
2908uint64_t
2909vnode_get_va_fsid(struct vnode_attr *vap)
2910{
2911 if (VATTR_IS_SUPPORTED(vap, va_fsid64)) {
2912 return (uint64_t)vap->va_fsid64.val[0] + ((uint64_t)vap->va_fsid64.val[1] << 32);
2913 }
2914 return vap->va_fsid;
2915}
2916
2917/*
2918 * Set the attributes on a vnode in a vnode context.
2919 *
2920 * Parameters: vp The vnode whose attributes to set.
2921 * vap A pointer to the attributes to set.
2922 * ctx The vnode context in which the
2923 * operation is to be attempted.
2924 *
2925 * Returns: 0 Success
2926 * !0 errno value
2927 *
2928 * Notes: The kauth_filesec_t in 'vap', if any, is in host byte order.
2929 *
2930 * The contents of the data area pointed to by 'vap' may be
2931 * modified if the vnode is on a filesystem which has been
2932 * mounted with ingore ownership flags, or by the underlyng
2933 * VFS itself, or by the fallback code, if the underlying VFS
2934 * does not support ACL, UUID, or GUUID attributes directly.
2935 *
2936 * XXX: We should enummerate the possible errno values here, and where
2937 * in the code they originated.
2938 */
2939int
2940vnode_setattr(vnode_t vp, struct vnode_attr *vap, vfs_context_t ctx)
2941{
2942 int error;
2943#if CONFIG_FSE
2944 uint64_t active;
2945 int is_perm_change = 0;
2946 int is_stat_change = 0;
2947#endif
2948
2949 /*
2950 * Reject attempts to set unknown attributes.
2951 */
2952 if (vap->va_active & ~VNODE_ATTR_ALL) {
2953 return EINVAL;
2954 }
2955
2956 /*
2957 * Make sure the filesystem is mounted R/W.
2958 * If not, return an error.
2959 */
2960 if (vfs_isrdonly(mp: vp->v_mount)) {
2961 error = EROFS;
2962 goto out;
2963 }
2964
2965#if DEVELOPMENT || DEBUG
2966 /*
2967 * XXX VSWAP: Check for entitlements or special flag here
2968 * so we can restrict access appropriately.
2969 */
2970#else /* DEVELOPMENT || DEBUG */
2971
2972 if (vnode_isswap(vp) && (ctx != vfs_context_kernel())) {
2973 error = EPERM;
2974 goto out;
2975 }
2976#endif /* DEVELOPMENT || DEBUG */
2977
2978#if NAMEDSTREAMS
2979 /* For streams, va_data_size is the only setable attribute. */
2980 if ((vp->v_flag & VISNAMEDSTREAM) && (vap->va_active != VNODE_ATTR_va_data_size)) {
2981 error = EPERM;
2982 goto out;
2983 }
2984#endif
2985 /* Check for truncation */
2986 if (VATTR_IS_ACTIVE(vap, va_data_size)) {
2987 switch (vp->v_type) {
2988 case VREG:
2989 /* For regular files it's ok */
2990 break;
2991 case VDIR:
2992 /* Not allowed to truncate directories */
2993 error = EISDIR;
2994 goto out;
2995 default:
2996 /* For everything else we will clear the bit and let underlying FS decide on the rest */
2997 VATTR_CLEAR_ACTIVE(vap, va_data_size);
2998 if (vap->va_active) {
2999 break;
3000 }
3001 /* If it was the only bit set, return success, to handle cases like redirect to /dev/null */
3002 return 0;
3003 }
3004 }
3005
3006 /*
3007 * If ownership is being ignored on this volume, we silently discard
3008 * ownership changes.
3009 */
3010 if (vp->v_mount->mnt_flag & MNT_IGNORE_OWNERSHIP) {
3011 VATTR_CLEAR_ACTIVE(vap, va_uid);
3012 VATTR_CLEAR_ACTIVE(vap, va_gid);
3013 }
3014
3015 /*
3016 * Make sure that extended security is enabled if we're going to try
3017 * to set any.
3018 */
3019 if (!vfs_extendedsecurity(mp: vnode_mount(vp)) &&
3020 (VATTR_IS_ACTIVE(vap, va_acl) || VATTR_IS_ACTIVE(vap, va_uuuid) || VATTR_IS_ACTIVE(vap, va_guuid))) {
3021 KAUTH_DEBUG("SETATTR - returning ENOTSUP to request to set extended security");
3022 error = ENOTSUP;
3023 goto out;
3024 }
3025
3026 /* Never allow the setting of any unsupported superuser flags. */
3027 if (VATTR_IS_ACTIVE(vap, va_flags)) {
3028 vap->va_flags &= (SF_SUPPORTED | UF_SETTABLE);
3029 }
3030
3031#if CONFIG_FSE
3032 /*
3033 * Remember all of the active attributes that we're
3034 * attempting to modify.
3035 */
3036 active = vap->va_active & ~VNODE_ATTR_RDONLY;
3037#endif
3038
3039 error = VNOP_SETATTR(vp, vap, ctx);
3040
3041 if ((error == 0) && !VATTR_ALL_SUPPORTED(vap)) {
3042 error = vnode_setattr_fallback(vp, vap, ctx);
3043 }
3044
3045#if CONFIG_FSE
3046#define PERMISSION_BITS (VNODE_ATTR_BIT(va_uid) | VNODE_ATTR_BIT(va_uuuid) | \
3047 VNODE_ATTR_BIT(va_gid) | VNODE_ATTR_BIT(va_guuid) | \
3048 VNODE_ATTR_BIT(va_mode) | VNODE_ATTR_BIT(va_acl))
3049
3050 /*
3051 * Now that we've changed them, decide whether to send an
3052 * FSevent.
3053 */
3054 if ((active & PERMISSION_BITS) & vap->va_supported) {
3055 is_perm_change = 1;
3056 } else {
3057 /*
3058 * We've already checked the permission bits, and we
3059 * also want to filter out access time / backup time
3060 * changes.
3061 */
3062 active &= ~(PERMISSION_BITS |
3063 VNODE_ATTR_BIT(va_access_time) |
3064 VNODE_ATTR_BIT(va_backup_time));
3065
3066 /* Anything left to notify about? */
3067 if (active & vap->va_supported) {
3068 is_stat_change = 1;
3069 }
3070 }
3071
3072 if (error == 0) {
3073 if (is_perm_change) {
3074 if (need_fsevent(FSE_CHOWN, vp)) {
3075 add_fsevent(FSE_CHOWN, ctx, FSE_ARG_VNODE, vp, FSE_ARG_DONE);
3076 }
3077 } else if (is_stat_change && need_fsevent(FSE_STAT_CHANGED, vp)) {
3078 add_fsevent(FSE_STAT_CHANGED, ctx, FSE_ARG_VNODE, vp, FSE_ARG_DONE);
3079 }
3080 }
3081#undef PERMISSION_BITS
3082#endif
3083
3084out:
3085 return error;
3086}
3087
3088/*
3089 * Fallback for setting the attributes on a vnode in a vnode context. This
3090 * Function will attempt to store ACL, UUID, and GUID information utilizing
3091 * a read/modify/write operation against an EA used as a backing store for
3092 * the object.
3093 *
3094 * Parameters: vp The vnode whose attributes to set.
3095 * vap A pointer to the attributes to set.
3096 * ctx The vnode context in which the
3097 * operation is to be attempted.
3098 *
3099 * Returns: 0 Success
3100 * !0 errno value
3101 *
3102 * Notes: The kauth_filesec_t in 'vap', if any, is in host byte order,
3103 * as are the fsec and lfsec, if they are used.
3104 *
3105 * The contents of the data area pointed to by 'vap' may be
3106 * modified to indicate that the attribute is supported for
3107 * any given requested attribute.
3108 *
3109 * XXX: We should enummerate the possible errno values here, and where
3110 * in the code they originated.
3111 */
3112int
3113vnode_setattr_fallback(vnode_t vp, struct vnode_attr *vap, vfs_context_t ctx)
3114{
3115 kauth_filesec_t fsec;
3116 kauth_acl_t facl;
3117 struct kauth_filesec lfsec;
3118 int error;
3119
3120 error = 0;
3121
3122 /*
3123 * Extended security fallback via extended attributes.
3124 *
3125 * Note that we do not free the filesec; the caller is expected to
3126 * do this.
3127 */
3128 if (VATTR_NOT_RETURNED(vap, va_acl) ||
3129 VATTR_NOT_RETURNED(vap, va_uuuid) ||
3130 VATTR_NOT_RETURNED(vap, va_guuid)) {
3131 VFS_DEBUG(ctx, vp, "SETATTR - doing filesec fallback");
3132
3133 /*
3134 * Fail for file types that we don't permit extended security
3135 * to be set on.
3136 */
3137 if (!XATTR_VNODE_SUPPORTED(vp)) {
3138 VFS_DEBUG(ctx, vp, "SETATTR - Can't write ACL to file type %d", vnode_vtype(vp));
3139 error = EINVAL;
3140 goto out;
3141 }
3142
3143 /*
3144 * If we don't have all the extended security items, we need
3145 * to fetch the existing data to perform a read-modify-write
3146 * operation.
3147 */
3148 fsec = NULL;
3149 if (!VATTR_IS_ACTIVE(vap, va_acl) ||
3150 !VATTR_IS_ACTIVE(vap, va_uuuid) ||
3151 !VATTR_IS_ACTIVE(vap, va_guuid)) {
3152 if ((error = vnode_get_filesec(vp, fsecp: &fsec, ctx)) != 0) {
3153 KAUTH_DEBUG("SETATTR - ERROR %d fetching filesec for update", error);
3154 goto out;
3155 }
3156 }
3157 /* if we didn't get a filesec, use our local one */
3158 if (fsec == NULL) {
3159 KAUTH_DEBUG("SETATTR - using local filesec for new/full update");
3160 fsec = &lfsec;
3161 } else {
3162 KAUTH_DEBUG("SETATTR - updating existing filesec");
3163 }
3164 /* find the ACL */
3165 facl = &fsec->fsec_acl;
3166
3167 /* if we're using the local filesec, we need to initialise it */
3168 if (fsec == &lfsec) {
3169 fsec->fsec_magic = KAUTH_FILESEC_MAGIC;
3170 fsec->fsec_owner = kauth_null_guid;
3171 fsec->fsec_group = kauth_null_guid;
3172 facl->acl_entrycount = KAUTH_FILESEC_NOACL;
3173 facl->acl_flags = 0;
3174 }
3175
3176 /*
3177 * Update with the supplied attributes.
3178 */
3179 if (VATTR_IS_ACTIVE(vap, va_uuuid)) {
3180 KAUTH_DEBUG("SETATTR - updating owner UUID");
3181 fsec->fsec_owner = vap->va_uuuid;
3182 VATTR_SET_SUPPORTED(vap, va_uuuid);
3183 }
3184 if (VATTR_IS_ACTIVE(vap, va_guuid)) {
3185 KAUTH_DEBUG("SETATTR - updating group UUID");
3186 fsec->fsec_group = vap->va_guuid;
3187 VATTR_SET_SUPPORTED(vap, va_guuid);
3188 }
3189 if (VATTR_IS_ACTIVE(vap, va_acl)) {
3190 if (vap->va_acl == NULL) {
3191 KAUTH_DEBUG("SETATTR - removing ACL");
3192 facl->acl_entrycount = KAUTH_FILESEC_NOACL;
3193 } else {
3194 KAUTH_DEBUG("SETATTR - setting ACL with %d entries", vap->va_acl->acl_entrycount);
3195 facl = vap->va_acl;
3196 }
3197 VATTR_SET_SUPPORTED(vap, va_acl);
3198 }
3199
3200 /*
3201 * If the filesec data is all invalid, we can just remove
3202 * the EA completely.
3203 */
3204 if ((facl->acl_entrycount == KAUTH_FILESEC_NOACL) &&
3205 kauth_guid_equal(guid1: &fsec->fsec_owner, guid2: &kauth_null_guid) &&
3206 kauth_guid_equal(guid1: &fsec->fsec_group, guid2: &kauth_null_guid)) {
3207 error = vn_removexattr(vp, KAUTH_FILESEC_XATTR, XATTR_NOSECURITY, ctx);
3208 /* no attribute is ok, nothing to delete */
3209 if (error == ENOATTR) {
3210 error = 0;
3211 }
3212 VFS_DEBUG(ctx, vp, "SETATTR - remove filesec returning %d", error);
3213 } else {
3214 /* write the EA */
3215 error = vnode_set_filesec(vp, fsec, acl: facl, ctx);
3216 VFS_DEBUG(ctx, vp, "SETATTR - update filesec returning %d", error);
3217 }
3218
3219 /* if we fetched a filesec, dispose of the buffer */
3220 if (fsec != &lfsec) {
3221 kauth_filesec_free(fsp: fsec);
3222 }
3223 }
3224out:
3225
3226 return error;
3227}
3228
3229/*
3230 * Upcall for a filesystem to tell VFS about an EVFILT_VNODE-type
3231 * event on a vnode.
3232 */
3233int
3234vnode_notify(vnode_t vp, uint32_t events, struct vnode_attr *vap)
3235{
3236 /* These are the same as the corresponding knotes, at least for now. Cheating a little. */
3237 uint32_t knote_mask = (VNODE_EVENT_WRITE | VNODE_EVENT_DELETE | VNODE_EVENT_RENAME
3238 | VNODE_EVENT_LINK | VNODE_EVENT_EXTEND | VNODE_EVENT_ATTRIB);
3239 uint32_t dir_contents_mask = (VNODE_EVENT_DIR_CREATED | VNODE_EVENT_FILE_CREATED
3240 | VNODE_EVENT_DIR_REMOVED | VNODE_EVENT_FILE_REMOVED);
3241 uint32_t knote_events = (events & knote_mask);
3242
3243 /* Permissions are not explicitly part of the kqueue model */
3244 if (events & VNODE_EVENT_PERMS) {
3245 knote_events |= NOTE_ATTRIB;
3246 }
3247
3248 /* Directory contents information just becomes NOTE_WRITE */
3249 if ((vnode_isdir(vp)) && (events & dir_contents_mask)) {
3250 knote_events |= NOTE_WRITE;
3251 }
3252
3253 if (knote_events) {
3254 lock_vnode_and_post(vp, knote_events);
3255#if CONFIG_FSE
3256 if (vap != NULL) {
3257 create_fsevent_from_kevent(vp, kevents: events, vap);
3258 }
3259#else
3260 (void)vap;
3261#endif
3262 }
3263
3264 return 0;
3265}
3266
3267
3268
3269int
3270vnode_isdyldsharedcache(vnode_t vp)
3271{
3272 return (vp->v_flag & VSHARED_DYLD) ? 1 : 0;
3273}
3274
3275
3276/*
3277 * For a filesystem that isn't tracking its own vnode watchers:
3278 * check whether a vnode is being monitored.
3279 */
3280int
3281vnode_ismonitored(vnode_t vp)
3282{
3283 return vp->v_knotes.slh_first != NULL;
3284}
3285
3286int
3287vnode_getbackingvnode(vnode_t in_vp, vnode_t* out_vpp)
3288{
3289 if (out_vpp) {
3290 *out_vpp = NULLVP;
3291 }
3292#if NULLFS
3293 return nullfs_getbackingvnode(in_vp, out_vpp);
3294#else
3295#pragma unused(in_vp)
3296 return ENOENT;
3297#endif
3298}
3299
3300/*
3301 * Initialize a struct vnode_attr and activate the attributes required
3302 * by the vnode_notify() call.
3303 */
3304int
3305vfs_get_notify_attributes(struct vnode_attr *vap)
3306{
3307 VATTR_INIT(vap);
3308 vap->va_active = VNODE_NOTIFY_ATTRS;
3309 return 0;
3310}
3311
3312#if CONFIG_TRIGGERS
3313int
3314vfs_settriggercallback(fsid_t *fsid, vfs_trigger_callback_t vtc, void *data, uint32_t flags __unused, vfs_context_t ctx)
3315{
3316 int error;
3317 mount_t mp;
3318
3319 mp = mount_list_lookupby_fsid(fsid, 0 /* locked */, 1 /* withref */);
3320 if (mp == NULL) {
3321 return ENOENT;
3322 }
3323
3324 error = vfs_busy(mp, LK_NOWAIT);
3325 mount_iterdrop(mp);
3326
3327 if (error != 0) {
3328 return ENOENT;
3329 }
3330
3331 mount_lock(mp);
3332 if (mp->mnt_triggercallback != NULL) {
3333 error = EBUSY;
3334 mount_unlock(mp);
3335 goto out;
3336 }
3337
3338 mp->mnt_triggercallback = vtc;
3339 mp->mnt_triggerdata = data;
3340 mount_unlock(mp);
3341
3342 mp->mnt_triggercallback(mp, VTC_REPLACE, data, ctx);
3343
3344out:
3345 vfs_unbusy(mp);
3346 return 0;
3347}
3348#endif /* CONFIG_TRIGGERS */
3349
3350/*
3351 * Definition of vnode operations.
3352 */
3353
3354#if 0
3355/*
3356*#
3357*#% lookup dvp L ? ?
3358*#% lookup vpp - L -
3359*/
3360struct vnop_lookup_args {
3361 struct vnodeop_desc *a_desc;
3362 vnode_t a_dvp;
3363 vnode_t *a_vpp;
3364 struct componentname *a_cnp;
3365 vfs_context_t a_context;
3366};
3367#endif /* 0*/
3368
3369/*
3370 * Returns: 0 Success
3371 * lock_fsnode:ENOENT No such file or directory [only for VFS
3372 * that is not thread safe & vnode is
3373 * currently being/has been terminated]
3374 * <vfs_lookup>:ENAMETOOLONG
3375 * <vfs_lookup>:ENOENT
3376 * <vfs_lookup>:EJUSTRETURN
3377 * <vfs_lookup>:EPERM
3378 * <vfs_lookup>:EISDIR
3379 * <vfs_lookup>:ENOTDIR
3380 * <vfs_lookup>:???
3381 *
3382 * Note: The return codes from the underlying VFS's lookup routine can't
3383 * be fully enumerated here, since third party VFS authors may not
3384 * limit their error returns to the ones documented here, even
3385 * though this may result in some programs functioning incorrectly.
3386 *
3387 * The return codes documented above are those which may currently
3388 * be returned by HFS from hfs_lookup, not including additional
3389 * error code which may be propagated from underlying routines.
3390 */
3391errno_t
3392VNOP_LOOKUP(vnode_t dvp, vnode_t *vpp, struct componentname *cnp, vfs_context_t ctx)
3393{
3394 int _err;
3395 struct vnop_lookup_args a;
3396
3397 a.a_desc = &vnop_lookup_desc;
3398 a.a_dvp = dvp;
3399 a.a_vpp = vpp;
3400 a.a_cnp = cnp;
3401 a.a_context = ctx;
3402
3403 _err = (*dvp->v_op[vnop_lookup_desc.vdesc_offset])(&a);
3404 if (_err == 0 && *vpp) {
3405 DTRACE_FSINFO(lookup, vnode_t, *vpp);
3406 }
3407
3408 return _err;
3409}
3410
3411#if 0
3412struct vnop_compound_open_args {
3413 struct vnodeop_desc *a_desc;
3414 vnode_t a_dvp;
3415 vnode_t *a_vpp;
3416 struct componentname *a_cnp;
3417 int32_t a_flags;
3418 int32_t a_fmode;
3419 struct vnode_attr *a_vap;
3420 vfs_context_t a_context;
3421 void *a_reserved;
3422};
3423#endif /* 0 */
3424
3425int
3426VNOP_COMPOUND_OPEN(vnode_t dvp, vnode_t *vpp, struct nameidata *ndp, int32_t flags, int32_t fmode, uint32_t *statusp, struct vnode_attr *vap, vfs_context_t ctx)
3427{
3428 int _err;
3429 struct vnop_compound_open_args a;
3430 int did_create = 0;
3431 int want_create;
3432 uint32_t tmp_status = 0;
3433 struct componentname *cnp = &ndp->ni_cnd;
3434
3435 want_create = (flags & O_CREAT);
3436
3437 a.a_desc = &vnop_compound_open_desc;
3438 a.a_dvp = dvp;
3439 a.a_vpp = vpp; /* Could be NULL */
3440 a.a_cnp = cnp;
3441 a.a_flags = flags;
3442 a.a_fmode = fmode;
3443 a.a_status = (statusp != NULL) ? statusp : &tmp_status;
3444 a.a_vap = vap;
3445 a.a_context = ctx;
3446 a.a_open_create_authorizer = vn_authorize_create;
3447 a.a_open_existing_authorizer = vn_authorize_open_existing;
3448 a.a_reserved = NULL;
3449
3450 if (dvp == NULLVP) {
3451 panic("No dvp?");
3452 }
3453 if (want_create && !vap) {
3454 panic("Want create, but no vap?");
3455 }
3456 if (!want_create && vap) {
3457 panic("Don't want create, but have a vap?");
3458 }
3459
3460 _err = (*dvp->v_op[vnop_compound_open_desc.vdesc_offset])(&a);
3461 if (want_create) {
3462 if (_err == 0 && *vpp) {
3463 DTRACE_FSINFO(compound_open, vnode_t, *vpp);
3464 } else {
3465 DTRACE_FSINFO(compound_open, vnode_t, dvp);
3466 }
3467 } else {
3468 DTRACE_FSINFO(compound_open, vnode_t, *vpp);
3469 }
3470
3471 did_create = (*a.a_status & COMPOUND_OPEN_STATUS_DID_CREATE);
3472
3473 if (did_create && !want_create) {
3474 panic("Filesystem did a create, even though none was requested?");
3475 }
3476
3477 if (did_create) {
3478#if CONFIG_APPLEDOUBLE
3479 if (!NATIVE_XATTR(dvp)) {
3480 /*
3481 * Remove stale Apple Double file (if any).
3482 */
3483 xattrfile_remove(dvp, basename: cnp->cn_nameptr, ctx, force: 0);
3484 }
3485#endif /* CONFIG_APPLEDOUBLE */
3486 /* On create, provide kqueue notification */
3487 post_event_if_success(dvp, _err, NOTE_WRITE);
3488 }
3489
3490 lookup_compound_vnop_post_hook(error: _err, dvp, vp: *vpp, ndp, did_create);
3491#if 0 /* FSEvents... */
3492 if (*vpp && _err && _err != EKEEPLOOKING) {
3493 vnode_put(*vpp);
3494 *vpp = NULLVP;
3495 }
3496#endif /* 0 */
3497
3498 return _err;
3499}
3500
3501#if 0
3502struct vnop_create_args {
3503 struct vnodeop_desc *a_desc;
3504 vnode_t a_dvp;
3505 vnode_t *a_vpp;
3506 struct componentname *a_cnp;
3507 struct vnode_attr *a_vap;
3508 vfs_context_t a_context;
3509};
3510#endif /* 0*/
3511errno_t
3512VNOP_CREATE(vnode_t dvp, vnode_t * vpp, struct componentname * cnp, struct vnode_attr * vap, vfs_context_t ctx)
3513{
3514 int _err;
3515 struct vnop_create_args a;
3516
3517 a.a_desc = &vnop_create_desc;
3518 a.a_dvp = dvp;
3519 a.a_vpp = vpp;
3520 a.a_cnp = cnp;
3521 a.a_vap = vap;
3522 a.a_context = ctx;
3523
3524 _err = (*dvp->v_op[vnop_create_desc.vdesc_offset])(&a);
3525 if (_err == 0 && *vpp) {
3526 DTRACE_FSINFO(create, vnode_t, *vpp);
3527 }
3528
3529#if CONFIG_APPLEDOUBLE
3530 if (_err == 0 && !NATIVE_XATTR(dvp)) {
3531 /*
3532 * Remove stale Apple Double file (if any).
3533 */
3534 xattrfile_remove(dvp, basename: cnp->cn_nameptr, ctx, force: 0);
3535 }
3536#endif /* CONFIG_APPLEDOUBLE */
3537
3538 post_event_if_success(dvp, _err, NOTE_WRITE);
3539
3540 return _err;
3541}
3542
3543#if 0
3544/*
3545*#
3546*#% whiteout dvp L L L
3547*#% whiteout cnp - - -
3548*#% whiteout flag - - -
3549*#
3550*/
3551struct vnop_whiteout_args {
3552 struct vnodeop_desc *a_desc;
3553 vnode_t a_dvp;
3554 struct componentname *a_cnp;
3555 int a_flags;
3556 vfs_context_t a_context;
3557};
3558#endif /* 0*/
3559errno_t
3560VNOP_WHITEOUT(__unused vnode_t dvp, __unused struct componentname *cnp,
3561 __unused int flags, __unused vfs_context_t ctx)
3562{
3563 return ENOTSUP; // XXX OBSOLETE
3564}
3565
3566#if 0
3567/*
3568*#
3569*#% mknod dvp L U U
3570*#% mknod vpp - X -
3571*#
3572*/
3573struct vnop_mknod_args {
3574 struct vnodeop_desc *a_desc;
3575 vnode_t a_dvp;
3576 vnode_t *a_vpp;
3577 struct componentname *a_cnp;
3578 struct vnode_attr *a_vap;
3579 vfs_context_t a_context;
3580};
3581#endif /* 0*/
3582errno_t
3583VNOP_MKNOD(vnode_t dvp, vnode_t * vpp, struct componentname * cnp, struct vnode_attr * vap, vfs_context_t ctx)
3584{
3585 int _err;
3586 struct vnop_mknod_args a;
3587
3588 a.a_desc = &vnop_mknod_desc;
3589 a.a_dvp = dvp;
3590 a.a_vpp = vpp;
3591 a.a_cnp = cnp;
3592 a.a_vap = vap;
3593 a.a_context = ctx;
3594
3595 _err = (*dvp->v_op[vnop_mknod_desc.vdesc_offset])(&a);
3596 if (_err == 0 && *vpp) {
3597 DTRACE_FSINFO(mknod, vnode_t, *vpp);
3598 }
3599
3600 post_event_if_success(dvp, _err, NOTE_WRITE);
3601
3602 return _err;
3603}
3604
3605#if 0
3606/*
3607*#
3608*#% open vp L L L
3609*#
3610*/
3611struct vnop_open_args {
3612 struct vnodeop_desc *a_desc;
3613 vnode_t a_vp;
3614 int a_mode;
3615 vfs_context_t a_context;
3616};
3617#endif /* 0*/
3618errno_t
3619VNOP_OPEN(vnode_t vp, int mode, vfs_context_t ctx)
3620{
3621 int _err;
3622 struct vnop_open_args a;
3623
3624 if (ctx == NULL) {
3625 ctx = vfs_context_current();
3626 }
3627 a.a_desc = &vnop_open_desc;
3628 a.a_vp = vp;
3629 a.a_mode = mode;
3630 a.a_context = ctx;
3631
3632 _err = (*vp->v_op[vnop_open_desc.vdesc_offset])(&a);
3633 DTRACE_FSINFO(open, vnode_t, vp);
3634
3635 return _err;
3636}
3637
3638#if 0
3639/*
3640*#
3641*#% close vp U U U
3642*#
3643*/
3644struct vnop_close_args {
3645 struct vnodeop_desc *a_desc;
3646 vnode_t a_vp;
3647 int a_fflag;
3648 vfs_context_t a_context;
3649};
3650#endif /* 0*/
3651errno_t
3652VNOP_CLOSE(vnode_t vp, int fflag, vfs_context_t ctx)
3653{
3654 int _err;
3655 struct vnop_close_args a;
3656
3657 if (ctx == NULL) {
3658 ctx = vfs_context_current();
3659 }
3660 a.a_desc = &vnop_close_desc;
3661 a.a_vp = vp;
3662 a.a_fflag = fflag;
3663 a.a_context = ctx;
3664
3665 _err = (*vp->v_op[vnop_close_desc.vdesc_offset])(&a);
3666 DTRACE_FSINFO(close, vnode_t, vp);
3667
3668 return _err;
3669}
3670
3671#if 0
3672/*
3673*#
3674*#% access vp L L L
3675*#
3676*/
3677struct vnop_access_args {
3678 struct vnodeop_desc *a_desc;
3679 vnode_t a_vp;
3680 int a_action;
3681 vfs_context_t a_context;
3682};
3683#endif /* 0*/
3684errno_t
3685VNOP_ACCESS(vnode_t vp, int action, vfs_context_t ctx)
3686{
3687 int _err;
3688 struct vnop_access_args a;
3689
3690 if (ctx == NULL) {
3691 ctx = vfs_context_current();
3692 }
3693 a.a_desc = &vnop_access_desc;
3694 a.a_vp = vp;
3695 a.a_action = action;
3696 a.a_context = ctx;
3697
3698 _err = (*vp->v_op[vnop_access_desc.vdesc_offset])(&a);
3699 DTRACE_FSINFO(access, vnode_t, vp);
3700
3701 return _err;
3702}
3703
3704#if 0
3705/*
3706*#
3707*#% getattr vp = = =
3708*#
3709*/
3710struct vnop_getattr_args {
3711 struct vnodeop_desc *a_desc;
3712 vnode_t a_vp;
3713 struct vnode_attr *a_vap;
3714 vfs_context_t a_context;
3715};
3716#endif /* 0*/
3717errno_t
3718VNOP_GETATTR(vnode_t vp, struct vnode_attr * vap, vfs_context_t ctx)
3719{
3720 int _err;
3721 struct vnop_getattr_args a;
3722
3723 a.a_desc = &vnop_getattr_desc;
3724 a.a_vp = vp;
3725 a.a_vap = vap;
3726 a.a_context = ctx;
3727
3728 _err = (*vp->v_op[vnop_getattr_desc.vdesc_offset])(&a);
3729 DTRACE_FSINFO(getattr, vnode_t, vp);
3730
3731 return _err;
3732}
3733
3734#if 0
3735/*
3736*#
3737*#% setattr vp L L L
3738*#
3739*/
3740struct vnop_setattr_args {
3741 struct vnodeop_desc *a_desc;
3742 vnode_t a_vp;
3743 struct vnode_attr *a_vap;
3744 vfs_context_t a_context;
3745};
3746#endif /* 0*/
3747errno_t
3748VNOP_SETATTR(vnode_t vp, struct vnode_attr * vap, vfs_context_t ctx)
3749{
3750 int _err;
3751 struct vnop_setattr_args a;
3752
3753 a.a_desc = &vnop_setattr_desc;
3754 a.a_vp = vp;
3755 a.a_vap = vap;
3756 a.a_context = ctx;
3757
3758 _err = (*vp->v_op[vnop_setattr_desc.vdesc_offset])(&a);
3759 DTRACE_FSINFO(setattr, vnode_t, vp);
3760
3761#if CONFIG_APPLEDOUBLE
3762 /*
3763 * Shadow uid/gid/mod change to extended attribute file.
3764 */
3765 if (_err == 0 && !NATIVE_XATTR(vp)) {
3766 struct vnode_attr va;
3767 int change = 0;
3768
3769 VATTR_INIT(&va);
3770 if (VATTR_IS_ACTIVE(vap, va_uid)) {
3771 VATTR_SET(&va, va_uid, vap->va_uid);
3772 change = 1;
3773 }
3774 if (VATTR_IS_ACTIVE(vap, va_gid)) {
3775 VATTR_SET(&va, va_gid, vap->va_gid);
3776 change = 1;
3777 }
3778 if (VATTR_IS_ACTIVE(vap, va_mode)) {
3779 VATTR_SET(&va, va_mode, vap->va_mode);
3780 change = 1;
3781 }
3782 if (change) {
3783 vnode_t dvp;
3784 const char *vname;
3785
3786 dvp = vnode_getparent(vp);
3787 vname = vnode_getname(vp);
3788
3789 xattrfile_setattr(dvp, basename: vname, vap: &va, ctx);
3790 if (dvp != NULLVP) {
3791 vnode_put(vp: dvp);
3792 }
3793 if (vname != NULL) {
3794 vnode_putname(name: vname);
3795 }
3796 }
3797 }
3798#endif /* CONFIG_APPLEDOUBLE */
3799
3800 /*
3801 * If we have changed any of the things about the file that are likely
3802 * to result in changes to authorization results, blow the vnode auth
3803 * cache
3804 */
3805 if (_err == 0 && (
3806 VATTR_IS_SUPPORTED(vap, va_mode) ||
3807 VATTR_IS_SUPPORTED(vap, va_uid) ||
3808 VATTR_IS_SUPPORTED(vap, va_gid) ||
3809 VATTR_IS_SUPPORTED(vap, va_flags) ||
3810 VATTR_IS_SUPPORTED(vap, va_acl) ||
3811 VATTR_IS_SUPPORTED(vap, va_uuuid) ||
3812 VATTR_IS_SUPPORTED(vap, va_guuid))) {
3813 vnode_uncache_authorized_action(vp, KAUTH_INVALIDATE_CACHED_RIGHTS);
3814
3815#if NAMEDSTREAMS
3816 if (vfs_authopaque(mp: vp->v_mount) && vnode_hasnamedstreams(vp)) {
3817 vnode_t svp;
3818 if (vnode_getnamedstream(vp, &svp, XATTR_RESOURCEFORK_NAME, NS_OPEN, 0, ctx) == 0) {
3819 vnode_uncache_authorized_action(vp: svp, KAUTH_INVALIDATE_CACHED_RIGHTS);
3820 vnode_put(vp: svp);
3821 }
3822 }
3823#endif /* NAMEDSTREAMS */
3824 }
3825
3826
3827 post_event_if_success(vp, _err, NOTE_ATTRIB);
3828
3829 return _err;
3830}
3831
3832
3833#if 0
3834/*
3835*#
3836*#% read vp L L L
3837*#
3838*/
3839struct vnop_read_args {
3840 struct vnodeop_desc *a_desc;
3841 vnode_t a_vp;
3842 struct uio *a_uio;
3843 int a_ioflag;
3844 vfs_context_t a_context;
3845};
3846#endif /* 0*/
3847errno_t
3848VNOP_READ(vnode_t vp, struct uio * uio, int ioflag, vfs_context_t ctx)
3849{
3850 int _err;
3851 struct vnop_read_args a;
3852#if CONFIG_DTRACE
3853 user_ssize_t resid = uio_resid(a_uio: uio);
3854#endif
3855
3856 if (ctx == NULL) {
3857 return EINVAL;
3858 }
3859
3860 a.a_desc = &vnop_read_desc;
3861 a.a_vp = vp;
3862 a.a_uio = uio;
3863 a.a_ioflag = ioflag;
3864 a.a_context = ctx;
3865
3866 _err = (*vp->v_op[vnop_read_desc.vdesc_offset])(&a);
3867 DTRACE_FSINFO_IO(read,
3868 vnode_t, vp, user_ssize_t, (resid - uio_resid(uio)));
3869
3870 return _err;
3871}
3872
3873
3874#if 0
3875/*
3876*#
3877*#% write vp L L L
3878*#
3879*/
3880struct vnop_write_args {
3881 struct vnodeop_desc *a_desc;
3882 vnode_t a_vp;
3883 struct uio *a_uio;
3884 int a_ioflag;
3885 vfs_context_t a_context;
3886};
3887#endif /* 0*/
3888errno_t
3889VNOP_WRITE(vnode_t vp, struct uio * uio, int ioflag, vfs_context_t ctx)
3890{
3891 struct vnop_write_args a;
3892 int _err;
3893#if CONFIG_DTRACE
3894 user_ssize_t resid = uio_resid(a_uio: uio);
3895#endif
3896
3897 if (ctx == NULL) {
3898 return EINVAL;
3899 }
3900
3901 a.a_desc = &vnop_write_desc;
3902 a.a_vp = vp;
3903 a.a_uio = uio;
3904 a.a_ioflag = ioflag;
3905 a.a_context = ctx;
3906
3907 _err = (*vp->v_op[vnop_write_desc.vdesc_offset])(&a);
3908 DTRACE_FSINFO_IO(write,
3909 vnode_t, vp, user_ssize_t, (resid - uio_resid(uio)));
3910
3911 post_event_if_success(vp, _err, NOTE_WRITE);
3912
3913 return _err;
3914}
3915
3916
3917#if 0
3918/*
3919*#
3920*#% ioctl vp U U U
3921*#
3922*/
3923struct vnop_ioctl_args {
3924 struct vnodeop_desc *a_desc;
3925 vnode_t a_vp;
3926 u_long a_command;
3927 caddr_t a_data;
3928 int a_fflag;
3929 vfs_context_t a_context;
3930};
3931#endif /* 0*/
3932errno_t
3933VNOP_IOCTL(vnode_t vp, u_long command, caddr_t data, int fflag, vfs_context_t ctx)
3934{
3935 int _err;
3936 struct vnop_ioctl_args a;
3937
3938 if (ctx == NULL) {
3939 ctx = vfs_context_current();
3940 }
3941
3942 /*
3943 * This check should probably have been put in the TTY code instead...
3944 *
3945 * We have to be careful about what we assume during startup and shutdown.
3946 * We have to be able to use the root filesystem's device vnode even when
3947 * devfs isn't mounted (yet/anymore), so we can't go looking at its mount
3948 * structure. If there is no data pointer, it doesn't matter whether
3949 * the device is 64-bit ready. Any command (like DKIOCSYNCHRONIZE)
3950 * which passes NULL for its data pointer can therefore be used during
3951 * mount or unmount of the root filesystem.
3952 *
3953 * Depending on what root filesystems need to do during mount/unmount, we
3954 * may need to loosen this check again in the future.
3955 */
3956 if (vfs_context_is64bit(ctx) && !(vnode_ischr(vp) || vnode_isblk(vp))) {
3957 if (data != NULL && !vnode_vfs64bitready(vp)) {
3958 return ENOTTY;
3959 }
3960 }
3961
3962 if ((command == DKIOCISSOLIDSTATE) && (vp == rootvp) && rootvp_is_ssd && data) {
3963 *data = 1;
3964 return 0;
3965 }
3966
3967 a.a_desc = &vnop_ioctl_desc;
3968 a.a_vp = vp;
3969 a.a_command = command;
3970 a.a_data = data;
3971 a.a_fflag = fflag;
3972 a.a_context = ctx;
3973
3974 _err = (*vp->v_op[vnop_ioctl_desc.vdesc_offset])(&a);
3975 DTRACE_FSINFO(ioctl, vnode_t, vp);
3976
3977 return _err;
3978}
3979
3980
3981#if 0
3982/*
3983*#
3984*#% select vp U U U
3985*#
3986*/
3987struct vnop_select_args {
3988 struct vnodeop_desc *a_desc;
3989 vnode_t a_vp;
3990 int a_which;
3991 int a_fflags;
3992 void *a_wql;
3993 vfs_context_t a_context;
3994};
3995#endif /* 0*/
3996errno_t
3997VNOP_SELECT(vnode_t vp, int which, int fflags, void * wql, vfs_context_t ctx)
3998{
3999 int _err;
4000 struct vnop_select_args a;
4001
4002 if (ctx == NULL) {
4003 ctx = vfs_context_current();
4004 }
4005 a.a_desc = &vnop_select_desc;
4006 a.a_vp = vp;
4007 a.a_which = which;
4008 a.a_fflags = fflags;
4009 a.a_context = ctx;
4010 a.a_wql = wql;
4011
4012 _err = (*vp->v_op[vnop_select_desc.vdesc_offset])(&a);
4013 DTRACE_FSINFO(select, vnode_t, vp);
4014
4015 return _err;
4016}
4017
4018
4019#if 0
4020/*
4021*#
4022*#% exchange fvp L L L
4023*#% exchange tvp L L L
4024*#
4025*/
4026struct vnop_exchange_args {
4027 struct vnodeop_desc *a_desc;
4028 vnode_t a_fvp;
4029 vnode_t a_tvp;
4030 int a_options;
4031 vfs_context_t a_context;
4032};
4033#endif /* 0*/
4034errno_t
4035VNOP_EXCHANGE(vnode_t fvp, vnode_t tvp, int options, vfs_context_t ctx)
4036{
4037 int _err;
4038 struct vnop_exchange_args a;
4039
4040 a.a_desc = &vnop_exchange_desc;
4041 a.a_fvp = fvp;
4042 a.a_tvp = tvp;
4043 a.a_options = options;
4044 a.a_context = ctx;
4045
4046 _err = (*fvp->v_op[vnop_exchange_desc.vdesc_offset])(&a);
4047 DTRACE_FSINFO(exchange, vnode_t, fvp);
4048
4049 /* Don't post NOTE_WRITE because file descriptors follow the data ... */
4050 post_event_if_success(fvp, _err, NOTE_ATTRIB);
4051 post_event_if_success(tvp, _err, NOTE_ATTRIB);
4052
4053 return _err;
4054}
4055
4056
4057#if 0
4058/*
4059*#
4060*#% revoke vp U U U
4061*#
4062*/
4063struct vnop_revoke_args {
4064 struct vnodeop_desc *a_desc;
4065 vnode_t a_vp;
4066 int a_flags;
4067 vfs_context_t a_context;
4068};
4069#endif /* 0*/
4070errno_t
4071VNOP_REVOKE(vnode_t vp, int flags, vfs_context_t ctx)
4072{
4073 struct vnop_revoke_args a;
4074 int _err;
4075
4076 a.a_desc = &vnop_revoke_desc;
4077 a.a_vp = vp;
4078 a.a_flags = flags;
4079 a.a_context = ctx;
4080
4081 _err = (*vp->v_op[vnop_revoke_desc.vdesc_offset])(&a);
4082 DTRACE_FSINFO(revoke, vnode_t, vp);
4083
4084 return _err;
4085}
4086
4087
4088#if 0
4089/*
4090*#
4091*# mmap_check - vp U U U
4092*#
4093*/
4094struct vnop_mmap_check_args {
4095 struct vnodeop_desc *a_desc;
4096 vnode_t a_vp;
4097 int a_flags;
4098 vfs_context_t a_context;
4099};
4100#endif /* 0 */
4101errno_t
4102VNOP_MMAP_CHECK(vnode_t vp, int flags, vfs_context_t ctx)
4103{
4104 int _err;
4105 struct vnop_mmap_check_args a;
4106
4107 a.a_desc = &vnop_mmap_check_desc;
4108 a.a_vp = vp;
4109 a.a_flags = flags;
4110 a.a_context = ctx;
4111
4112 _err = (*vp->v_op[vnop_mmap_check_desc.vdesc_offset])(&a);
4113 if (_err == ENOTSUP) {
4114 _err = 0;
4115 }
4116 DTRACE_FSINFO(mmap_check, vnode_t, vp);
4117
4118 return _err;
4119}
4120
4121#if 0
4122/*
4123*#
4124*# mmap - vp U U U
4125*#
4126*/
4127struct vnop_mmap_args {
4128 struct vnodeop_desc *a_desc;
4129 vnode_t a_vp;
4130 int a_fflags;
4131 vfs_context_t a_context;
4132};
4133#endif /* 0*/
4134errno_t
4135VNOP_MMAP(vnode_t vp, int fflags, vfs_context_t ctx)
4136{
4137 int _err;
4138 struct vnop_mmap_args a;
4139
4140 a.a_desc = &vnop_mmap_desc;
4141 a.a_vp = vp;
4142 a.a_fflags = fflags;
4143 a.a_context = ctx;
4144
4145 _err = (*vp->v_op[vnop_mmap_desc.vdesc_offset])(&a);
4146 DTRACE_FSINFO(mmap, vnode_t, vp);
4147
4148 return _err;
4149}
4150
4151
4152#if 0
4153/*
4154*#
4155*# mnomap - vp U U U
4156*#
4157*/
4158struct vnop_mnomap_args {
4159 struct vnodeop_desc *a_desc;
4160 vnode_t a_vp;
4161 vfs_context_t a_context;
4162};
4163#endif /* 0*/
4164errno_t
4165VNOP_MNOMAP(vnode_t vp, vfs_context_t ctx)
4166{
4167 int _err;
4168 struct vnop_mnomap_args a;
4169
4170 a.a_desc = &vnop_mnomap_desc;
4171 a.a_vp = vp;
4172 a.a_context = ctx;
4173
4174 _err = (*vp->v_op[vnop_mnomap_desc.vdesc_offset])(&a);
4175 DTRACE_FSINFO(mnomap, vnode_t, vp);
4176
4177 return _err;
4178}
4179
4180
4181#if 0
4182/*
4183*#
4184*#% fsync vp L L L
4185*#
4186*/
4187struct vnop_fsync_args {
4188 struct vnodeop_desc *a_desc;
4189 vnode_t a_vp;
4190 int a_waitfor;
4191 vfs_context_t a_context;
4192};
4193#endif /* 0*/
4194errno_t
4195VNOP_FSYNC(vnode_t vp, int waitfor, vfs_context_t ctx)
4196{
4197 struct vnop_fsync_args a;
4198 int _err;
4199
4200 a.a_desc = &vnop_fsync_desc;
4201 a.a_vp = vp;
4202 a.a_waitfor = waitfor;
4203 a.a_context = ctx;
4204
4205 _err = (*vp->v_op[vnop_fsync_desc.vdesc_offset])(&a);
4206 DTRACE_FSINFO(fsync, vnode_t, vp);
4207
4208 return _err;
4209}
4210
4211
4212#if 0
4213/*
4214*#
4215*#% remove dvp L U U
4216*#% remove vp L U U
4217*#
4218*/
4219struct vnop_remove_args {
4220 struct vnodeop_desc *a_desc;
4221 vnode_t a_dvp;
4222 vnode_t a_vp;
4223 struct componentname *a_cnp;
4224 int a_flags;
4225 vfs_context_t a_context;
4226};
4227#endif /* 0*/
4228errno_t
4229VNOP_REMOVE(vnode_t dvp, vnode_t vp, struct componentname * cnp, int flags, vfs_context_t ctx)
4230{
4231 int _err;
4232 struct vnop_remove_args a;
4233
4234 a.a_desc = &vnop_remove_desc;
4235 a.a_dvp = dvp;
4236 a.a_vp = vp;
4237 a.a_cnp = cnp;
4238 a.a_flags = flags;
4239 a.a_context = ctx;
4240
4241 _err = (*dvp->v_op[vnop_remove_desc.vdesc_offset])(&a);
4242 DTRACE_FSINFO(remove, vnode_t, vp);
4243
4244 if (_err == 0) {
4245 vnode_setneedinactive(vp);
4246#if CONFIG_APPLEDOUBLE
4247 if (!(NATIVE_XATTR(dvp))) {
4248 /*
4249 * Remove any associated extended attribute file (._ AppleDouble file).
4250 */
4251 xattrfile_remove(dvp, basename: cnp->cn_nameptr, ctx, force: 1);
4252 }
4253#endif /* CONFIG_APPLEDOUBLE */
4254 }
4255
4256 post_event_if_success(vp, _err, NOTE_DELETE | NOTE_LINK);
4257 post_event_if_success(dvp, _err, NOTE_WRITE);
4258
4259 return _err;
4260}
4261
4262int
4263VNOP_COMPOUND_REMOVE(vnode_t dvp, vnode_t *vpp, struct nameidata *ndp, int32_t flags, struct vnode_attr *vap, vfs_context_t ctx)
4264{
4265 int _err;
4266 struct vnop_compound_remove_args a;
4267 int no_vp = (*vpp == NULLVP);
4268
4269 a.a_desc = &vnop_compound_remove_desc;
4270 a.a_dvp = dvp;
4271 a.a_vpp = vpp;
4272 a.a_cnp = &ndp->ni_cnd;
4273 a.a_flags = flags;
4274 a.a_vap = vap;
4275 a.a_context = ctx;
4276 a.a_remove_authorizer = vn_authorize_unlink;
4277
4278 _err = (*dvp->v_op[vnop_compound_remove_desc.vdesc_offset])(&a);
4279 if (_err == 0 && *vpp) {
4280 DTRACE_FSINFO(compound_remove, vnode_t, *vpp);
4281 } else {
4282 DTRACE_FSINFO(compound_remove, vnode_t, dvp);
4283 }
4284 if (_err == 0) {
4285 vnode_setneedinactive(vp: *vpp);
4286#if CONFIG_APPLEDOUBLE
4287 if (!(NATIVE_XATTR(dvp))) {
4288 /*
4289 * Remove any associated extended attribute file (._ AppleDouble file).
4290 */
4291 xattrfile_remove(dvp, basename: ndp->ni_cnd.cn_nameptr, ctx, force: 1);
4292 }
4293#endif /* CONFIG_APPLEDOUBLE */
4294 }
4295
4296 post_event_if_success(*vpp, _err, NOTE_DELETE | NOTE_LINK);
4297 post_event_if_success(dvp, _err, NOTE_WRITE);
4298
4299 if (no_vp) {
4300 lookup_compound_vnop_post_hook(error: _err, dvp, vp: *vpp, ndp, did_create: 0);
4301 if (*vpp && _err && _err != EKEEPLOOKING) {
4302 vnode_put(vp: *vpp);
4303 *vpp = NULLVP;
4304 }
4305 }
4306
4307 //printf("VNOP_COMPOUND_REMOVE() returning %d\n", _err);
4308
4309 return _err;
4310}
4311
4312#if 0
4313/*
4314*#
4315*#% link vp U U U
4316*#% link tdvp L U U
4317*#
4318*/
4319struct vnop_link_args {
4320 struct vnodeop_desc *a_desc;
4321 vnode_t a_vp;
4322 vnode_t a_tdvp;
4323 struct componentname *a_cnp;
4324 vfs_context_t a_context;
4325};
4326#endif /* 0*/
4327errno_t
4328VNOP_LINK(vnode_t vp, vnode_t tdvp, struct componentname * cnp, vfs_context_t ctx)
4329{
4330 int _err;
4331 struct vnop_link_args a;
4332
4333#if CONFIG_APPLEDOUBLE
4334 /*
4335 * For file systems with non-native extended attributes,
4336 * disallow linking to an existing "._" Apple Double file.
4337 */
4338 if (!NATIVE_XATTR(tdvp) && (vp->v_type == VREG)) {
4339 const char *vname;
4340
4341 vname = vnode_getname(vp);
4342 if (vname != NULL) {
4343 _err = 0;
4344 if (vname[0] == '.' && vname[1] == '_' && vname[2] != '\0') {
4345 _err = EPERM;
4346 }
4347 vnode_putname(name: vname);
4348 if (_err) {
4349 return _err;
4350 }
4351 }
4352 }
4353#endif /* CONFIG_APPLEDOUBLE */
4354
4355 a.a_desc = &vnop_link_desc;
4356 a.a_vp = vp;
4357 a.a_tdvp = tdvp;
4358 a.a_cnp = cnp;
4359 a.a_context = ctx;
4360
4361 _err = (*tdvp->v_op[vnop_link_desc.vdesc_offset])(&a);
4362 DTRACE_FSINFO(link, vnode_t, vp);
4363
4364 post_event_if_success(vp, _err, NOTE_LINK);
4365 post_event_if_success(tdvp, _err, NOTE_WRITE);
4366
4367 return _err;
4368}
4369
4370errno_t
4371vn_rename(struct vnode *fdvp, struct vnode **fvpp, struct componentname *fcnp, struct vnode_attr *fvap,
4372 struct vnode *tdvp, struct vnode **tvpp, struct componentname *tcnp, struct vnode_attr *tvap,
4373 vfs_rename_flags_t flags, vfs_context_t ctx)
4374{
4375 int _err;
4376 struct nameidata *fromnd = NULL;
4377 struct nameidata *tond = NULL;
4378#if CONFIG_APPLEDOUBLE
4379 vnode_t src_attr_vp = NULLVP;
4380 vnode_t dst_attr_vp = NULLVP;
4381 char smallname1[48];
4382 char smallname2[48];
4383 char *xfromname = NULL;
4384 char *xtoname = NULL;
4385#endif /* CONFIG_APPLEDOUBLE */
4386 int batched;
4387 uint32_t tdfflags; // Target directory file flags
4388
4389 batched = vnode_compound_rename_available(vp: fdvp);
4390
4391 if (!batched) {
4392 if (*fvpp == NULLVP) {
4393 panic("Not batched, and no fvp?");
4394 }
4395 }
4396
4397#if CONFIG_APPLEDOUBLE
4398 /*
4399 * We need to preflight any potential AppleDouble file for the source file
4400 * before doing the rename operation, since we could potentially be doing
4401 * this operation on a network filesystem, and would end up duplicating
4402 * the work. Also, save the source and destination names. Skip it if the
4403 * source has a "._" prefix.
4404 */
4405
4406 size_t xfromname_len = 0;
4407 size_t xtoname_len = 0;
4408 if (!NATIVE_XATTR(fdvp) &&
4409 !(fcnp->cn_nameptr[0] == '.' && fcnp->cn_nameptr[1] == '_')) {
4410 int error;
4411
4412 /* Get source attribute file name. */
4413 xfromname_len = fcnp->cn_namelen + 3;
4414 if (xfromname_len > sizeof(smallname1)) {
4415 xfromname = kalloc_data(xfromname_len, Z_WAITOK);
4416 } else {
4417 xfromname = &smallname1[0];
4418 }
4419 strlcpy(dst: xfromname, src: "._", n: xfromname_len);
4420 strlcat(dst: xfromname, src: fcnp->cn_nameptr, n: xfromname_len);
4421
4422 /* Get destination attribute file name. */
4423 xtoname_len = tcnp->cn_namelen + 3;
4424 if (xtoname_len > sizeof(smallname2)) {
4425 xtoname = kalloc_data(xtoname_len, Z_WAITOK);
4426 } else {
4427 xtoname = &smallname2[0];
4428 }
4429 strlcpy(dst: xtoname, src: "._", n: xtoname_len);
4430 strlcat(dst: xtoname, src: tcnp->cn_nameptr, n: xtoname_len);
4431
4432 /*
4433 * Look up source attribute file, keep reference on it if exists.
4434 * Note that we do the namei with the nameiop of RENAME, which is different than
4435 * in the rename syscall. It's OK if the source file does not exist, since this
4436 * is only for AppleDouble files.
4437 */
4438 fromnd = kalloc_type(struct nameidata, Z_WAITOK);
4439 NDINIT(fromnd, RENAME, OP_RENAME, NOFOLLOW | USEDVP | CN_NBMOUNTLOOK,
4440 UIO_SYSSPACE, CAST_USER_ADDR_T(xfromname), ctx);
4441 fromnd->ni_dvp = fdvp;
4442 error = namei(ndp: fromnd);
4443
4444 /*
4445 * If there was an error looking up source attribute file,
4446 * we'll behave as if it didn't exist.
4447 */
4448
4449 if (error == 0) {
4450 if (fromnd->ni_vp) {
4451 /* src_attr_vp indicates need to call vnode_put / nameidone later */
4452 src_attr_vp = fromnd->ni_vp;
4453
4454 if (fromnd->ni_vp->v_type != VREG) {
4455 src_attr_vp = NULLVP;
4456 vnode_put(vp: fromnd->ni_vp);
4457 }
4458 }
4459 /*
4460 * Either we got an invalid vnode type (not a regular file) or the namei lookup
4461 * suppressed ENOENT as a valid error since we're renaming. Either way, we don't
4462 * have a vnode here, so we drop our namei buffer for the source attribute file
4463 */
4464 if (src_attr_vp == NULLVP) {
4465 nameidone(fromnd);
4466 }
4467 }
4468 }
4469#endif /* CONFIG_APPLEDOUBLE */
4470
4471 if (batched) {
4472 _err = VNOP_COMPOUND_RENAME(fdvp, fvpp, fcnp, fvap, tdvp, tvpp, tcnp, tvap, flags, ctx);
4473 if (_err != 0) {
4474 printf("VNOP_COMPOUND_RENAME() returned %d\n", _err);
4475 }
4476 } else {
4477 if (flags) {
4478 _err = VNOP_RENAMEX(fdvp, *fvpp, fcnp, tdvp, *tvpp, tcnp, flags, ctx);
4479 if (_err == ENOTSUP && flags == VFS_RENAME_SECLUDE) {
4480 // Legacy...
4481 if ((*fvpp)->v_mount->mnt_vtable->vfc_vfsflags & VFC_VFSVNOP_SECLUDE_RENAME) {
4482 fcnp->cn_flags |= CN_SECLUDE_RENAME;
4483 _err = VNOP_RENAME(fdvp, *fvpp, fcnp, tdvp, *tvpp, tcnp, ctx);
4484 }
4485 }
4486 } else {
4487 _err = VNOP_RENAME(fdvp, *fvpp, fcnp, tdvp, *tvpp, tcnp, ctx);
4488 }
4489 }
4490
4491 /*
4492 * If moved to a new directory that is restricted,
4493 * set the restricted flag on the item moved.
4494 */
4495 if (_err == 0) {
4496 _err = vnode_flags(tdvp, &tdfflags, ctx);
4497 if (_err == 0) {
4498 uint32_t inherit_flags = tdfflags & (UF_DATAVAULT | SF_RESTRICTED);
4499 if (inherit_flags) {
4500 uint32_t fflags;
4501 _err = vnode_flags(*fvpp, &fflags, ctx);
4502 if (_err == 0 && fflags != (fflags | inherit_flags)) {
4503 struct vnode_attr va;
4504 VATTR_INIT(&va);
4505 VATTR_SET(&va, va_flags, fflags | inherit_flags);
4506 _err = vnode_setattr(vp: *fvpp, vap: &va, ctx);
4507 }
4508 }
4509 }
4510 }
4511
4512#if CONFIG_MACF
4513 if (_err == 0) {
4514 mac_vnode_notify_rename(
4515 ctx, /* ctx */
4516 fvp: *fvpp, /* fvp */
4517 fdvp, /* fdvp */
4518 fcnp, /* fcnp */
4519 tvp: *tvpp, /* tvp */
4520 tdvp, /* tdvp */
4521 tcnp, /* tcnp */
4522 swap: (flags & VFS_RENAME_SWAP) /* swap */
4523 );
4524 }
4525#endif
4526
4527#if CONFIG_APPLEDOUBLE
4528 /*
4529 * Rename any associated extended attribute file (._ AppleDouble file).
4530 */
4531 if (_err == 0 && !NATIVE_XATTR(fdvp) && xfromname != NULL) {
4532 int error = 0;
4533
4534 /*
4535 * Get destination attribute file vnode.
4536 * Note that tdvp already has an iocount reference. Make sure to check that we
4537 * get a valid vnode from namei.
4538 */
4539 tond = kalloc_type(struct nameidata, Z_WAITOK);
4540 NDINIT(tond, RENAME, OP_RENAME,
4541 NOCACHE | NOFOLLOW | USEDVP | CN_NBMOUNTLOOK, UIO_SYSSPACE,
4542 CAST_USER_ADDR_T(xtoname), ctx);
4543 tond->ni_dvp = tdvp;
4544 error = namei(ndp: tond);
4545
4546 if (error) {
4547 goto ad_error;
4548 }
4549
4550 if (tond->ni_vp) {
4551 dst_attr_vp = tond->ni_vp;
4552 }
4553
4554 if (src_attr_vp) {
4555 const char *old_name = src_attr_vp->v_name;
4556 vnode_t old_parent = src_attr_vp->v_parent;
4557
4558 if (batched) {
4559 error = VNOP_COMPOUND_RENAME(fdvp, fvpp: &src_attr_vp, fcnp: &fromnd->ni_cnd, NULL,
4560 tdvp, tvpp: &dst_attr_vp, tcnp: &tond->ni_cnd, NULL,
4561 flags: 0, ctx);
4562 } else {
4563 error = VNOP_RENAME(fdvp, src_attr_vp, &fromnd->ni_cnd,
4564 tdvp, dst_attr_vp, &tond->ni_cnd, ctx);
4565 }
4566
4567 if (error == 0 && old_name == src_attr_vp->v_name &&
4568 old_parent == src_attr_vp->v_parent) {
4569 int update_flags = VNODE_UPDATE_NAME;
4570
4571 if (fdvp != tdvp) {
4572 update_flags |= VNODE_UPDATE_PARENT;
4573 }
4574
4575 if ((src_attr_vp->v_mount->mnt_vtable->vfc_vfsflags & VFC_VFSVNOP_NOUPDATEID_RENAME) == 0) {
4576 vnode_update_identity(vp: src_attr_vp, dvp: tdvp,
4577 name: tond->ni_cnd.cn_nameptr,
4578 name_len: tond->ni_cnd.cn_namelen,
4579 name_hashval: tond->ni_cnd.cn_hash,
4580 flags: update_flags);
4581 }
4582 }
4583
4584 /* kevent notifications for moving resource files
4585 * _err is zero if we're here, so no need to notify directories, code
4586 * below will do that. only need to post the rename on the source and
4587 * possibly a delete on the dest
4588 */
4589 post_event_if_success(src_attr_vp, error, NOTE_RENAME);
4590 if (dst_attr_vp) {
4591 post_event_if_success(dst_attr_vp, error, NOTE_DELETE);
4592 }
4593 } else if (dst_attr_vp) {
4594 /*
4595 * Just delete destination attribute file vnode if it exists, since
4596 * we didn't have a source attribute file.
4597 * Note that tdvp already has an iocount reference.
4598 */
4599
4600 struct vnop_remove_args args;
4601
4602 args.a_desc = &vnop_remove_desc;
4603 args.a_dvp = tdvp;
4604 args.a_vp = dst_attr_vp;
4605 args.a_cnp = &tond->ni_cnd;
4606 args.a_context = ctx;
4607
4608 if (error == 0) {
4609 error = (*tdvp->v_op[vnop_remove_desc.vdesc_offset])(&args);
4610
4611 if (error == 0) {
4612 vnode_setneedinactive(vp: dst_attr_vp);
4613 }
4614 }
4615
4616 /* kevent notification for deleting the destination's attribute file
4617 * if it existed. Only need to post the delete on the destination, since
4618 * the code below will handle the directories.
4619 */
4620 post_event_if_success(dst_attr_vp, error, NOTE_DELETE);
4621 }
4622 }
4623ad_error:
4624 if (src_attr_vp) {
4625 vnode_put(vp: src_attr_vp);
4626 nameidone(fromnd);
4627 }
4628 if (dst_attr_vp) {
4629 vnode_put(vp: dst_attr_vp);
4630 nameidone(tond);
4631 }
4632 if (xfromname && xfromname != &smallname1[0]) {
4633 kfree_data(xfromname, xfromname_len);
4634 }
4635 if (xtoname && xtoname != &smallname2[0]) {
4636 kfree_data(xtoname, xtoname_len);
4637 }
4638#endif /* CONFIG_APPLEDOUBLE */
4639 kfree_type(struct nameidata, fromnd);
4640 kfree_type(struct nameidata, tond);
4641 return _err;
4642}
4643
4644
4645#if 0
4646/*
4647*#
4648*#% rename fdvp U U U
4649*#% rename fvp U U U
4650*#% rename tdvp L U U
4651*#% rename tvp X U U
4652*#
4653*/
4654struct vnop_rename_args {
4655 struct vnodeop_desc *a_desc;
4656 vnode_t a_fdvp;
4657 vnode_t a_fvp;
4658 struct componentname *a_fcnp;
4659 vnode_t a_tdvp;
4660 vnode_t a_tvp;
4661 struct componentname *a_tcnp;
4662 vfs_context_t a_context;
4663};
4664#endif /* 0*/
4665errno_t
4666VNOP_RENAME(struct vnode *fdvp, struct vnode *fvp, struct componentname *fcnp,
4667 struct vnode *tdvp, struct vnode *tvp, struct componentname *tcnp,
4668 vfs_context_t ctx)
4669{
4670 int _err = 0;
4671 struct vnop_rename_args a;
4672
4673 a.a_desc = &vnop_rename_desc;
4674 a.a_fdvp = fdvp;
4675 a.a_fvp = fvp;
4676 a.a_fcnp = fcnp;
4677 a.a_tdvp = tdvp;
4678 a.a_tvp = tvp;
4679 a.a_tcnp = tcnp;
4680 a.a_context = ctx;
4681
4682 /* do the rename of the main file. */
4683 _err = (*fdvp->v_op[vnop_rename_desc.vdesc_offset])(&a);
4684 DTRACE_FSINFO(rename, vnode_t, fdvp);
4685
4686 if (_err) {
4687 return _err;
4688 }
4689
4690 return post_rename(fdvp, fvp, tdvp, tvp);
4691}
4692
4693static errno_t
4694post_rename(vnode_t fdvp, vnode_t fvp, vnode_t tdvp, vnode_t tvp)
4695{
4696 if (tvp && tvp != fvp) {
4697 vnode_setneedinactive(vp: tvp);
4698 }
4699
4700 /* Wrote at least one directory. If transplanted a dir, also changed link counts */
4701 int events = NOTE_WRITE;
4702 if (vnode_isdir(vp: fvp)) {
4703 /* Link count on dir changed only if we are moving a dir and...
4704 * --Moved to new dir, not overwriting there
4705 * --Kept in same dir and DID overwrite
4706 */
4707 if (((fdvp != tdvp) && (!tvp)) || ((fdvp == tdvp) && (tvp))) {
4708 events |= NOTE_LINK;
4709 }
4710 }
4711
4712 lock_vnode_and_post(fdvp, events);
4713 if (fdvp != tdvp) {
4714 lock_vnode_and_post(tdvp, events);
4715 }
4716
4717 /* If you're replacing the target, post a deletion for it */
4718 if (tvp) {
4719 lock_vnode_and_post(tvp, NOTE_DELETE);
4720 }
4721
4722 lock_vnode_and_post(fvp, NOTE_RENAME);
4723
4724 return 0;
4725}
4726
4727#if 0
4728/*
4729*#
4730*#% renamex fdvp U U U
4731*#% renamex fvp U U U
4732*#% renamex tdvp L U U
4733*#% renamex tvp X U U
4734*#
4735*/
4736struct vnop_renamex_args {
4737 struct vnodeop_desc *a_desc;
4738 vnode_t a_fdvp;
4739 vnode_t a_fvp;
4740 struct componentname *a_fcnp;
4741 vnode_t a_tdvp;
4742 vnode_t a_tvp;
4743 struct componentname *a_tcnp;
4744 vfs_rename_flags_t a_flags;
4745 vfs_context_t a_context;
4746};
4747#endif /* 0*/
4748errno_t
4749VNOP_RENAMEX(struct vnode *fdvp, struct vnode *fvp, struct componentname *fcnp,
4750 struct vnode *tdvp, struct vnode *tvp, struct componentname *tcnp,
4751 vfs_rename_flags_t flags, vfs_context_t ctx)
4752{
4753 int _err = 0;
4754 struct vnop_renamex_args a;
4755
4756 a.a_desc = &vnop_renamex_desc;
4757 a.a_fdvp = fdvp;
4758 a.a_fvp = fvp;
4759 a.a_fcnp = fcnp;
4760 a.a_tdvp = tdvp;
4761 a.a_tvp = tvp;
4762 a.a_tcnp = tcnp;
4763 a.a_flags = flags;
4764 a.a_context = ctx;
4765
4766 /* do the rename of the main file. */
4767 _err = (*fdvp->v_op[vnop_renamex_desc.vdesc_offset])(&a);
4768 DTRACE_FSINFO(renamex, vnode_t, fdvp);
4769
4770 if (_err) {
4771 return _err;
4772 }
4773
4774 return post_rename(fdvp, fvp, tdvp, tvp);
4775}
4776
4777
4778int
4779VNOP_COMPOUND_RENAME(
4780 struct vnode *fdvp, struct vnode **fvpp, struct componentname *fcnp, struct vnode_attr *fvap,
4781 struct vnode *tdvp, struct vnode **tvpp, struct componentname *tcnp, struct vnode_attr *tvap,
4782 uint32_t flags, vfs_context_t ctx)
4783{
4784 int _err = 0;
4785 int events;
4786 struct vnop_compound_rename_args a;
4787 int no_fvp, no_tvp;
4788
4789 no_fvp = (*fvpp) == NULLVP;
4790 no_tvp = (*tvpp) == NULLVP;
4791
4792 a.a_desc = &vnop_compound_rename_desc;
4793
4794 a.a_fdvp = fdvp;
4795 a.a_fvpp = fvpp;
4796 a.a_fcnp = fcnp;
4797 a.a_fvap = fvap;
4798
4799 a.a_tdvp = tdvp;
4800 a.a_tvpp = tvpp;
4801 a.a_tcnp = tcnp;
4802 a.a_tvap = tvap;
4803
4804 a.a_flags = flags;
4805 a.a_context = ctx;
4806 a.a_rename_authorizer = vn_authorize_rename;
4807 a.a_reserved = NULL;
4808
4809 /* do the rename of the main file. */
4810 _err = (*fdvp->v_op[vnop_compound_rename_desc.vdesc_offset])(&a);
4811 DTRACE_FSINFO(compound_rename, vnode_t, fdvp);
4812
4813 if (_err == 0) {
4814 if (*tvpp && *tvpp != *fvpp) {
4815 vnode_setneedinactive(vp: *tvpp);
4816 }
4817 }
4818
4819 /* Wrote at least one directory. If transplanted a dir, also changed link counts */
4820 if (_err == 0 && *fvpp != *tvpp) {
4821 if (!*fvpp) {
4822 panic("No fvpp after compound rename?");
4823 }
4824
4825 events = NOTE_WRITE;
4826 if (vnode_isdir(vp: *fvpp)) {
4827 /* Link count on dir changed only if we are moving a dir and...
4828 * --Moved to new dir, not overwriting there
4829 * --Kept in same dir and DID overwrite
4830 */
4831 if (((fdvp != tdvp) && (!*tvpp)) || ((fdvp == tdvp) && (*tvpp))) {
4832 events |= NOTE_LINK;
4833 }
4834 }
4835
4836 lock_vnode_and_post(fdvp, events);
4837 if (fdvp != tdvp) {
4838 lock_vnode_and_post(tdvp, events);
4839 }
4840
4841 /* If you're replacing the target, post a deletion for it */
4842 if (*tvpp) {
4843 lock_vnode_and_post(*tvpp, NOTE_DELETE);
4844 }
4845
4846 lock_vnode_and_post(*fvpp, NOTE_RENAME);
4847 }
4848
4849 if (no_fvp) {
4850 lookup_compound_vnop_post_hook(error: _err, dvp: fdvp, vp: *fvpp, ndp: fcnp->cn_ndp, did_create: 0);
4851 }
4852 if (no_tvp && *tvpp != NULLVP) {
4853 lookup_compound_vnop_post_hook(error: _err, dvp: tdvp, vp: *tvpp, ndp: tcnp->cn_ndp, did_create: 0);
4854 }
4855
4856 if (_err && _err != EKEEPLOOKING) {
4857 if (*fvpp) {
4858 vnode_put(vp: *fvpp);
4859 *fvpp = NULLVP;
4860 }
4861 if (*tvpp) {
4862 vnode_put(vp: *tvpp);
4863 *tvpp = NULLVP;
4864 }
4865 }
4866
4867 return _err;
4868}
4869
4870int
4871vn_mkdir(struct vnode *dvp, struct vnode **vpp, struct nameidata *ndp,
4872 struct vnode_attr *vap, vfs_context_t ctx)
4873{
4874 if (ndp->ni_cnd.cn_nameiop != CREATE) {
4875 panic("Non-CREATE nameiop in vn_mkdir()?");
4876 }
4877
4878 if (vnode_compound_mkdir_available(vp: dvp)) {
4879 return VNOP_COMPOUND_MKDIR(dvp, vpp, ndp, vap, ctx);
4880 } else {
4881 return VNOP_MKDIR(dvp, vpp, &ndp->ni_cnd, vap, ctx);
4882 }
4883}
4884
4885#if 0
4886/*
4887*#
4888*#% mkdir dvp L U U
4889*#% mkdir vpp - L -
4890*#
4891*/
4892struct vnop_mkdir_args {
4893 struct vnodeop_desc *a_desc;
4894 vnode_t a_dvp;
4895 vnode_t *a_vpp;
4896 struct componentname *a_cnp;
4897 struct vnode_attr *a_vap;
4898 vfs_context_t a_context;
4899};
4900#endif /* 0*/
4901errno_t
4902VNOP_MKDIR(struct vnode *dvp, struct vnode **vpp, struct componentname *cnp,
4903 struct vnode_attr *vap, vfs_context_t ctx)
4904{
4905 int _err;
4906 struct vnop_mkdir_args a;
4907
4908 a.a_desc = &vnop_mkdir_desc;
4909 a.a_dvp = dvp;
4910 a.a_vpp = vpp;
4911 a.a_cnp = cnp;
4912 a.a_vap = vap;
4913 a.a_context = ctx;
4914
4915 _err = (*dvp->v_op[vnop_mkdir_desc.vdesc_offset])(&a);
4916 if (_err == 0 && *vpp) {
4917 DTRACE_FSINFO(mkdir, vnode_t, *vpp);
4918 }
4919#if CONFIG_APPLEDOUBLE
4920 if (_err == 0 && !NATIVE_XATTR(dvp)) {
4921 /*
4922 * Remove stale Apple Double file (if any).
4923 */
4924 xattrfile_remove(dvp, basename: cnp->cn_nameptr, ctx, force: 0);
4925 }
4926#endif /* CONFIG_APPLEDOUBLE */
4927
4928 post_event_if_success(dvp, _err, NOTE_LINK | NOTE_WRITE);
4929
4930 return _err;
4931}
4932
4933int
4934VNOP_COMPOUND_MKDIR(struct vnode *dvp, struct vnode **vpp, struct nameidata *ndp,
4935 struct vnode_attr *vap, vfs_context_t ctx)
4936{
4937 int _err;
4938 struct vnop_compound_mkdir_args a;
4939
4940 a.a_desc = &vnop_compound_mkdir_desc;
4941 a.a_dvp = dvp;
4942 a.a_vpp = vpp;
4943 a.a_cnp = &ndp->ni_cnd;
4944 a.a_vap = vap;
4945 a.a_flags = 0;
4946 a.a_context = ctx;
4947#if 0
4948 a.a_mkdir_authorizer = vn_authorize_mkdir;
4949#endif /* 0 */
4950 a.a_reserved = NULL;
4951
4952 _err = (*dvp->v_op[vnop_compound_mkdir_desc.vdesc_offset])(&a);
4953 if (_err == 0 && *vpp) {
4954 DTRACE_FSINFO(compound_mkdir, vnode_t, *vpp);
4955 }
4956#if CONFIG_APPLEDOUBLE
4957 if (_err == 0 && !NATIVE_XATTR(dvp)) {
4958 /*
4959 * Remove stale Apple Double file (if any).
4960 */
4961 xattrfile_remove(dvp, basename: ndp->ni_cnd.cn_nameptr, ctx, force: 0);
4962 }
4963#endif /* CONFIG_APPLEDOUBLE */
4964
4965 post_event_if_success(dvp, _err, NOTE_LINK | NOTE_WRITE);
4966
4967 lookup_compound_vnop_post_hook(error: _err, dvp, vp: *vpp, ndp, did_create: (_err == 0));
4968 if (*vpp && _err && _err != EKEEPLOOKING) {
4969 vnode_put(vp: *vpp);
4970 *vpp = NULLVP;
4971 }
4972
4973 return _err;
4974}
4975
4976int
4977vn_rmdir(vnode_t dvp, vnode_t *vpp, struct nameidata *ndp, struct vnode_attr *vap, vfs_context_t ctx)
4978{
4979 if (vnode_compound_rmdir_available(vp: dvp)) {
4980 return VNOP_COMPOUND_RMDIR(dvp, vpp, ndp, vap, ctx);
4981 } else {
4982 if (*vpp == NULLVP) {
4983 panic("NULL vp, but not a compound VNOP?");
4984 }
4985 if (vap != NULL) {
4986 panic("Non-NULL vap, but not a compound VNOP?");
4987 }
4988 return VNOP_RMDIR(dvp, *vpp, &ndp->ni_cnd, ctx);
4989 }
4990}
4991
4992#if 0
4993/*
4994*#
4995*#% rmdir dvp L U U
4996*#% rmdir vp L U U
4997*#
4998*/
4999struct vnop_rmdir_args {
5000 struct vnodeop_desc *a_desc;
5001 vnode_t a_dvp;
5002 vnode_t a_vp;
5003 struct componentname *a_cnp;
5004 vfs_context_t a_context;
5005};
5006
5007#endif /* 0*/
5008errno_t
5009VNOP_RMDIR(struct vnode *dvp, struct vnode *vp, struct componentname *cnp, vfs_context_t ctx)
5010{
5011 int _err;
5012 struct vnop_rmdir_args a;
5013
5014 a.a_desc = &vnop_rmdir_desc;
5015 a.a_dvp = dvp;
5016 a.a_vp = vp;
5017 a.a_cnp = cnp;
5018 a.a_context = ctx;
5019
5020 _err = (*vp->v_op[vnop_rmdir_desc.vdesc_offset])(&a);
5021 DTRACE_FSINFO(rmdir, vnode_t, vp);
5022
5023 if (_err == 0) {
5024 vnode_setneedinactive(vp);
5025#if CONFIG_APPLEDOUBLE
5026 if (!(NATIVE_XATTR(dvp))) {
5027 /*
5028 * Remove any associated extended attribute file (._ AppleDouble file).
5029 */
5030 xattrfile_remove(dvp, basename: cnp->cn_nameptr, ctx, force: 1);
5031 }
5032#endif
5033 }
5034
5035 /* If you delete a dir, it loses its "." reference --> NOTE_LINK */
5036 post_event_if_success(vp, _err, NOTE_DELETE | NOTE_LINK);
5037 post_event_if_success(dvp, _err, NOTE_LINK | NOTE_WRITE);
5038
5039 return _err;
5040}
5041
5042int
5043VNOP_COMPOUND_RMDIR(struct vnode *dvp, struct vnode **vpp, struct nameidata *ndp,
5044 struct vnode_attr *vap, vfs_context_t ctx)
5045{
5046 int _err;
5047 struct vnop_compound_rmdir_args a;
5048 int no_vp;
5049
5050 a.a_desc = &vnop_mkdir_desc;
5051 a.a_dvp = dvp;
5052 a.a_vpp = vpp;
5053 a.a_cnp = &ndp->ni_cnd;
5054 a.a_vap = vap;
5055 a.a_flags = 0;
5056 a.a_context = ctx;
5057 a.a_rmdir_authorizer = vn_authorize_rmdir;
5058 a.a_reserved = NULL;
5059
5060 no_vp = (*vpp == NULLVP);
5061
5062 _err = (*dvp->v_op[vnop_compound_rmdir_desc.vdesc_offset])(&a);
5063 if (_err == 0 && *vpp) {
5064 DTRACE_FSINFO(compound_rmdir, vnode_t, *vpp);
5065 }
5066#if CONFIG_APPLEDOUBLE
5067 if (_err == 0 && !NATIVE_XATTR(dvp)) {
5068 /*
5069 * Remove stale Apple Double file (if any).
5070 */
5071 xattrfile_remove(dvp, basename: ndp->ni_cnd.cn_nameptr, ctx, force: 0);
5072 }
5073#endif
5074
5075 if (*vpp) {
5076 post_event_if_success(*vpp, _err, NOTE_DELETE | NOTE_LINK);
5077 }
5078 post_event_if_success(dvp, _err, NOTE_LINK | NOTE_WRITE);
5079
5080 if (no_vp) {
5081 lookup_compound_vnop_post_hook(error: _err, dvp, vp: *vpp, ndp, did_create: 0);
5082
5083#if 0 /* Removing orphaned ._ files requires a vp.... */
5084 if (*vpp && _err && _err != EKEEPLOOKING) {
5085 vnode_put(*vpp);
5086 *vpp = NULLVP;
5087 }
5088#endif /* 0 */
5089 }
5090
5091 return _err;
5092}
5093
5094#if CONFIG_APPLEDOUBLE
5095/*
5096 * Remove a ._ AppleDouble file
5097 */
5098#define AD_STALE_SECS (180)
5099static void
5100xattrfile_remove(vnode_t dvp, const char * basename, vfs_context_t ctx, int force)
5101{
5102 vnode_t xvp;
5103 struct nameidata nd;
5104 char smallname[64];
5105 char *filename = NULL;
5106 size_t alloc_len;
5107 size_t copy_len;
5108
5109 if ((basename == NULL) || (basename[0] == '\0') ||
5110 (basename[0] == '.' && basename[1] == '_')) {
5111 return;
5112 }
5113 filename = &smallname[0];
5114 alloc_len = snprintf(filename, count: sizeof(smallname), "._%s", basename);
5115 if (alloc_len >= sizeof(smallname)) {
5116 alloc_len++; /* snprintf result doesn't include '\0' */
5117 filename = kalloc_data(alloc_len, Z_WAITOK);
5118 copy_len = snprintf(filename, count: alloc_len, "._%s", basename);
5119 }
5120 NDINIT(&nd, DELETE, OP_UNLINK, WANTPARENT | LOCKLEAF | NOFOLLOW | USEDVP, UIO_SYSSPACE,
5121 CAST_USER_ADDR_T(filename), ctx);
5122 nd.ni_dvp = dvp;
5123 if (namei(ndp: &nd) != 0) {
5124 goto out2;
5125 }
5126
5127 xvp = nd.ni_vp;
5128 nameidone(&nd);
5129 if (xvp->v_type != VREG) {
5130 goto out1;
5131 }
5132
5133 /*
5134 * When creating a new object and a "._" file already
5135 * exists, check to see if it's a stale "._" file. These are
5136 * typically AppleDouble (AD) files generated via XNU's
5137 * VFS compatibility shims for storing XATTRs and streams
5138 * on filesystems that do not support them natively.
5139 */
5140 if (!force) {
5141 struct vnode_attr va;
5142
5143 VATTR_INIT(&va);
5144 VATTR_WANTED(&va, va_data_size);
5145 VATTR_WANTED(&va, va_modify_time);
5146 VATTR_WANTED(&va, va_change_time);
5147
5148 if (VNOP_GETATTR(vp: xvp, vap: &va, ctx) == 0 &&
5149 VATTR_IS_SUPPORTED(&va, va_data_size) &&
5150 va.va_data_size != 0) {
5151 struct timeval tv_compare = {};
5152 struct timeval tv_now = {};
5153
5154 /*
5155 * If the file exists (and has non-zero size), then use the newer of
5156 * chgtime / modtime to compare against present time. Note that setting XATTRs or updating
5157 * streams through the compatibility interfaces may not trigger chgtime to be updated, so
5158 * checking either modtime or chgtime is useful.
5159 */
5160 if (VATTR_IS_SUPPORTED(&va, va_modify_time) && (va.va_modify_time.tv_sec)) {
5161 if (VATTR_IS_SUPPORTED(&va, va_change_time) && (va.va_change_time.tv_sec)) {
5162 tv_compare.tv_sec = va.va_change_time.tv_sec;
5163 if (tv_compare.tv_sec < va.va_modify_time.tv_sec) {
5164 tv_compare.tv_sec = va.va_modify_time.tv_sec;
5165 }
5166 } else {
5167 /* fall back to mod-time alone if chgtime not supported or set to 0 */
5168 tv_compare.tv_sec = va.va_modify_time.tv_sec;
5169 }
5170 }
5171
5172 /* Now, we have a time to compare against, compare against AD_STALE_SEC */
5173 microtime(tv: &tv_now);
5174 if ((tv_compare.tv_sec > 0) &&
5175 (tv_now.tv_sec > tv_compare.tv_sec) &&
5176 ((tv_now.tv_sec - tv_compare.tv_sec) > AD_STALE_SECS)) {
5177 force = 1; /* must be stale */
5178 }
5179 }
5180 }
5181
5182 if (force) {
5183 int error;
5184
5185 error = VNOP_REMOVE(dvp, vp: xvp, cnp: &nd.ni_cnd, flags: 0, ctx);
5186 if (error == 0) {
5187 vnode_setneedinactive(vp: xvp);
5188 }
5189
5190 post_event_if_success(xvp, error, NOTE_DELETE);
5191 post_event_if_success(dvp, error, NOTE_WRITE);
5192 }
5193
5194out1:
5195 vnode_put(vp: dvp);
5196 vnode_put(vp: xvp);
5197out2:
5198 if (filename && filename != &smallname[0]) {
5199 kfree_data(filename, alloc_len);
5200 }
5201}
5202
5203/*
5204 * Shadow uid/gid/mod to a ._ AppleDouble file
5205 */
5206static void
5207xattrfile_setattr(vnode_t dvp, const char * basename, struct vnode_attr * vap,
5208 vfs_context_t ctx)
5209{
5210 vnode_t xvp;
5211 struct nameidata nd;
5212 char smallname[64];
5213 char *filename = NULL;
5214 size_t alloc_len;
5215 size_t copy_len;
5216
5217 if ((dvp == NULLVP) ||
5218 (basename == NULL) || (basename[0] == '\0') ||
5219 (basename[0] == '.' && basename[1] == '_')) {
5220 return;
5221 }
5222 filename = &smallname[0];
5223 alloc_len = snprintf(filename, count: sizeof(smallname), "._%s", basename);
5224 if (alloc_len >= sizeof(smallname)) {
5225 alloc_len++; /* snprintf result doesn't include '\0' */
5226 filename = kalloc_data(alloc_len, Z_WAITOK);
5227 copy_len = snprintf(filename, count: alloc_len, "._%s", basename);
5228 }
5229 NDINIT(&nd, LOOKUP, OP_SETATTR, NOFOLLOW | USEDVP, UIO_SYSSPACE,
5230 CAST_USER_ADDR_T(filename), ctx);
5231 nd.ni_dvp = dvp;
5232 if (namei(ndp: &nd) != 0) {
5233 goto out2;
5234 }
5235
5236 xvp = nd.ni_vp;
5237 nameidone(&nd);
5238
5239 if (xvp->v_type == VREG) {
5240 struct vnop_setattr_args a;
5241
5242 a.a_desc = &vnop_setattr_desc;
5243 a.a_vp = xvp;
5244 a.a_vap = vap;
5245 a.a_context = ctx;
5246
5247 (void) (*xvp->v_op[vnop_setattr_desc.vdesc_offset])(&a);
5248 }
5249
5250 vnode_put(vp: xvp);
5251out2:
5252 if (filename && filename != &smallname[0]) {
5253 kfree_data(filename, alloc_len);
5254 }
5255}
5256#endif /* CONFIG_APPLEDOUBLE */
5257
5258 #if 0
5259/*
5260*#
5261*#% symlink dvp L U U
5262*#% symlink vpp - U -
5263*#
5264*/
5265struct vnop_symlink_args {
5266 struct vnodeop_desc *a_desc;
5267 vnode_t a_dvp;
5268 vnode_t *a_vpp;
5269 struct componentname *a_cnp;
5270 struct vnode_attr *a_vap;
5271 char *a_target;
5272 vfs_context_t a_context;
5273};
5274
5275#endif /* 0*/
5276errno_t
5277VNOP_SYMLINK(struct vnode *dvp, struct vnode **vpp, struct componentname *cnp,
5278 struct vnode_attr *vap, char *target, vfs_context_t ctx)
5279{
5280 int _err;
5281 struct vnop_symlink_args a;
5282
5283 a.a_desc = &vnop_symlink_desc;
5284 a.a_dvp = dvp;
5285 a.a_vpp = vpp;
5286 a.a_cnp = cnp;
5287 a.a_vap = vap;
5288 a.a_target = target;
5289 a.a_context = ctx;
5290
5291 _err = (*dvp->v_op[vnop_symlink_desc.vdesc_offset])(&a);
5292 DTRACE_FSINFO(symlink, vnode_t, dvp);
5293#if CONFIG_APPLEDOUBLE
5294 if (_err == 0 && !NATIVE_XATTR(dvp)) {
5295 /*
5296 * Remove stale Apple Double file (if any). Posts its own knotes
5297 */
5298 xattrfile_remove(dvp, basename: cnp->cn_nameptr, ctx, force: 0);
5299 }
5300#endif /* CONFIG_APPLEDOUBLE */
5301
5302 post_event_if_success(dvp, _err, NOTE_WRITE);
5303
5304 return _err;
5305}
5306
5307#if 0
5308/*
5309*#
5310*#% readdir vp L L L
5311*#
5312*/
5313struct vnop_readdir_args {
5314 struct vnodeop_desc *a_desc;
5315 vnode_t a_vp;
5316 struct uio *a_uio;
5317 int a_flags;
5318 int *a_eofflag;
5319 int *a_numdirent;
5320 vfs_context_t a_context;
5321};
5322
5323#endif /* 0*/
5324errno_t
5325VNOP_READDIR(struct vnode *vp, struct uio *uio, int flags, int *eofflag,
5326 int *numdirent, vfs_context_t ctx)
5327{
5328 int _err;
5329 struct vnop_readdir_args a;
5330#if CONFIG_DTRACE
5331 user_ssize_t resid = uio_resid(a_uio: uio);
5332#endif
5333
5334 a.a_desc = &vnop_readdir_desc;
5335 a.a_vp = vp;
5336 a.a_uio = uio;
5337 a.a_flags = flags;
5338 a.a_eofflag = eofflag;
5339 a.a_numdirent = numdirent;
5340 a.a_context = ctx;
5341
5342 _err = (*vp->v_op[vnop_readdir_desc.vdesc_offset])(&a);
5343 DTRACE_FSINFO_IO(readdir,
5344 vnode_t, vp, user_ssize_t, (resid - uio_resid(uio)));
5345
5346 return _err;
5347}
5348
5349#if 0
5350/*
5351*#
5352*#% readdirattr vp L L L
5353*#
5354*/
5355struct vnop_readdirattr_args {
5356 struct vnodeop_desc *a_desc;
5357 vnode_t a_vp;
5358 struct attrlist *a_alist;
5359 struct uio *a_uio;
5360 uint32_t a_maxcount;
5361 uint32_t a_options;
5362 uint32_t *a_newstate;
5363 int *a_eofflag;
5364 uint32_t *a_actualcount;
5365 vfs_context_t a_context;
5366};
5367
5368#endif /* 0*/
5369errno_t
5370VNOP_READDIRATTR(struct vnode *vp, struct attrlist *alist, struct uio *uio, uint32_t maxcount,
5371 uint32_t options, uint32_t *newstate, int *eofflag, uint32_t *actualcount, vfs_context_t ctx)
5372{
5373 int _err;
5374 struct vnop_readdirattr_args a;
5375#if CONFIG_DTRACE
5376 user_ssize_t resid = uio_resid(a_uio: uio);
5377#endif
5378
5379 a.a_desc = &vnop_readdirattr_desc;
5380 a.a_vp = vp;
5381 a.a_alist = alist;
5382 a.a_uio = uio;
5383 a.a_maxcount = maxcount;
5384 a.a_options = options;
5385 a.a_newstate = newstate;
5386 a.a_eofflag = eofflag;
5387 a.a_actualcount = actualcount;
5388 a.a_context = ctx;
5389
5390 _err = (*vp->v_op[vnop_readdirattr_desc.vdesc_offset])(&a);
5391 DTRACE_FSINFO_IO(readdirattr,
5392 vnode_t, vp, user_ssize_t, (resid - uio_resid(uio)));
5393
5394 return _err;
5395}
5396
5397#if 0
5398struct vnop_getttrlistbulk_args {
5399 struct vnodeop_desc *a_desc;
5400 vnode_t a_vp;
5401 struct attrlist *a_alist;
5402 struct vnode_attr *a_vap;
5403 struct uio *a_uio;
5404 void *a_private
5405 uint64_t a_options;
5406 int *a_eofflag;
5407 uint32_t *a_actualcount;
5408 vfs_context_t a_context;
5409};
5410#endif /* 0*/
5411errno_t
5412VNOP_GETATTRLISTBULK(struct vnode *vp, struct attrlist *alist,
5413 struct vnode_attr *vap, struct uio *uio, void *private, uint64_t options,
5414 int32_t *eofflag, int32_t *actualcount, vfs_context_t ctx)
5415{
5416 int _err;
5417 struct vnop_getattrlistbulk_args a;
5418#if CONFIG_DTRACE
5419 user_ssize_t resid = uio_resid(a_uio: uio);
5420#endif
5421
5422 a.a_desc = &vnop_getattrlistbulk_desc;
5423 a.a_vp = vp;
5424 a.a_alist = alist;
5425 a.a_vap = vap;
5426 a.a_uio = uio;
5427 a.a_private = private;
5428 a.a_options = options;
5429 a.a_eofflag = eofflag;
5430 a.a_actualcount = actualcount;
5431 a.a_context = ctx;
5432
5433 _err = (*vp->v_op[vnop_getattrlistbulk_desc.vdesc_offset])(&a);
5434 DTRACE_FSINFO_IO(getattrlistbulk,
5435 vnode_t, vp, user_ssize_t, (resid - uio_resid(uio)));
5436
5437 return _err;
5438}
5439
5440#if 0
5441/*
5442*#
5443*#% readlink vp L L L
5444*#
5445*/
5446struct vnop_readlink_args {
5447 struct vnodeop_desc *a_desc;
5448 vnode_t a_vp;
5449 struct uio *a_uio;
5450 vfs_context_t a_context;
5451};
5452#endif /* 0 */
5453
5454/*
5455 * Returns: 0 Success
5456 * lock_fsnode:ENOENT No such file or directory [only for VFS
5457 * that is not thread safe & vnode is
5458 * currently being/has been terminated]
5459 * <vfs_readlink>:EINVAL
5460 * <vfs_readlink>:???
5461 *
5462 * Note: The return codes from the underlying VFS's readlink routine
5463 * can't be fully enumerated here, since third party VFS authors
5464 * may not limit their error returns to the ones documented here,
5465 * even though this may result in some programs functioning
5466 * incorrectly.
5467 *
5468 * The return codes documented above are those which may currently
5469 * be returned by HFS from hfs_vnop_readlink, not including
5470 * additional error code which may be propagated from underlying
5471 * routines.
5472 */
5473errno_t
5474VNOP_READLINK(struct vnode *vp, struct uio *uio, vfs_context_t ctx)
5475{
5476 int _err;
5477 struct vnop_readlink_args a;
5478#if CONFIG_DTRACE
5479 user_ssize_t resid = uio_resid(a_uio: uio);
5480#endif
5481 a.a_desc = &vnop_readlink_desc;
5482 a.a_vp = vp;
5483 a.a_uio = uio;
5484 a.a_context = ctx;
5485
5486 _err = (*vp->v_op[vnop_readlink_desc.vdesc_offset])(&a);
5487 DTRACE_FSINFO_IO(readlink,
5488 vnode_t, vp, user_ssize_t, (resid - uio_resid(uio)));
5489
5490 return _err;
5491}
5492
5493#if 0
5494/*
5495*#
5496*#% inactive vp L U U
5497*#
5498*/
5499struct vnop_inactive_args {
5500 struct vnodeop_desc *a_desc;
5501 vnode_t a_vp;
5502 vfs_context_t a_context;
5503};
5504#endif /* 0*/
5505errno_t
5506VNOP_INACTIVE(struct vnode *vp, vfs_context_t ctx)
5507{
5508 int _err;
5509 struct vnop_inactive_args a;
5510
5511 a.a_desc = &vnop_inactive_desc;
5512 a.a_vp = vp;
5513 a.a_context = ctx;
5514
5515 _err = (*vp->v_op[vnop_inactive_desc.vdesc_offset])(&a);
5516 DTRACE_FSINFO(inactive, vnode_t, vp);
5517
5518#if NAMEDSTREAMS
5519 /* For file systems that do not support namedstream natively, mark
5520 * the shadow stream file vnode to be recycled as soon as the last
5521 * reference goes away. To avoid re-entering reclaim code, do not
5522 * call recycle on terminating namedstream vnodes.
5523 */
5524 if (vnode_isnamedstream(vp) &&
5525 (vp->v_parent != NULLVP) &&
5526 vnode_isshadow(vp) &&
5527 ((vp->v_lflag & VL_TERMINATE) == 0)) {
5528 vnode_recycle(vp);
5529 }
5530#endif
5531
5532 return _err;
5533}
5534
5535
5536#if 0
5537/*
5538*#
5539*#% reclaim vp U U U
5540*#
5541*/
5542struct vnop_reclaim_args {
5543 struct vnodeop_desc *a_desc;
5544 vnode_t a_vp;
5545 vfs_context_t a_context;
5546};
5547#endif /* 0*/
5548errno_t
5549VNOP_RECLAIM(struct vnode *vp, vfs_context_t ctx)
5550{
5551 int _err;
5552 struct vnop_reclaim_args a;
5553
5554 a.a_desc = &vnop_reclaim_desc;
5555 a.a_vp = vp;
5556 a.a_context = ctx;
5557
5558 _err = (*vp->v_op[vnop_reclaim_desc.vdesc_offset])(&a);
5559 DTRACE_FSINFO(reclaim, vnode_t, vp);
5560
5561 return _err;
5562}
5563
5564
5565/*
5566 * Returns: 0 Success
5567 * lock_fsnode:ENOENT No such file or directory [only for VFS
5568 * that is not thread safe & vnode is
5569 * currently being/has been terminated]
5570 * <vnop_pathconf_desc>:??? [per FS implementation specific]
5571 */
5572#if 0
5573/*
5574*#
5575*#% pathconf vp L L L
5576*#
5577*/
5578struct vnop_pathconf_args {
5579 struct vnodeop_desc *a_desc;
5580 vnode_t a_vp;
5581 int a_name;
5582 int32_t *a_retval;
5583 vfs_context_t a_context;
5584};
5585#endif /* 0*/
5586errno_t
5587VNOP_PATHCONF(struct vnode *vp, int name, int32_t *retval, vfs_context_t ctx)
5588{
5589 int _err;
5590 struct vnop_pathconf_args a;
5591
5592 a.a_desc = &vnop_pathconf_desc;
5593 a.a_vp = vp;
5594 a.a_name = name;
5595 a.a_retval = retval;
5596 a.a_context = ctx;
5597
5598 _err = (*vp->v_op[vnop_pathconf_desc.vdesc_offset])(&a);
5599 DTRACE_FSINFO(pathconf, vnode_t, vp);
5600
5601 return _err;
5602}
5603
5604/*
5605 * Returns: 0 Success
5606 * err_advlock:ENOTSUP
5607 * lf_advlock:???
5608 * <vnop_advlock_desc>:???
5609 *
5610 * Notes: VFS implementations of advisory locking using calls through
5611 * <vnop_advlock_desc> because lock enforcement does not occur
5612 * locally should try to limit themselves to the return codes
5613 * documented above for lf_advlock and err_advlock.
5614 */
5615#if 0
5616/*
5617*#
5618*#% advlock vp U U U
5619*#
5620*/
5621struct vnop_advlock_args {
5622 struct vnodeop_desc *a_desc;
5623 vnode_t a_vp;
5624 caddr_t a_id;
5625 int a_op;
5626 struct flock *a_fl;
5627 int a_flags;
5628 vfs_context_t a_context;
5629};
5630#endif /* 0*/
5631errno_t
5632VNOP_ADVLOCK(struct vnode *vp, caddr_t id, int op, struct flock *fl, int flags, vfs_context_t ctx, struct timespec *timeout)
5633{
5634 int _err;
5635 struct vnop_advlock_args a;
5636
5637 a.a_desc = &vnop_advlock_desc;
5638 a.a_vp = vp;
5639 a.a_id = id;
5640 a.a_op = op;
5641 a.a_fl = fl;
5642 a.a_flags = flags;
5643 a.a_context = ctx;
5644 a.a_timeout = timeout;
5645
5646 /* Disallow advisory locking on non-seekable vnodes */
5647 if (vnode_isfifo(vp)) {
5648 _err = err_advlock(ap: &a);
5649 } else {
5650 if ((vp->v_flag & VLOCKLOCAL)) {
5651 /* Advisory locking done at this layer */
5652 _err = lf_advlock(&a);
5653 } else if (flags & F_OFD_LOCK) {
5654 /* Non-local locking doesn't work for OFD locks */
5655 _err = err_advlock(ap: &a);
5656 } else if (op == F_TRANSFER) {
5657 /* Non-local locking doesn't have F_TRANSFER */
5658 _err = err_advlock(ap: &a);
5659 } else {
5660 /* Advisory locking done by underlying filesystem */
5661 _err = (*vp->v_op[vnop_advlock_desc.vdesc_offset])(&a);
5662 }
5663 DTRACE_FSINFO(advlock, vnode_t, vp);
5664 if (op == F_UNLCK &&
5665 (flags & (F_FLOCK | F_OFD_LOCK)) != 0) {
5666 post_event_if_success(vp, _err, NOTE_FUNLOCK);
5667 }
5668 }
5669
5670 return _err;
5671}
5672
5673
5674
5675#if 0
5676/*
5677*#
5678*#% allocate vp L L L
5679*#
5680*/
5681struct vnop_allocate_args {
5682 struct vnodeop_desc *a_desc;
5683 vnode_t a_vp;
5684 off_t a_length;
5685 u_int32_t a_flags;
5686 off_t *a_bytesallocated;
5687 off_t a_offset;
5688 vfs_context_t a_context;
5689};
5690
5691#endif /* 0*/
5692errno_t
5693VNOP_ALLOCATE(struct vnode *vp, off_t length, u_int32_t flags, off_t *bytesallocated, off_t offset, vfs_context_t ctx)
5694{
5695 int _err;
5696 struct vnop_allocate_args a;
5697
5698 a.a_desc = &vnop_allocate_desc;
5699 a.a_vp = vp;
5700 a.a_length = length;
5701 a.a_flags = flags;
5702 a.a_bytesallocated = bytesallocated;
5703 a.a_offset = offset;
5704 a.a_context = ctx;
5705
5706 _err = (*vp->v_op[vnop_allocate_desc.vdesc_offset])(&a);
5707 DTRACE_FSINFO(allocate, vnode_t, vp);
5708#if CONFIG_FSE
5709 if (_err == 0) {
5710 add_fsevent(FSE_STAT_CHANGED, ctx, FSE_ARG_VNODE, vp, FSE_ARG_DONE);
5711 }
5712#endif
5713
5714 return _err;
5715}
5716
5717#if 0
5718/*
5719*#
5720*#% pagein vp = = =
5721*#
5722*/
5723struct vnop_pagein_args {
5724 struct vnodeop_desc *a_desc;
5725 vnode_t a_vp;
5726 upl_t a_pl;
5727 upl_offset_t a_pl_offset;
5728 off_t a_f_offset;
5729 size_t a_size;
5730 int a_flags;
5731 vfs_context_t a_context;
5732};
5733#endif /* 0*/
5734errno_t
5735VNOP_PAGEIN(struct vnode *vp, upl_t pl, upl_offset_t pl_offset, off_t f_offset, size_t size, int flags, vfs_context_t ctx)
5736{
5737 int _err;
5738 struct vnop_pagein_args a;
5739
5740 a.a_desc = &vnop_pagein_desc;
5741 a.a_vp = vp;
5742 a.a_pl = pl;
5743 a.a_pl_offset = pl_offset;
5744 a.a_f_offset = f_offset;
5745 a.a_size = size;
5746 a.a_flags = flags;
5747 a.a_context = ctx;
5748
5749 _err = (*vp->v_op[vnop_pagein_desc.vdesc_offset])(&a);
5750 DTRACE_FSINFO(pagein, vnode_t, vp);
5751
5752 return _err;
5753}
5754
5755#if 0
5756/*
5757*#
5758*#% pageout vp = = =
5759*#
5760*/
5761struct vnop_pageout_args {
5762 struct vnodeop_desc *a_desc;
5763 vnode_t a_vp;
5764 upl_t a_pl;
5765 upl_offset_t a_pl_offset;
5766 off_t a_f_offset;
5767 size_t a_size;
5768 int a_flags;
5769 vfs_context_t a_context;
5770};
5771
5772#endif /* 0*/
5773errno_t
5774VNOP_PAGEOUT(struct vnode *vp, upl_t pl, upl_offset_t pl_offset, off_t f_offset, size_t size, int flags, vfs_context_t ctx)
5775{
5776 int _err;
5777 struct vnop_pageout_args a;
5778
5779 a.a_desc = &vnop_pageout_desc;
5780 a.a_vp = vp;
5781 a.a_pl = pl;
5782 a.a_pl_offset = pl_offset;
5783 a.a_f_offset = f_offset;
5784 a.a_size = size;
5785 a.a_flags = flags;
5786 a.a_context = ctx;
5787
5788 _err = (*vp->v_op[vnop_pageout_desc.vdesc_offset])(&a);
5789 DTRACE_FSINFO(pageout, vnode_t, vp);
5790
5791 post_event_if_success(vp, _err, NOTE_WRITE);
5792
5793 return _err;
5794}
5795
5796int
5797vn_remove(vnode_t dvp, vnode_t *vpp, struct nameidata *ndp, int32_t flags, struct vnode_attr *vap, vfs_context_t ctx)
5798{
5799 if (vnode_compound_remove_available(vp: dvp)) {
5800 return VNOP_COMPOUND_REMOVE(dvp, vpp, ndp, flags, vap, ctx);
5801 } else {
5802 return VNOP_REMOVE(dvp, vp: *vpp, cnp: &ndp->ni_cnd, flags, ctx);
5803 }
5804}
5805
5806#if CONFIG_SEARCHFS
5807
5808#if 0
5809/*
5810*#
5811*#% searchfs vp L L L
5812*#
5813*/
5814struct vnop_searchfs_args {
5815 struct vnodeop_desc *a_desc;
5816 vnode_t a_vp;
5817 void *a_searchparams1;
5818 void *a_searchparams2;
5819 struct attrlist *a_searchattrs;
5820 uint32_t a_maxmatches;
5821 struct timeval *a_timelimit;
5822 struct attrlist *a_returnattrs;
5823 uint32_t *a_nummatches;
5824 uint32_t a_scriptcode;
5825 uint32_t a_options;
5826 struct uio *a_uio;
5827 struct searchstate *a_searchstate;
5828 vfs_context_t a_context;
5829};
5830
5831#endif /* 0*/
5832errno_t
5833VNOP_SEARCHFS(struct vnode *vp, void *searchparams1, void *searchparams2, struct attrlist *searchattrs, uint32_t maxmatches, struct timeval *timelimit, struct attrlist *returnattrs, uint32_t *nummatches, uint32_t scriptcode, uint32_t options, struct uio *uio, struct searchstate *searchstate, vfs_context_t ctx)
5834{
5835 int _err;
5836 struct vnop_searchfs_args a;
5837
5838 a.a_desc = &vnop_searchfs_desc;
5839 a.a_vp = vp;
5840 a.a_searchparams1 = searchparams1;
5841 a.a_searchparams2 = searchparams2;
5842 a.a_searchattrs = searchattrs;
5843 a.a_maxmatches = maxmatches;
5844 a.a_timelimit = timelimit;
5845 a.a_returnattrs = returnattrs;
5846 a.a_nummatches = nummatches;
5847 a.a_scriptcode = scriptcode;
5848 a.a_options = options;
5849 a.a_uio = uio;
5850 a.a_searchstate = searchstate;
5851 a.a_context = ctx;
5852
5853 _err = (*vp->v_op[vnop_searchfs_desc.vdesc_offset])(&a);
5854 DTRACE_FSINFO(searchfs, vnode_t, vp);
5855
5856 return _err;
5857}
5858#endif /* CONFIG_SEARCHFS */
5859
5860#if 0
5861/*
5862*#
5863*#% copyfile fvp U U U
5864*#% copyfile tdvp L U U
5865*#% copyfile tvp X U U
5866*#
5867*/
5868struct vnop_copyfile_args {
5869 struct vnodeop_desc *a_desc;
5870 vnode_t a_fvp;
5871 vnode_t a_tdvp;
5872 vnode_t a_tvp;
5873 struct componentname *a_tcnp;
5874 int a_mode;
5875 int a_flags;
5876 vfs_context_t a_context;
5877};
5878#endif /* 0*/
5879errno_t
5880VNOP_COPYFILE(struct vnode *fvp, struct vnode *tdvp, struct vnode *tvp, struct componentname *tcnp,
5881 int mode, int flags, vfs_context_t ctx)
5882{
5883 int _err;
5884 struct vnop_copyfile_args a;
5885 a.a_desc = &vnop_copyfile_desc;
5886 a.a_fvp = fvp;
5887 a.a_tdvp = tdvp;
5888 a.a_tvp = tvp;
5889 a.a_tcnp = tcnp;
5890 a.a_mode = mode;
5891 a.a_flags = flags;
5892 a.a_context = ctx;
5893 _err = (*fvp->v_op[vnop_copyfile_desc.vdesc_offset])(&a);
5894 DTRACE_FSINFO(copyfile, vnode_t, fvp);
5895 return _err;
5896}
5897
5898#if 0
5899struct vnop_clonefile_args {
5900 struct vnodeop_desc *a_desc;
5901 vnode_t a_fvp;
5902 vnode_t a_dvp;
5903 vnode_t *a_vpp;
5904 struct componentname *a_cnp;
5905 struct vnode_attr *a_vap;
5906 uint32_t a_flags;
5907 vfs_context_t a_context;
5908 int (*a_dir_clone_authorizer)( /* Authorization callback */
5909 struct vnode_attr *vap, /* attribute to be authorized */
5910 kauth_action_t action, /* action for which attribute is to be authorized */
5911 struct vnode_attr *dvap, /* target directory attributes */
5912 vnode_t sdvp, /* source directory vnode pointer (optional) */
5913 mount_t mp, /* mount point of filesystem */
5914 dir_clone_authorizer_op_t vattr_op, /* specific operation requested : setup, authorization or cleanup */
5915 uint32_t flags; /* value passed in a_flags to the VNOP */
5916 vfs_context_t ctx, /* As passed to VNOP */
5917 void *reserved); /* Always NULL */
5918 void *a_reserved; /* Currently unused */
5919};
5920#endif /* 0 */
5921
5922errno_t
5923VNOP_CLONEFILE(vnode_t fvp, vnode_t dvp, vnode_t *vpp,
5924 struct componentname *cnp, struct vnode_attr *vap, uint32_t flags,
5925 vfs_context_t ctx)
5926{
5927 int _err;
5928 struct vnop_clonefile_args a;
5929 a.a_desc = &vnop_clonefile_desc;
5930 a.a_fvp = fvp;
5931 a.a_dvp = dvp;
5932 a.a_vpp = vpp;
5933 a.a_cnp = cnp;
5934 a.a_vap = vap;
5935 a.a_flags = flags;
5936 a.a_context = ctx;
5937
5938 if (vnode_vtype(vp: fvp) == VDIR) {
5939 a.a_dir_clone_authorizer = vnode_attr_authorize_dir_clone;
5940 } else {
5941 a.a_dir_clone_authorizer = NULL;
5942 }
5943
5944 _err = (*dvp->v_op[vnop_clonefile_desc.vdesc_offset])(&a);
5945
5946 if (_err == 0 && *vpp) {
5947 DTRACE_FSINFO(clonefile, vnode_t, *vpp);
5948 if (kdebug_enable) {
5949 kdebug_lookup(dp: *vpp, cnp);
5950 }
5951 }
5952
5953 post_event_if_success(dvp, _err, NOTE_WRITE);
5954
5955 return _err;
5956}
5957
5958errno_t
5959VNOP_GETXATTR(vnode_t vp, const char *name, uio_t uio, size_t *size, int options, vfs_context_t ctx)
5960{
5961 struct vnop_getxattr_args a;
5962 int error;
5963
5964 a.a_desc = &vnop_getxattr_desc;
5965 a.a_vp = vp;
5966 a.a_name = name;
5967 a.a_uio = uio;
5968 a.a_size = size;
5969 a.a_options = options;
5970 a.a_context = ctx;
5971
5972 error = (*vp->v_op[vnop_getxattr_desc.vdesc_offset])(&a);
5973 DTRACE_FSINFO(getxattr, vnode_t, vp);
5974
5975 return error;
5976}
5977
5978errno_t
5979VNOP_SETXATTR(vnode_t vp, const char *name, uio_t uio, int options, vfs_context_t ctx)
5980{
5981 struct vnop_setxattr_args a;
5982 int error;
5983
5984 a.a_desc = &vnop_setxattr_desc;
5985 a.a_vp = vp;
5986 a.a_name = name;
5987 a.a_uio = uio;
5988 a.a_options = options;
5989 a.a_context = ctx;
5990
5991 error = (*vp->v_op[vnop_setxattr_desc.vdesc_offset])(&a);
5992 DTRACE_FSINFO(setxattr, vnode_t, vp);
5993
5994 if (error == 0) {
5995 vnode_uncache_authorized_action(vp, KAUTH_INVALIDATE_CACHED_RIGHTS);
5996 }
5997
5998 post_event_if_success(vp, error, NOTE_ATTRIB);
5999
6000 return error;
6001}
6002
6003errno_t
6004VNOP_REMOVEXATTR(vnode_t vp, const char *name, int options, vfs_context_t ctx)
6005{
6006 struct vnop_removexattr_args a;
6007 int error;
6008
6009 a.a_desc = &vnop_removexattr_desc;
6010 a.a_vp = vp;
6011 a.a_name = name;
6012 a.a_options = options;
6013 a.a_context = ctx;
6014
6015 error = (*vp->v_op[vnop_removexattr_desc.vdesc_offset])(&a);
6016 DTRACE_FSINFO(removexattr, vnode_t, vp);
6017
6018 post_event_if_success(vp, error, NOTE_ATTRIB);
6019
6020 return error;
6021}
6022
6023errno_t
6024VNOP_LISTXATTR(vnode_t vp, uio_t uio, size_t *size, int options, vfs_context_t ctx)
6025{
6026 struct vnop_listxattr_args a;
6027 int error;
6028
6029 a.a_desc = &vnop_listxattr_desc;
6030 a.a_vp = vp;
6031 a.a_uio = uio;
6032 a.a_size = size;
6033 a.a_options = options;
6034 a.a_context = ctx;
6035
6036 error = (*vp->v_op[vnop_listxattr_desc.vdesc_offset])(&a);
6037 DTRACE_FSINFO(listxattr, vnode_t, vp);
6038
6039 return error;
6040}
6041
6042
6043#if 0
6044/*
6045*#
6046*#% blktooff vp = = =
6047*#
6048*/
6049struct vnop_blktooff_args {
6050 struct vnodeop_desc *a_desc;
6051 vnode_t a_vp;
6052 daddr64_t a_lblkno;
6053 off_t *a_offset;
6054};
6055#endif /* 0*/
6056errno_t
6057VNOP_BLKTOOFF(struct vnode *vp, daddr64_t lblkno, off_t *offset)
6058{
6059 int _err;
6060 struct vnop_blktooff_args a;
6061
6062 a.a_desc = &vnop_blktooff_desc;
6063 a.a_vp = vp;
6064 a.a_lblkno = lblkno;
6065 a.a_offset = offset;
6066
6067 _err = (*vp->v_op[vnop_blktooff_desc.vdesc_offset])(&a);
6068 DTRACE_FSINFO(blktooff, vnode_t, vp);
6069
6070 return _err;
6071}
6072
6073#if 0
6074/*
6075*#
6076*#% offtoblk vp = = =
6077*#
6078*/
6079struct vnop_offtoblk_args {
6080 struct vnodeop_desc *a_desc;
6081 vnode_t a_vp;
6082 off_t a_offset;
6083 daddr64_t *a_lblkno;
6084};
6085#endif /* 0*/
6086errno_t
6087VNOP_OFFTOBLK(struct vnode *vp, off_t offset, daddr64_t *lblkno)
6088{
6089 int _err;
6090 struct vnop_offtoblk_args a;
6091
6092 a.a_desc = &vnop_offtoblk_desc;
6093 a.a_vp = vp;
6094 a.a_offset = offset;
6095 a.a_lblkno = lblkno;
6096
6097 _err = (*vp->v_op[vnop_offtoblk_desc.vdesc_offset])(&a);
6098 DTRACE_FSINFO(offtoblk, vnode_t, vp);
6099
6100 return _err;
6101}
6102
6103#if 0
6104/*
6105*#
6106*#% ap vp L L L
6107*#
6108*/
6109struct vnop_verify_args {
6110 struct vnodeop_desc *a_desc;
6111 vnode_t a_vp;
6112 off_t a_foffset;
6113 char *a_buf;
6114 size_t a_bufsize;
6115 size_t *a_verifyblksize;
6116 void **a_verify_ctxp;
6117 int a_flags;
6118 vfs_context_t a_context;
6119};
6120#endif
6121
6122errno_t
6123VNOP_VERIFY(struct vnode *vp, off_t foffset, uint8_t *buf, size_t bufsize,
6124 size_t *verify_block_size, void **verify_ctxp, vnode_verify_flags_t flags,
6125 vfs_context_t ctx)
6126{
6127 int _err;
6128 struct vnop_verify_args a;
6129
6130 if (ctx == NULL) {
6131 ctx = vfs_context_kernel();
6132 }
6133 a.a_desc = &vnop_verify_desc;
6134 a.a_vp = vp;
6135 a.a_foffset = foffset;
6136 a.a_buf = buf;
6137 a.a_bufsize = bufsize;
6138 a.a_verifyblksize = verify_block_size;
6139 a.a_flags = flags;
6140 a.a_verify_ctxp = verify_ctxp;
6141 a.a_context = ctx;
6142
6143 _err = (*vp->v_op[vnop_verify_desc.vdesc_offset])(&a);
6144 DTRACE_FSINFO(verify, vnode_t, vp);
6145
6146 /* It is not an error for a filesystem to not support this VNOP */
6147 if (_err == ENOTSUP) {
6148 if (!buf && verify_block_size) {
6149 *verify_block_size = 0;
6150 }
6151
6152 _err = 0;
6153 }
6154
6155 return _err;
6156}
6157
6158#if 0
6159/*
6160*#
6161*#% blockmap vp L L L
6162*#
6163*/
6164struct vnop_blockmap_args {
6165 struct vnodeop_desc *a_desc;
6166 vnode_t a_vp;
6167 off_t a_foffset;
6168 size_t a_size;
6169 daddr64_t *a_bpn;
6170 size_t *a_run;
6171 void *a_poff;
6172 int a_flags;
6173 vfs_context_t a_context;
6174};
6175#endif /* 0*/
6176errno_t
6177VNOP_BLOCKMAP(struct vnode *vp, off_t foffset, size_t size, daddr64_t *bpn, size_t *run, void *poff, int flags, vfs_context_t ctx)
6178{
6179 int _err;
6180 struct vnop_blockmap_args a;
6181 size_t localrun = 0;
6182
6183 if (ctx == NULL) {
6184 ctx = vfs_context_current();
6185 }
6186 a.a_desc = &vnop_blockmap_desc;
6187 a.a_vp = vp;
6188 a.a_foffset = foffset;
6189 a.a_size = size;
6190 a.a_bpn = bpn;
6191 a.a_run = &localrun;
6192 a.a_poff = poff;
6193 a.a_flags = flags;
6194 a.a_context = ctx;
6195
6196 _err = (*vp->v_op[vnop_blockmap_desc.vdesc_offset])(&a);
6197 DTRACE_FSINFO(blockmap, vnode_t, vp);
6198
6199 /*
6200 * We used a local variable to request information from the underlying
6201 * filesystem about the length of the I/O run in question. If
6202 * we get malformed output from the filesystem, we cap it to the length
6203 * requested, at most. Update 'run' on the way out.
6204 */
6205 if (_err == 0) {
6206 if (localrun > size) {
6207 localrun = size;
6208 }
6209
6210 if (run) {
6211 *run = localrun;
6212 }
6213 }
6214
6215 return _err;
6216}
6217
6218#if 0
6219struct vnop_strategy_args {
6220 struct vnodeop_desc *a_desc;
6221 struct buf *a_bp;
6222};
6223
6224#endif /* 0*/
6225errno_t
6226VNOP_STRATEGY(struct buf *bp)
6227{
6228 int _err;
6229 struct vnop_strategy_args a;
6230 vnode_t vp = buf_vnode(bp);
6231 a.a_desc = &vnop_strategy_desc;
6232 a.a_bp = bp;
6233 _err = (*vp->v_op[vnop_strategy_desc.vdesc_offset])(&a);
6234 DTRACE_FSINFO(strategy, vnode_t, vp);
6235 return _err;
6236}
6237
6238#if 0
6239struct vnop_bwrite_args {
6240 struct vnodeop_desc *a_desc;
6241 buf_t a_bp;
6242};
6243#endif /* 0*/
6244errno_t
6245VNOP_BWRITE(struct buf *bp)
6246{
6247 int _err;
6248 struct vnop_bwrite_args a;
6249 vnode_t vp = buf_vnode(bp);
6250 a.a_desc = &vnop_bwrite_desc;
6251 a.a_bp = bp;
6252 _err = (*vp->v_op[vnop_bwrite_desc.vdesc_offset])(&a);
6253 DTRACE_FSINFO(bwrite, vnode_t, vp);
6254 return _err;
6255}
6256
6257#if 0
6258struct vnop_kqfilt_add_args {
6259 struct vnodeop_desc *a_desc;
6260 struct vnode *a_vp;
6261 struct knote *a_kn;
6262 vfs_context_t a_context;
6263};
6264#endif
6265errno_t
6266VNOP_KQFILT_ADD(struct vnode *vp, struct knote *kn, vfs_context_t ctx)
6267{
6268 int _err;
6269 struct vnop_kqfilt_add_args a;
6270
6271 a.a_desc = VDESC(vnop_kqfilt_add);
6272 a.a_vp = vp;
6273 a.a_kn = kn;
6274 a.a_context = ctx;
6275
6276 _err = (*vp->v_op[vnop_kqfilt_add_desc.vdesc_offset])(&a);
6277 DTRACE_FSINFO(kqfilt_add, vnode_t, vp);
6278
6279 return _err;
6280}
6281
6282#if 0
6283struct vnop_kqfilt_remove_args {
6284 struct vnodeop_desc *a_desc;
6285 struct vnode *a_vp;
6286 uintptr_t a_ident;
6287 vfs_context_t a_context;
6288};
6289#endif
6290errno_t
6291VNOP_KQFILT_REMOVE(struct vnode *vp, uintptr_t ident, vfs_context_t ctx)
6292{
6293 int _err;
6294 struct vnop_kqfilt_remove_args a;
6295
6296 a.a_desc = VDESC(vnop_kqfilt_remove);
6297 a.a_vp = vp;
6298 a.a_ident = ident;
6299 a.a_context = ctx;
6300
6301 _err = (*vp->v_op[vnop_kqfilt_remove_desc.vdesc_offset])(&a);
6302 DTRACE_FSINFO(kqfilt_remove, vnode_t, vp);
6303
6304 return _err;
6305}
6306
6307errno_t
6308VNOP_MONITOR(vnode_t vp, uint32_t events, uint32_t flags, void *handle, vfs_context_t ctx)
6309{
6310 int _err;
6311 struct vnop_monitor_args a;
6312
6313 a.a_desc = VDESC(vnop_monitor);
6314 a.a_vp = vp;
6315 a.a_events = events;
6316 a.a_flags = flags;
6317 a.a_handle = handle;
6318 a.a_context = ctx;
6319
6320 _err = (*vp->v_op[vnop_monitor_desc.vdesc_offset])(&a);
6321 DTRACE_FSINFO(monitor, vnode_t, vp);
6322
6323 return _err;
6324}
6325
6326#if 0
6327struct vnop_setlabel_args {
6328 struct vnodeop_desc *a_desc;
6329 struct vnode *a_vp;
6330 struct label *a_vl;
6331 vfs_context_t a_context;
6332};
6333#endif
6334errno_t
6335VNOP_SETLABEL(struct vnode *vp, struct label *label, vfs_context_t ctx)
6336{
6337 int _err;
6338 struct vnop_setlabel_args a;
6339
6340 a.a_desc = VDESC(vnop_setlabel);
6341 a.a_vp = vp;
6342 a.a_vl = label;
6343 a.a_context = ctx;
6344
6345 _err = (*vp->v_op[vnop_setlabel_desc.vdesc_offset])(&a);
6346 DTRACE_FSINFO(setlabel, vnode_t, vp);
6347
6348 return _err;
6349}
6350
6351
6352#if NAMEDSTREAMS
6353/*
6354 * Get a named streamed
6355 */
6356errno_t
6357VNOP_GETNAMEDSTREAM(vnode_t vp, vnode_t *svpp, const char *name, enum nsoperation operation, int flags, vfs_context_t ctx)
6358{
6359 int _err;
6360 struct vnop_getnamedstream_args a;
6361
6362 a.a_desc = &vnop_getnamedstream_desc;
6363 a.a_vp = vp;
6364 a.a_svpp = svpp;
6365 a.a_name = name;
6366 a.a_operation = operation;
6367 a.a_flags = flags;
6368 a.a_context = ctx;
6369
6370 _err = (*vp->v_op[vnop_getnamedstream_desc.vdesc_offset])(&a);
6371 DTRACE_FSINFO(getnamedstream, vnode_t, vp);
6372 return _err;
6373}
6374
6375/*
6376 * Create a named streamed
6377 */
6378errno_t
6379VNOP_MAKENAMEDSTREAM(vnode_t vp, vnode_t *svpp, const char *name, int flags, vfs_context_t ctx)
6380{
6381 int _err;
6382 struct vnop_makenamedstream_args a;
6383
6384 a.a_desc = &vnop_makenamedstream_desc;
6385 a.a_vp = vp;
6386 a.a_svpp = svpp;
6387 a.a_name = name;
6388 a.a_flags = flags;
6389 a.a_context = ctx;
6390
6391 _err = (*vp->v_op[vnop_makenamedstream_desc.vdesc_offset])(&a);
6392 DTRACE_FSINFO(makenamedstream, vnode_t, vp);
6393 return _err;
6394}
6395
6396
6397/*
6398 * Remove a named streamed
6399 */
6400errno_t
6401VNOP_REMOVENAMEDSTREAM(vnode_t vp, vnode_t svp, const char *name, int flags, vfs_context_t ctx)
6402{
6403 int _err;
6404 struct vnop_removenamedstream_args a;
6405
6406 a.a_desc = &vnop_removenamedstream_desc;
6407 a.a_vp = vp;
6408 a.a_svp = svp;
6409 a.a_name = name;
6410 a.a_flags = flags;
6411 a.a_context = ctx;
6412
6413 _err = (*vp->v_op[vnop_removenamedstream_desc.vdesc_offset])(&a);
6414 DTRACE_FSINFO(removenamedstream, vnode_t, vp);
6415 return _err;
6416}
6417#endif
6418