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