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