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