1/*
2 * Copyright (c) 2000-2004 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28/*
29 * Copyright (c) 1998-1999 Apple Computer, Inc. All Rights Reserved.
30 *
31 *	Modification History:
32 *
33 *	02-Feb-2000	Clark Warner    Added copyfile to table
34 *	17-Aug-1999	Pat Dirks	New today.
35 */
36
37#include <mach/mach_types.h>
38#include <mach/machine/boolean.h>
39#include <sys/param.h>
40#include <sys/systm.h>
41#include <sys/kernel.h>
42#include <sys/file.h>
43#include <sys/stat.h>
44#include <sys/proc.h>
45#include <sys/kauth.h>
46#include <sys/conf.h>
47#include <sys/mount_internal.h>
48#include <sys/vnode_internal.h>
49#include <sys/malloc.h>
50#include <sys/dirent.h>
51#include <sys/namei.h>
52#include <sys/attr.h>
53#include <sys/uio_internal.h>
54
55#include <sys/vm.h>
56#include <sys/errno.h>
57#include <vfs/vfs_support.h>
58
59#include "synthfs.h"
60
61#define RWSUPPORT 0
62
63#if RWSUPPORT
64#error NOT PORTED FOR UBC
65#include <sys/ubc.h>
66#endif
67
68static int synthfs_remove_internal(struct vnode *dvp, struct vnode *vp,
69                                   struct componentname *cnp, vfs_context_t context);
70
71
72#define VOPFUNC int (*)(void *)
73
74/* Global vfs data structures for synthfs. */
75int (**synthfs_vnodeop_p) (void *);
76struct vnodeopv_entry_desc synthfs_vnodeop_entries[] = {
77    {&vnop_default_desc, (VOPFUNC)vn_default_error},
78    {&vnop_strategy_desc, (VOPFUNC)err_strategy},		/* strategy		- not supported  */
79    {&vnop_bwrite_desc, (VOPFUNC)err_bwrite},			/* bwrite		- not supported  */
80    {&vnop_lookup_desc, (VOPFUNC)synthfs_cached_lookup},	/* cached lookup */
81    {&vnop_create_desc, (VOPFUNC)synthfs_create},		/* create		- DEBUGGER */
82    {&vnop_whiteout_desc, (VOPFUNC)err_whiteout},		/* whiteout		- not supported  */
83    {&vnop_mknod_desc, (VOPFUNC)err_mknod},			/* mknod		- not supported  */
84    {&vnop_open_desc, (VOPFUNC)synthfs_open},			/* open			- DEBUGGER */
85    {&vnop_close_desc, (VOPFUNC)nop_close},			/* close		- NOP */
86    {&vnop_getattr_desc, (VOPFUNC)synthfs_getattr},		/* getattr */
87    {&vnop_setattr_desc, (VOPFUNC)synthfs_setattr},		/* setattr */
88    {&vnop_getattrlist_desc, (VOPFUNC)err_getattrlist},	/* getattrlist	- not supported  */
89    {&vnop_setattrlist_desc, (VOPFUNC)err_setattrlist},	/* setattrlist	- not supported  */
90    {&vnop_read_desc, (VOPFUNC)err_read},			/* read			- not supported  */
91    {&vnop_write_desc, (VOPFUNC)err_write},			/* write		- not supported  */
92    {&vnop_ioctl_desc, (VOPFUNC)err_ioctl},			/* ioctl		- not supported  */
93    {&vnop_select_desc, (VOPFUNC)synthfs_select},		/* select */
94    {&vnop_exchange_desc, (VOPFUNC)err_exchange},		/* exchange		- not supported  */
95    {&vnop_revoke_desc, (VOPFUNC)nop_revoke},			/* revoke		- NOP */
96    {&vnop_mmap_desc, (VOPFUNC)synthfs_mmap},			/* mmap			- DEBUGGER */
97    {&vnop_fsync_desc, (VOPFUNC)nop_fsync},			/* fsync		- NOP */
98    {&vnop_remove_desc, (VOPFUNC)synthfs_remove},		/* remove */
99    {&vnop_link_desc, (VOPFUNC)err_link},			/* link			- not supported  */
100    {&vnop_rename_desc, (VOPFUNC)synthfs_rename},		/* rename */
101    {&vnop_mkdir_desc, (VOPFUNC)synthfs_mkdir},			/* mkdir */
102    {&vnop_rmdir_desc, (VOPFUNC)synthfs_rmdir},			/* rmdir */
103    {&vnop_symlink_desc, (VOPFUNC)synthfs_symlink},		/* symlink */
104    {&vnop_readdir_desc, (VOPFUNC)synthfs_readdir},		/* readdir */
105    {&vnop_readdirattr_desc, (VOPFUNC)err_readdirattr},	/* readdirattr	- not supported  */
106    {&vnop_readlink_desc, (VOPFUNC)synthfs_readlink},		/* readlink */
107    {&vnop_inactive_desc, (VOPFUNC)synthfs_inactive},		/* inactive */
108    {&vnop_reclaim_desc, (VOPFUNC)synthfs_reclaim},		/* reclaim */
109    {&vnop_pathconf_desc, (VOPFUNC)synthfs_pathconf},		/* pathconf */
110    {&vnop_advlock_desc, (VOPFUNC)err_advlock},			/* advlock		- not supported  */
111    {&vnop_allocate_desc, (VOPFUNC)err_allocate},		/* allocate		- not supported  */
112	{&vnop_pagein_desc, (VOPFUNC)err_pagein},		/* pagein		- not supported  */
113	{&vnop_pageout_desc, (VOPFUNC)err_pageout},		/* pageout		- not supported  */
114	{&vnop_searchfs_desc, (VOPFUNC)err_searchfs},		/* searchfs		- not supported */
115	{&vnop_copyfile_desc, (VOPFUNC)err_copyfile},		/* copyfile - not supported */
116 	{ &vnop_blktooff_desc, (VOPFUNC)err_blktooff },		/* blktooff not supported */
117	{ &vnop_offtoblk_desc, (VOPFUNC)err_offtoblk },		/* offtoblk  not supported */
118	{ &vnop_blockmap_desc, (VOPFUNC)err_blockmap },		/* blockmap  not supported */
119   {(struct vnodeop_desc *) NULL, (int (*) ()) NULL}
120};
121
122/*
123 * Oh what a tangled web we weave.  This structure will be used by
124 * bsd/vfs/vfs_conf.c to actually do the initialization of synthfs_vnodeop_p
125 */
126struct vnodeopv_desc synthfs_vnodeop_opv_desc =
127{&synthfs_vnodeop_p, synthfs_vnodeop_entries};
128
129
130
131/*
132 * Create a regular file
133#% create	dvp	L U U
134#% create	vpp	- L -
135#
136 vnop_create {
137     IN WILLRELE struct vnode *dvp;
138     OUT struct vnode **vpp;
139     IN struct componentname *cnp;
140     IN struct vnode_attr *vap;
141
142     We are responsible for freeing the namei buffer, it is done in hfs_makenode(), unless there is
143	a previous error.
144
145*/
146
147int
148synthfs_create(ap)
149struct vnop_create_args /* {
150    struct vnode *a_dvp;
151    struct vnode **a_vpp;
152    struct componentname *a_cnp;
153    struct vnode_attr *a_vap;
154    vfs_context_t a_context;
155} */ *ap;
156{
157#if DEBUG
158	struct vnode *dvp = ap->a_dvp;
159	char debugmsg[255];
160
161	sprintf(debugmsg, "synthfs_create: attempt to create '%s' in '%s' ?!", ap->a_cnp->cn_nameptr, VTOS(dvp)->s_name);
162	Debugger(debugmsg);
163#endif
164
165	return err_create(ap);
166}
167
168
169
170/*
171 * Open called.
172#% open		vp	L L L
173#
174 vnop_open {
175     IN struct vnode *vp;
176     IN int mode;
177     IN vfs_context_t a_context;
178 */
179
180int
181synthfs_open(ap)
182struct vnop_open_args /* {
183    struct vnode *a_vp;
184    int  a_mode;
185    vfs_context_t a_context;
186} */ *ap;
187{
188	struct vnode *vp = ap->a_vp;
189
190	if (vp->v_type == VDIR) {
191	  return 0;
192	} else {
193#if DEBUG
194		struct synthfsnode *sp = VTOS(vp);
195		char debugmsg[255];
196
197		sprintf(debugmsg, "synthfs_open: attempt to open '/%s' ?!", sp->s_name);
198		Debugger(debugmsg);
199#endif
200	};
201
202	return 0;
203}
204
205
206
207/*
208 * Mmap a file
209 *
210 * NB Currently unsupported.
211# XXX - not used
212#
213 vnop_mmap {
214     IN struct vnode *vp;
215     IN int fflags;
216     IN kauth_cred_t cred;
217     IN struct proc *p;
218
219     */
220
221/* ARGSUSED */
222
223int
224synthfs_mmap(__unused struct vnop_mmap_args *ap)
225{
226    return EINVAL;
227}
228
229
230
231/*
232#% getattr	vp	= = =
233#
234 vnop_getattr {
235     IN struct vnode *vp;
236     IN struct vnode_attr *vap;
237     IN vfs_context_t context;
238
239*/
240int
241synthfs_getattr(ap)
242struct vnop_getattr_args /* {
243    struct vnode *a_vp;
244    struct vnode_attr *a_vap;
245    vfs_context_t a_context;
246} */ *ap;
247{
248	struct vnode *vp     = ap->a_vp;
249	struct vnode_attr *vap    = ap->a_vap;
250	struct synthfsnode *sp = VTOS(vp);
251
252	VATTR_RETURN(vap, va_type, vp->v_type);
253	VATTR_RETURN(vap, va_mode, sp->s_mode);
254	VATTR_RETURN(vap, va_nlink, sp->s_linkcount);
255	VATTR_RETURN(vap, va_uid, sp->s_uid);
256	VATTR_RETURN(vap, va_gid, sp->s_gid);
257	VATTR_RETURN(vap, va_fsid, VTOVFS(vp)->mnt_vfsstat.f_fsid.val[0]);
258	VATTR_RETURN(vap, va_fileid, sp->s_nodeid);
259	switch (vp->v_type) {
260	case VDIR:
261		VATTR_RETURN(vap, va_data_size, (sp->s_u.d.d_entrycount + 2) * sizeof(struct dirent));
262		break;
263
264	case VREG:
265	  	VATTR_RETURN(vap, va_data_size, sp->s_u.f.f_size);
266	  	break;
267
268	case VLNK:
269		VATTR_RETURN(vap, va_data_size, sp->s_u.s.s_length);
270		break;
271
272	default:
273		VATTR_RETURN(vap, va_data_size, 0);
274	};
275	VATTR_RETURN(vap, va_iosize, 512);
276	vap->va_access_time.tv_sec = sp->s_accesstime.tv_sec;
277	vap->va_access_time.tv_nsec = sp->s_accesstime.tv_usec * 1000;
278	VATTR_SET_SUPPORTED(vap, va_access_time);
279	vap->va_modify_time.tv_sec = sp->s_modificationtime.tv_sec;
280	vap->va_modify_time.tv_nsec = sp->s_modificationtime.tv_usec * 1000;
281	VATTR_SET_SUPPORTED(vap, va_modify_time);
282	vap->va_change_time.tv_sec = sp->s_changetime.tv_sec;
283	vap->va_change_time.tv_nsec = sp->s_changetime.tv_usec * 1000;
284	VATTR_SET_SUPPORTED(vap, va_change_time);
285	VATTR_RETURN(vap, va_gen, sp->s_generation);
286	VATTR_RETURN(vap, va_flags, sp->s_flags);
287	VATTR_RETURN(vap, va_rdev, sp->s_rdev);
288	VATTR_RETURN(vap, va_filerev, 0);
289	VATTR_RETURN(vap, va_acl, NULL);
290
291	return (0);
292}
293
294
295
296/*
297 * Change the mode on a file or directory.
298 * vnode vp must be locked on entry.
299 */
300int synthfs_chmod(struct vnode *vp, int mode, kauth_cred_t cred, struct proc *p)
301{
302    struct synthfsnode *sp = VTOS(vp);
303    int result;
304
305    sp->s_mode &= ~ALLPERMS;
306    sp->s_mode |= (mode & ALLPERMS);
307    sp->s_nodeflags |= IN_CHANGE;
308#if RWSUPPORT
309    if ((vp->v_flag & VTEXT) && (sp->s_mode & S_ISTXT) == 0) (void) vnode_uncache(vp);
310#endif
311
312    return 0;
313}
314
315
316
317/*
318 * Change the flags on a file or directory.
319 * vnode vp must be locked on entry.
320 */
321int synthfs_chflags(struct vnode *vp, u_long flags, kauth_cred_t cred, struct proc *p)
322{
323    struct synthfsnode *sp = VTOS(vp);
324
325    sp->s_flags = flags;
326    sp->s_nodeflags |= IN_CHANGE;
327
328    return 0;
329}
330
331
332
333/*
334 * Perform chown operation on vnode vp;
335 * vnode vp must be locked on entry.
336 */
337int synthfs_chown(struct vnode *vp, uid_t uid, gid_t gid, kauth_cred_t cred, struct proc *p)
338{
339    struct synthfsnode *sp = VTOS(vp);
340    uid_t ouid;
341    gid_t ogid;
342    int result = 0;
343    int is_member;
344
345    if (uid == (uid_t)VNOVAL) uid = sp->s_uid;
346    if (gid == (gid_t)VNOVAL) gid = sp->s_gid;
347
348    ogid = sp->s_gid;
349    ouid = sp->s_uid;
350
351    sp->s_gid = gid;
352    sp->s_uid = uid;
353
354    if (ouid != uid || ogid != gid) sp->s_nodeflags |= IN_CHANGE;
355    if (ouid != uid && suser(cred, NULL)) sp->s_mode &= ~S_ISUID;
356    if (ogid != gid && suser(cred, NULL)) sp->s_mode &= ~S_ISGID;
357
358    return 0;
359}
360
361
362
363/*
364 * Set attribute vnode op. called from several syscalls
365#% setattr      vp      L L L
366#
367 vnop_setattr {
368     IN struct vnode *vp;
369     IN struct vnode_attr *vap;
370     IN vfs_context_t context;
371     */
372
373int
374synthfs_setattr(ap)
375struct vnop_setattr_args /* {
376struct vnode *a_vp;
377struct vnode_attr *a_vap;
378vfs_context_t a_context;
379} */ *ap;
380{
381	struct vnode *vp = ap->a_vp;
382	struct synthfsnode *sp = VTOS(vp);
383	struct vnode_attr *vap = ap->a_vap;
384	kauth_cred_t cred = vfs_context_ucred(ap->a_context);
385	struct proc *p = vfs_context_proc(ap->a_context);
386	struct timeval atimeval, mtimeval;
387	uid_t nuid;
388	gid_t ngid;
389	int result;
390
391	result = 0;
392
393	if (VATTR_IS_ACTIVE(vap, va_flags)) {
394		if ((result = synthfs_chflags(vp, vap->va_flags, cred, p))) {
395			goto Err_Exit;
396		}
397	}
398	VATTR_SET_SUPPORTED(vap, va_flags);
399
400	nuid = (uid_t)ngid = (gid_t)VNOVAL;
401	if (VATTR_IS_ACTIVE(vap, va_uid))
402		nuid = vap->va_uid;
403	if (VATTR_IS_ACTIVE(vap, va_gid))
404		ngid = vap->va_gid;
405	if (nuid != (uid_t)VNOVAL || ngid != (gid_t)VNOVAL) {
406		if ((result = synthfs_chown(vp, nuid, ngid, cred, p))) {
407			goto Err_Exit;
408		}
409	}
410	VATTR_SET_SUPPORTED(vap, va_uid);
411	VATTR_SET_SUPPORTED(vap, va_gid);
412
413	if (VATTR_IS_ACTIVE(vap, va_data_size)) {
414#if RWSUPPORT
415		if ((result = vnode_setsize(vp, vap->va_data_size, 0, ap->a_context))) {
416			goto Err_Exit;
417		};
418		VATTR_SET_SUPPORTED(vap, va_data_size);
419#else
420		result = EINVAL;
421		goto Err_Exit;
422#endif
423	}
424
425	sp = VTOS(vp);
426	if (VATTR_IS_ACTIVE(vap, va_access_time) || VATTR_IS_ACTIVE(vap, va_modify_time)) {
427		if (VATTR_IS_ACTIVE(vap, va_access_time)) {
428			sp->s_nodeflags |= IN_ACCESS;
429			atimeval.tv_sec = vap->va_access_time.tv_sec;
430			atimeval.tv_usec = vap->va_access_time.tv_nsec / 1000;
431		}
432		if (VATTR_IS_ACTIVE(vap, va_modify_time)) {
433			sp->s_nodeflags |= IN_CHANGE | IN_UPDATE;
434			mtimeval.tv_sec = vap->va_modify_time.tv_sec;
435			mtimeval.tv_usec = vap->va_modify_time.tv_nsec / 1000;
436		}
437		if ((result = synthfs_update(vp, &atimeval, &mtimeval, 1))) {
438			goto Err_Exit;
439		}
440	}
441	VATTR_SET_SUPPORTED(vap, va_access_time);
442	VATTR_SET_SUPPORTED(vap, va_modify_time);
443
444	if (VATTR_IS_ACTIVE(vap, va_mode))
445		result = synthfs_chmod(vp, (int)vap->va_mode, cred, p);
446	VATTR_SET_SUPPORTED(vap, va_mode);
447
448	Err_Exit:
449
450	DBG_VOP(("synthfs_setattr: returning %d...\n", result));
451
452	return (result);
453}
454
455
456
457/*
458
459#% rename	sourcePar_vp	U U U
460#% rename	source_vp		U U U
461#% rename	targetPar_vp	L U U
462#% rename	target_vp		X U U
463#
464 vnop_rename {
465     IN WILLRELE struct vnode *sourcePar_vp;
466     IN WILLRELE struct vnode *source_vp;
467     IN struct componentname *source_cnp;
468     IN WILLRELE struct vnode *targetPar_vp;
469     IN WILLRELE struct vnode *target_vp;
470     IN struct componentname *target_cnp;
471
472
473 */
474
475/*
476 * On entry:
477 *	source's parent directory is unlocked
478 *	source file or directory is unlocked
479 *	destination's parent directory is locked
480 *	destination file or directory is locked if it exists
481 *
482 * On exit:
483 *	all denodes should be released
484 *
485 */
486
487int
488synthfs_rename(ap)
489struct vnop_rename_args  /* {
490    struct vnode *a_fdvp;
491    struct vnode *a_fvp;
492    struct componentname *a_fcnp;
493    struct vnode *a_tdvp;
494    struct vnode *a_tvp;
495    struct componentname *a_tcnp;
496    vfs_context_t a_context;
497} */ *ap;
498{
499	struct vnode			*target_vp = ap->a_tvp;
500	struct vnode			*targetPar_vp = ap->a_tdvp;
501	struct vnode			*source_vp = ap->a_fvp;
502	struct vnode			*sourcePar_vp = ap->a_fdvp;
503	struct componentname	*target_cnp = ap->a_tcnp;
504	struct componentname	*source_cnp = ap->a_fcnp;
505	struct synthfsnode		*target_sp, *targetPar_sp, *source_sp, *sourcePar_sp;
506	u_short					doingdirectory = 0, oldparent = 0, newparent = 0;
507	int						retval = 0;
508	struct timeval			tv;
509
510#if SYNTHFS_DIAGNOSTIC
511    if ((target_cnp->cn_flags & HASBUF) == 0 ||
512        (source_cnp->cn_flags & HASBUF) == 0)
513        panic("synthfs_rename: no name");
514#endif
515
516	DBG_ASSERT((ap->a_fdvp->v_type == VDIR) && (ap->a_tdvp->v_type == VDIR));
517	target_sp = targetPar_sp = source_sp = sourcePar_sp = NULL;
518
519
520	sourcePar_sp = VTOS(sourcePar_vp);
521	source_sp = VTOS(source_vp);
522	oldparent = sourcePar_sp->s_nodeid;
523
524	/*
525	 * Be sure we are not renaming ".", "..", or an alias of ".". This
526	 * leads to a crippled directory tree.	It's pretty tough to do a
527	 * "ls" or "pwd" with the "." directory entry missing, and "cd .."
528	 * doesn't work if the ".." entry is missing.
529	 */
530	if (source_sp->s_type == SYNTHFS_DIRECTORY) {
531		if ((source_cnp->cn_namelen == 1 && source_cnp->cn_nameptr[0] == '.')
532			|| sourcePar_sp == source_sp
533			|| (source_cnp->cn_flags & ISDOTDOT)
534			|| (source_sp->s_nodeflags & IN_RENAME)) {
535			retval = EINVAL;
536			goto abortit;
537		}
538		source_sp->s_nodeflags |= IN_RENAME;
539		doingdirectory = TRUE;
540	}
541
542	/* Transit between abort and bad */
543
544    targetPar_sp = VTOS(targetPar_vp);
545    target_sp = target_vp ? VTOS(target_vp) : NULL;
546    newparent = targetPar_sp->s_nodeid;
547
548
549	/*
550	 * If the destination exists, then be sure its type (file or dir)
551	 * matches that of the source.	And, if it is a directory make sure
552	 * it is empty.	 Then delete the destination.
553	 */
554	if (target_vp) {
555
556#if RWSUPPORT
557		if (target_vp->v_type == VREG) {
558			(void) vnode_uncache(target_vp);
559		};
560#endif
561		cache_purge(target_vp);
562
563		retval = synthfs_remove_internal(targetPar_vp, target_vp, target_cnp, ap->a_context);
564
565		target_vp = NULL;
566		target_sp = NULL;
567
568		if (retval) goto bad;
569	};
570
571
572	/* remove the existing entry from the namei cache: */
573	if (source_vp->v_type == VREG) cache_purge(source_vp);
574
575	retval = synthfs_move_rename_entry( source_vp, targetPar_vp, target_cnp->cn_nameptr);
576
577	if (retval) goto bad;
578
579	source_sp->s_nodeflags &= ~IN_RENAME;
580
581	/*
582	 * Timestamp both parent directories.
583	 * Note that if this is a rename within the same directory,
584	 * (where targetPar_hp == sourcePar_hp)
585	 * the code below is still safe and correct.
586	 */
587	targetPar_sp->s_nodeflags |= IN_UPDATE;
588	sourcePar_sp->s_nodeflags |= IN_UPDATE;
589
590	microtime(&tv);
591	SYNTHFSTIMES(targetPar_sp, &tv, &tv);
592	SYNTHFSTIMES(sourcePar_sp, &tv, &tv);
593
594	return (retval);
595
596bad:;
597	if (retval && doingdirectory)
598		source_sp->s_nodeflags &= ~IN_RENAME;
599
600	return (retval);
601
602abortit:;
603	return (retval);
604}
605
606
607
608/*
609 * Mkdir system call
610
611#% mkdir	dvp	L U U
612#% mkdir	vpp	- L -
613#
614 vnop_mkdir {
615     IN WILLRELE struct vnode *dvp;
616     OUT struct vnode **vpp;
617     IN struct componentname *cnp;
618     IN struct vnode_attr *vap;
619     IN vfs_context_t context;
620
621     We are responsible for freeing the namei buffer, it is done in synthfs_makenode(), unless there is
622    a previous error.
623
624*/
625
626int
627synthfs_mkdir(ap)
628struct vnop_mkdir_args /* {
629    struct vnode *a_dvp;
630    struct vnode **a_vpp;
631    struct componentname *a_cnp;
632    struct vnode_attr *a_vap;
633    vfs_context_t a_context;
634} */ *ap;
635{
636	int retval;
637	struct vnode *dvp = ap->a_dvp;
638	struct componentname *cnp = ap->a_cnp;
639	int mode = MAKEIMODE(ap->a_vap->va_type, ap->a_vap->va_mode);
640	struct vnode *vp = NULL;
641
642	*ap->a_vpp = NULL;
643
644	retval = synthfs_new_directory(VTOVFS(dvp), dvp, cnp->cn_nameptr, VTOSFS(dvp)->synthfs_nextid++, mode, vfs_context_proc(cnp->cn_context), &vp);
645	if (retval) goto Error_Exit;
646
647	*ap->a_vpp = vp;
648
649	retval = vnode_setattr(vp, ap->a_vap, ap->a_context);
650	if (retval != 0) goto Error_Exit;
651
652	Error_Exit:;
653	if (retval != 0) {
654		if (vp) synthfs_remove_directory(vp);
655	}
656
657	return retval;
658}
659
660
661
662/*
663
664#% remove	dvp	L U U
665#% remove	vp	L U U
666#
667 vnop_remove {
668     IN WILLRELE struct vnode *dvp;
669     IN WILLRELE struct vnode *vp;
670     IN struct componentname *cnp;
671     IN vfs_context_t context;
672
673     */
674
675int
676synthfs_remove(ap)
677struct vnop_remove_args /* {
678    struct vnode *a_dvp;
679    struct vnode *a_vp;
680    struct componentname *a_cnp;
681    vfs_context_t a_context;
682} */ *ap;
683{
684	return synthfs_remove_internal(ap->a_dvp, ap->a_vp, ap->a_cnp, ap->a_context);
685}
686
687static int
688synthfs_remove_internal(struct vnode *dvp, struct vnode *vp,
689			__unused struct componentname *cnp,
690			__unused vfs_context_t context)
691{
692	struct synthfsnode *sp = VTOS(vp);
693	struct timeval tv;
694	int retval = 0;
695
696	/* This is sort of silly right now but someday it may make sense... */
697	if (sp->s_nodeflags & IN_MODIFIED) {
698		microtime(&tv);
699		synthfs_update(vp, &tv, &tv, 0);
700	};
701
702	/* remove the entry from the namei cache: */
703	cache_purge(vp);
704
705	/* remove entry from tree and reclaim any resources consumed: */
706	switch (sp->s_type) {
707		case SYNTHFS_DIRECTORY:
708			synthfs_remove_directory(vp);
709			break;
710
711
712		case SYNTHFS_SYMLINK:
713			synthfs_remove_symlink(vp);
714			break;
715
716		case SYNTHFS_FILE:
717			/* Fall through to default case */
718
719		default:
720			synthfs_remove_entry(vp);
721	};
722
723out:
724
725	if (! retval)
726		VTOS(dvp)->s_nodeflags |= IN_CHANGE | IN_UPDATE;
727
728	return (retval);
729}
730
731
732
733/*
734#% rmdir	dvp	L U U
735#% rmdir	vp	L U U
736#
737 vnop_rmdir {
738     IN WILLRELE struct vnode *dvp;
739     IN WILLRELE struct vnode *vp;
740     IN struct componentname *cnp;
741     IN vfs_context_t context;
742
743     */
744
745int
746synthfs_rmdir(ap)
747    struct vnop_rmdir_args /* {
748    	struct vnode *a_dvp;
749    	struct vnode *a_vp;
750        struct componentname *a_cnp;
751	vfs_context_t a_context;
752} */ *ap;
753{
754	return synthfs_remove((struct vnop_remove_args *)ap);
755}
756
757
758
759/*
760 * synthfs_select - just say OK.  Only possible op is readdir
761 *
762 * Locking policy: ignore
763 */
764int
765synthfs_select(__unused
766struct vnop_select_args /* {
767    struct vnode *a_vp;
768    int  a_which;
769    int  a_fflags;
770    kauth_cred_t a_cred;
771	void *a_wql;
772    struct proc *a_p;
773} */ *ap)
774{
775    DBG_VOP(("synthfs_select called\n"));
776
777    return (1);
778}
779
780/*
781#
782#% symlink	dvp	L U U
783#% symlink	vpp	- U -
784#
785# XXX - note that the return vnode has already been vnode_put'ed
786#	by the filesystem layer.  To use it you must use vnode_get,
787#	possibly with a further namei.
788#
789 vnop_symlink {
790     IN WILLRELE struct vnode *dvp;
791     OUT WILLRELE struct vnode **vpp;
792     IN struct componentname *cnp;
793     IN struct vnode_attr *vap;
794     IN char *target;
795
796     We are responsible for freeing the namei buffer, it is done in synthfs_makenode(), unless there is
797    a previous error.
798
799
800*/
801
802int
803synthfs_symlink(ap)
804    struct vnop_symlink_args /* {
805        struct vnode *a_dvp;
806        struct vnode **a_vpp;
807        struct componentname *a_cnp;
808        struct vnode_attr *a_vap;
809        char *a_target;
810	vfs_context_t a_context;
811    } */ *ap;
812{
813    struct vnode *dvp = ap->a_dvp;
814    struct vnode **vpp = ap->a_vpp;
815    struct componentname *cnp = ap->a_cnp;
816    int retval;
817
818    *vpp = NULL;
819
820    retval = synthfs_new_symlink(VTOVFS(dvp), dvp, cnp->cn_nameptr, VTOSFS(dvp)->synthfs_nextid++, ap->a_target, vfs_context_proc(cnp->cn_context), vpp);
821
822    return (retval);
823}
824
825
826
827/*
828#
829#% readlink	vp	L L L
830#
831 vnop_readlink {
832     IN struct vnode *vp;
833     INOUT struct uio *uio;
834     IN kauth_cred_t cred;
835     */
836
837int
838synthfs_readlink(ap)
839struct vnop_readlink_args /* {
840	struct vnode *a_vp;
841	struct uio *a_uio;
842	vfs_context_t a_context;
843} */ *ap;
844{
845	struct vnode *vp = ap->a_vp;
846	struct synthfsnode *sp = VTOS(vp);
847    struct uio *uio = ap->a_uio;
848    int retval;
849    unsigned long count;
850
851    if (ap->a_uio->uio_offset > sp->s_u.s.s_length) {
852        return 0;
853    };
854
855	// LP64todo - fix this!
856    if (uio->uio_offset + uio_resid(uio) <= sp->s_u.s.s_length) {
857    	count = uio_resid(uio);
858    } else {
859    	count = sp->s_u.s.s_length - uio->uio_offset;
860    };
861    retval = uiomove((void *)((unsigned char *)sp->s_u.s.s_symlinktarget + uio->uio_offset), count, uio);
862    return (retval);
863
864}
865
866
867
868
869
870
871/*
872 * Read directory entries.
873 */
874int
875synthfs_readdir(ap)
876struct vnop_readdir_args /* {
877	struct vnode *a_vp;
878	struct uio *a_uio;
879	int a_flags;
880	int *a_eofflag;
881	int *a_numdirent;
882	vfs_context_t a_context;
883} */ *ap;
884{
885    struct synthfsnode *sp = VTOS(ap->a_vp);
886    register struct uio *uio = ap->a_uio;
887    off_t diroffset;						/* Offset into simulated directory file */
888    struct synthfsnode *entry;
889
890    DBG_VOP(("\tuio_offset = %d, uio_resid = %lld\n", (int) uio->uio_offset, uio_resid(uio)));
891
892	if (ap->a_flags & (VNODE_READDIR_EXTENDED | VNODE_READDIR_REQSEEKOFF))
893		return (EINVAL);
894
895	/* We assume it's all one big buffer... */
896    if (uio->uio_iovcnt > 1) {
897    	DBG_VOP(("\tuio->uio_iovcnt = %d?\n", uio->uio_iovcnt));
898    	return EINVAL;
899    };
900
901 	diroffset = 0;
902
903    /*
904     * We must synthesize . and ..
905     */
906    DBG_VOP(("\tstarting ... uio_offset = %d, uio_resid = %lld\n", (int) uio->uio_offset, uio_resid(uio)));
907    if (uio->uio_offset == diroffset)
908      {
909        DBG_VOP(("\tAdding .\n"));
910		diroffset += synthfs_adddirentry(sp->s_nodeid, DT_DIR, ".", uio);
911        DBG_VOP(("\t   after adding ., uio_offset = %d, uio_resid = %lld\n", (int) uio->uio_offset, uio_resid(uio)));
912      }
913    if ((uio_resid(uio) > 0) && (diroffset > uio->uio_offset)) {
914    	/* Oops - we skipped over a partial entry: at best, diroffset should've just matched uio->uio_offset */
915		return EINVAL;
916	};
917
918    if (uio->uio_offset == diroffset)
919      {
920        DBG_VOP(("\tAdding ..\n"));
921        if (sp->s_parent != NULL) {
922            diroffset += synthfs_adddirentry(sp->s_parent->s_nodeid, DT_DIR, "..", uio);
923        } else {
924            diroffset += synthfs_adddirentry(sp->s_nodeid, DT_DIR, "..", uio);
925        }
926        DBG_VOP(("\t   after adding .., uio_offset = %d, uio_resid = %lld\n", (int) uio->uio_offset, uio_resid(uio)));
927      }
928    if ((uio_resid(uio) > 0) && (diroffset > uio->uio_offset)) {
929    	/* Oops - we skipped over a partial entry: at best, diroffset should've just matched uio->uio_offset */
930		return EINVAL;
931	};
932
933	/* OK, so much for the fakes.  Now for the "real thing": */
934	TAILQ_FOREACH(entry, &sp->s_u.d.d_subnodes, s_sibling) {
935		if (diroffset == uio->uio_offset) {
936			/* Return this entry */
937			diroffset += synthfs_adddirentry(entry->s_nodeid, VTTOIF(STOV(entry)->v_type), entry->s_name, uio);
938		};
939    	if ((uio_resid(uio) > 0) && (diroffset > uio->uio_offset)) {
940	    	/* Oops - we skipped over a partial entry: at best, diroffset should've just matched uio->uio_offset */
941			return EINVAL;
942		};
943	};
944
945    if (ap->a_eofflag)
946        *ap->a_eofflag = (entry == NULL);		/* If we ran all the way through the list, there is no more */
947
948    return 0;
949}
950
951
952
953/*
954
955#% lookup	dvp L ? ?
956#% lookup	vpp - L -
957
958 */
959
960int
961synthfs_cached_lookup(ap)
962	struct vnop_lookup_args /* {
963		struct vnode *a_dvp;
964		struct vnode **a_vpp;
965		struct componentname *a_cnp;
966	} */ *ap;
967{
968    struct vnode *dp = ap->a_dvp;
969    struct componentname *cnp = ap->a_cnp;
970    u_long nameiop = cnp->cn_nameiop;
971    u_long flags = cnp->cn_flags;
972    struct vnode **vpp = ap->a_vpp;
973    int result = 0;
974
975    DBG_VOP(("synthfs_cached_lookup called, name = %s, namelen = %ld\n", ap->a_cnp->cn_nameptr, ap->a_cnp->cn_namelen));
976#if DEBUG
977    if (flags & ISLASTCN) DBG_VOP(("\tISLASTCN is set\n"));
978#endif
979
980    *vpp = NULL;
981
982	/*
983	 * Look up an entry in the namei cache
984	 */
985
986	result = cache_lookup(dp, vpp, cnp);
987	if (result == 0) {
988		/* There was no entry in the cache for this parent vnode/name pair:
989		   do the full-blown pathname lookup
990		 */
991		return synthfs_lookup(ap);
992	};
993	if (result == ENOENT) return result;
994
995	/* An entry matching the parent vnode/name was found in the cache: */
996
997	return (0);
998
999Err_Exit:;
1000	return result;
1001}
1002
1003
1004
1005int
1006synthfs_lookup(ap)
1007	struct vnop_lookup_args /* {
1008		struct vnode *a_dvp;
1009		struct vnode **a_vpp;
1010		struct componentname *a_cnp;
1011		vfs_context_t a_context;
1012	} */ *ap;
1013{
1014    struct vnode *dp = ap->a_dvp;
1015    struct synthfsnode *dsp = VTOS(dp);
1016    struct componentname *cnp = ap->a_cnp;
1017    u_long nameiop = cnp->cn_nameiop;
1018//  char *nameptr = cnp->cn_nameptr;
1019    u_long flags = cnp->cn_flags;
1020    long namelen = cnp->cn_namelen;
1021//  struct proc *p = cnp->cn_proc;
1022    vfs_context_t ctx = cnp->cn_context;
1023    kauth_cred_t cred = vfs_context_ucred(ctx);
1024    struct synthfsnode *entry;
1025    struct vnode *target_vp = NULL;
1026    int result = 0;
1027    boolean_t found = FALSE;
1028    boolean_t isDot = FALSE;
1029    boolean_t isDotDot = FALSE;
1030	struct vnode *starting_parent = dp;
1031
1032    DBG_VOP(("synthfs_lookup called, name = %s, namelen = %ld\n", ap->a_cnp->cn_nameptr, ap->a_cnp->cn_namelen));
1033#if DEBUG
1034    if (flags & LOCKPARENT) DBG_VOP(("\tLOCKPARENT is set\n"));
1035    if (flags & ISLASTCN) DBG_VOP(("\tISLASTCN is set\n"));
1036#endif
1037
1038    *ap->a_vpp = NULL;
1039
1040	/* first check for "." and ".." */
1041	if (cnp->cn_nameptr[0] == '.') {
1042		if (namelen == 1) {
1043			/*
1044			   "." requested
1045			 */
1046            isDot = TRUE;
1047            found = TRUE;
1048
1049            target_vp = dp;
1050            vnode_get(target_vp);
1051
1052            result = 0;
1053
1054            goto Std_Exit;
1055        } else if ((namelen == 2) && (cnp->cn_nameptr[1] == '.')) {
1056			/*
1057			   ".." requested
1058			 */
1059            isDotDot = TRUE;
1060            found = TRUE;
1061
1062            if ((dsp->s_parent != NULL) && (dsp->s_parent != VTOS(dp))) {
1063                target_vp = STOV(dsp->s_parent);
1064                /*
1065                 * Special case for ".." to prevent deadlock:
1066                 * always release the parent vnode BEFORE trying to acquire
1067                 * ITS parent.  This avoids deadlocking with another lookup
1068                 * starting from the target_vp trying to vnode_get() this directory.
1069                 */
1070                result = vnode_get(target_vp);
1071
1072            } else {
1073                target_vp = dp;
1074                /* dp is alread locked and ref'ed */
1075                result = 0;
1076            }
1077
1078            goto Std_Exit;
1079		}
1080	}
1081
1082	/* finally, just look for entries by name (making sure the entry's length
1083	   matches the cnp's namelen... */
1084    TAILQ_FOREACH(entry, &dsp->s_u.d.d_subnodes, s_sibling) {
1085    	if ((bcmp(cnp->cn_nameptr, entry->s_name, (unsigned)namelen) == 0) &&
1086    		(*(entry->s_name + namelen) == (char)0)) {
1087            found = TRUE;
1088			target_vp = STOV(entry);
1089            result = vnode_getwithref(target_vp);		/* refcount is always > 0 for any vnode in this list... */
1090			if (result != 0) {
1091				goto Err_Exit;
1092			};
1093
1094            /* The specified entry was found and successfully acquired: */
1095			goto Std_Exit;
1096        };
1097	};
1098
1099    found = FALSE;
1100
1101Std_Exit:;
1102    if (found) {
1103        if ((nameiop == DELETE) && (flags & ISLASTCN)) {
1104
1105            /*
1106             * If the parent directory is "sticky" then the user must own
1107             * the directory, or the file in it, in order to be allowed to
1108             * delete it (unless the user is root).  This implements
1109             * append-only directories
1110             */
1111            if ((dsp->s_mode & S_ISVTX) &&
1112                suser(cred, NULL) &&
1113                (kauth_cred_getuid(cred) != dsp->s_uid) &&
1114                (target_vp != NULL) &&
1115                (target_vp->v_type != VLNK) &&
1116                (VTOS(target_vp)->s_uid != kauth_cred_getuid(cred))) {
1117                vnode_put(target_vp);
1118                result = EPERM;
1119                goto Err_Exit;
1120            };
1121        };
1122
1123        if ((nameiop == RENAME) && (flags & WANTPARENT) && (flags * ISLASTCN)) {
1124
1125            if (isDot) {
1126                vnode_put(target_vp);
1127                result = EISDIR;
1128                goto Err_Exit;
1129            };
1130        };
1131    } else {
1132        /* The specified entry wasn't found: */
1133        result = ENOENT;
1134
1135        if ((flags & ISLASTCN) &&
1136            ((nameiop == CREATE) ||
1137             (nameiop == RENAME) ||
1138             ((nameiop == DELETE) && (flags & DOWHITEOUT) && (flags & ISWHITEOUT)))) {
1139		/* create a new entry */
1140            result = EJUSTRETURN;
1141        }
1142    };
1143
1144    *ap->a_vpp = target_vp;
1145
1146Err_Exit:;
1147	DBG_VOP(("synthfs_lookup: result = %d.\n", result));
1148	if (found) {
1149		if (target_vp) {
1150				DBG_VOP(("synthfs_lookup: target_vp = 0x%08X \n", (u_long)target_vp));
1151		} else {
1152			DBG_VOP(("synthfs_lookup: found = true but target_vp = NULL?\n"));
1153		};
1154	} else {
1155		DBG_VOP(("synthf_lookup: target not found.\n"));
1156	};
1157		DBG_VOP(("synthfs_lookup: dp = %08X; starting_parent = 0x%08X .\n", (u_long)dp, (u_long)starting_parent));
1158
1159   return result;
1160}
1161
1162
1163
1164/*
1165
1166#% pathconf	vp	L L L
1167#
1168 vnop_pathconf {
1169     IN struct vnode *vp;
1170     IN int name;
1171     OUT register_t *retval;
1172*/
1173int
1174synthfs_pathconf(ap)
1175struct vnop_pathconf_args /* {
1176    struct vnode *a_vp;
1177    int a_name;
1178    int *a_retval;
1179    vfs_context_t a_context;
1180} */ *ap;
1181{
1182    DBG_VOP(("synthfs_pathconf called\n"));
1183
1184    switch (ap->a_name)
1185      {
1186        case _PC_LINK_MAX:
1187            *ap->a_retval = LINK_MAX;
1188            return (0);
1189        case _PC_NAME_MAX:
1190            *ap->a_retval = NAME_MAX;
1191            return (0);
1192        case _PC_PATH_MAX:
1193            *ap->a_retval = PATH_MAX;
1194            return (0);
1195        case _PC_PIPE_BUF:
1196            *ap->a_retval = PIPE_BUF;
1197            return (0);
1198        case _PC_CHOWN_RESTRICTED:
1199	    *ap->a_retval = 200112;		/* _POSIX_CHOWN_RESTRICTED */
1200            return (0);
1201        case _PC_NO_TRUNC:
1202            *ap->a_retval = 200112;		/* _POSIX_NO_TRUNC */
1203            return (0);
1204        default:
1205            return (EINVAL);
1206      }
1207    /* NOTREACHED */
1208}
1209
1210
1211/*
1212 * Update the access, modified, and node change times as specified by the
1213 * IACCESS, IUPDATE, and ICHANGE flags respectively. The IMODIFIED flag is
1214 * used to specify that the node needs to be updated but that the times have
1215 * already been set. The access and modified times are taken from the second
1216 * and third parameters; the node change time is always taken from the current
1217 * time. If waitfor is set, then wait for the disk write of the node to
1218 * complete.
1219 */
1220
1221int
1222synthfs_update(struct vnode *vp, struct timeval *access, struct timeval *modify, __unused int waitfor)
1223{
1224	struct synthfsnode *sp = VTOS(vp);
1225	struct timeval tv;
1226
1227	DBG_ASSERT(sp != NULL);
1228
1229	if (((sp->s_nodeflags & (IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE)) != 0) &&
1230		!(VTOVFS(vp)->mnt_flag & MNT_RDONLY)) {
1231		if (sp->s_nodeflags & IN_ACCESS) sp->s_accesstime = *access;
1232		if (sp->s_nodeflags & IN_UPDATE) sp->s_modificationtime = *modify;
1233		if (sp->s_nodeflags & IN_CHANGE) {
1234
1235			microtime(&tv);
1236			sp->s_changetime = tv;
1237		}
1238	};
1239
1240	/* After the updates are finished, clear the flags */
1241	sp->s_nodeflags &= ~(IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE);
1242
1243	return 0;
1244}
1245
1246
1247
1248/*******************************************************************************************
1249
1250	Utility/housekeeping vnode operations:
1251
1252 ******************************************************************************************/
1253
1254
1255/*
1256#
1257#% inactive	vp	L U U
1258#
1259 vnop_inactive {
1260     IN struct vnode *vp;
1261     IN struct proc *p;
1262
1263*/
1264
1265int
1266synthfs_inactive(ap)
1267struct vnop_inactive_args /* {
1268    struct vnode *a_vp;
1269    vfs_context_t a_context;
1270} */ *ap;
1271{
1272	struct vnode *vp = ap->a_vp;
1273	struct synthfsnode *sp = VTOS(vp);
1274	struct timeval tv;
1275
1276#if DEBUG
1277	if (vp->v_usecount != 0)
1278        DBG_VOP(("synthfs_inactive: bad usecount = %d\n", vp->v_usecount ));
1279#endif
1280
1281	/*
1282	 * Ignore nodes related to stale file handles.
1283	 */
1284	if (vp->v_type == VNON)
1285		goto out;
1286
1287	/* This is sort of silly but might make sense in the future: */
1288	if (sp->s_nodeflags & (IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE)) {
1289		microtime(&tv);
1290		synthfs_update(vp, &tv, &tv, 0);
1291	}
1292
1293out:
1294	/*
1295	 * If we are done with the inode, reclaim it
1296	 * so that it can be reused immediately.
1297	 */
1298	if (vp->v_type == VNON) {
1299		vnode_recycle(vp);
1300	};
1301
1302	return 0;
1303}
1304
1305
1306
1307/*
1308 * synthfs_reclaim - Reclaim a vnode so that it can be used for other purposes.
1309 *
1310 * Locking policy: ignored
1311 */
1312int
1313synthfs_reclaim(ap)
1314    struct vnop_reclaim_args /* { struct vnode *a_vp; struct proc *a_p; } */ *ap;
1315{
1316    struct vnode *vp = ap->a_vp;
1317    struct synthfsnode *sp = VTOS(vp);
1318    void *name = sp->s_name;
1319
1320    sp->s_name = NULL;
1321    FREE(name, M_TEMP);
1322
1323	vp->v_data = NULL;
1324    FREE((void *)sp, M_SYNTHFS);
1325
1326    return (0);
1327}
1328