darwin-xnu/bsd/miscfs/synthfs/synthfs_vnops.c
<<
>>
Prefs
   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
  62static 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. */
  69int (**synthfs_vnodeop_p) (void *);
  70struct 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 */
 121struct 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
 142int
 143synthfs_create(ap)
 144struct 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
 175int
 176synthfs_open(ap)
 177struct 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
 218int
 219synthfs_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*/
 235int
 236synthfs_getattr(ap)
 237struct 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 */
 295int 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 */
 316int 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 */
 332int 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
 368int
 369synthfs_setattr(ap)
 370struct vnop_setattr_args /* {
 371struct vnode *a_vp;
 372struct vnode_attr *a_vap;
 373vfs_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
 482int
 483synthfs_rename(ap)
 484struct 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
 591bad:;
 592        if (retval && doingdirectory)
 593                source_sp->s_nodeflags &= ~IN_RENAME;
 594
 595        return (retval);
 596
 597abortit:;
 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
 621int
 622synthfs_mkdir(ap)
 623struct 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
 670int
 671synthfs_remove(ap)
 672struct 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
 682static int
 683synthfs_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
 718out:
 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
 740int
 741synthfs_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 */
 759int
 760synthfs_select(__unused
 761struct 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
 797int
 798synthfs_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
 832int
 833synthfs_readlink(ap)
 834struct 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 */
 869int
 870synthfs_readdir(ap)
 871struct 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
 955int
 956synthfs_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        
 994Err_Exit:;
 995        return result;
 996}
 997
 998
 999
1000int
1001synthfs_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
1096Std_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
1141Err_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*/
1168int
1169synthfs_pathconf(ap)
1170struct 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
1216int
1217synthfs_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
1260int
1261synthfs_inactive(ap)
1262struct 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
1288out:
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 */
1307int
1308synthfs_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}
1323
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.