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