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