Пример #1
0
static int
fuse_sync_callback(vnode_t vp, void *cargs)
{
    int type;
    struct fuse_sync_cargs *args;
    struct fuse_vnode_data *fvdat;
    struct fuse_filehandle *fufh;
    struct fuse_data       *data;
    mount_t mp;

    if (!vnode_hasdirtyblks(vp)) {
        return VNODE_RETURNED;
    }

    mp = vnode_mount(vp);

    if (fuse_isdeadfs_mp(mp)) {
        return VNODE_RETURNED_DONE;
    }

    data = fuse_get_mpdata(mp);

    if (!fuse_implemented(data, (vnode_isdir(vp)) ?
        FSESS_NOIMPLBIT(FSYNCDIR) : FSESS_NOIMPLBIT(FSYNC))) {
        return VNODE_RETURNED;
    }

    args = (struct fuse_sync_cargs *)cargs;
    fvdat = VTOFUD(vp);

#if M_OSXFUSE_ENABLE_BIG_LOCK
    fuse_biglock_unlock(data->biglock);
#endif
    cluster_push(vp, 0);
#if M_OSXFUSE_ENABLE_BIG_LOCK
    fuse_biglock_lock(data->biglock);
#endif

    for (type = 0; type < FUFH_MAXTYPE; type++) {
        fufh = &(fvdat->fufh[type]);
        if (FUFH_IS_VALID(fufh)) {
            (void)fuse_internal_fsync_fh(vp, args->context, fufh,
                                         FUSE_OP_FOREGROUNDED);
        }
    }

    /*
     * In general:
     *
     * - can use vnode_isinuse() if the need be
     * - vnode and UBC are in lock-step
     * - note that umount will call ubc_sync_range()
     */

    return VNODE_RETURNED;
}
Пример #2
0
static int
fuse_sync_callback(vnode_t vp, void *cargs)
{
    int type;
    struct fuse_sync_cargs *args;
    struct fuse_vnode_data *fvdat;
    struct fuse_dispatcher  fdi;
    struct fuse_filehandle *fufh;
    struct fuse_data       *data;
    mount_t mp;

    if (!vnode_hasdirtyblks(vp)) {
        return VNODE_RETURNED;
    }

    mp = vnode_mount(vp);

    if (fuse_isdeadfs(vp)) {
        return VNODE_RETURNED_DONE;
    }

    data = fuse_get_mpdata(mp);

    if (!fuse_implemented(data, (vnode_isdir(vp)) ?
        FSESS_NOIMPLBIT(FSYNCDIR) : FSESS_NOIMPLBIT(FSYNC))) {
        return VNODE_RETURNED;
    }

    args = (struct fuse_sync_cargs *)cargs;
    fvdat = VTOFUD(vp);

    cluster_push(vp, 0);

    fuse_dispatcher_init(&fdi, 0);
    for (type = 0; type < FUFH_MAXTYPE; type++) {
        fufh = &(fvdat->fufh[type]);
        if (FUFH_IS_VALID(fufh)) {
            (void)fuse_internal_fsync(vp, args->context, fufh, &fdi);
        }
    }

    /*
     * In general:
     *
     * - can use vnode_isinuse() if the need be
     * - vnode and UBC are in lock-step
     * - note that umount will call ubc_sync_range()
     */

    return VNODE_RETURNED;
}
Пример #3
0
__private_extern__
int
fuse_internal_fsync(vnode_t                 vp,
                    vfs_context_t           context,
                    struct fuse_filehandle *fufh,
                    void                   *param,
                    fuse_op_waitfor_t       waitfor)
{
    int err = 0;
    int op = FUSE_FSYNC;
    struct fuse_fsync_in *ffsi;
    struct fuse_dispatcher *fdip = param;

    fuse_trace_printf_func();

    fdip->iosize = sizeof(*ffsi);
    fdip->tick = NULL;
    if (vnode_isdir(vp)) {
        op = FUSE_FSYNCDIR;
    }
    
    fdisp_make_vp(fdip, op, vp, context);
    ffsi = fdip->indata;
    ffsi->fh = fufh->fh_id;

    ffsi->fsync_flags = 1; /* datasync */

    if (waitfor == FUSE_OP_FOREGROUNDED) {
        if ((err = fdisp_wait_answ(fdip))) {
            if (err == ENOSYS) {
                if (op == FUSE_FSYNC) {
                    fuse_clear_implemented(fdip->tick->tk_data,
                                           FSESS_NOIMPLBIT(FSYNC));
                } else if (op == FUSE_FSYNCDIR) {
                    fuse_clear_implemented(fdip->tick->tk_data,
                                           FSESS_NOIMPLBIT(FSYNCDIR));
                }
            }
            goto out;
        } else {
            fuse_ticket_drop(fdip->tick);
        }
    } else {
        fuse_insert_callback(fdip->tick, fuse_internal_fsync_callback);
        fuse_insert_message(fdip->tick);
    }

out:
    return err;
}
Пример #4
0
__private_extern__
int
fuse_internal_fsync_callback(struct fuse_ticket *ftick, __unused uio_t uio)
{
    fuse_trace_printf_func();

    if (ftick->tk_aw_ohead.error == ENOSYS) {
        if (fticket_opcode(ftick) == FUSE_FSYNC) {
            fuse_clear_implemented(ftick->tk_data, FSESS_NOIMPLBIT(FSYNC));
        } else if (fticket_opcode(ftick) == FUSE_FSYNCDIR) {
            fuse_clear_implemented(ftick->tk_data, FSESS_NOIMPLBIT(FSYNCDIR));
        } else {
            IOLog("MacFUSE: unexpected opcode in sync handling\n");
        }
    }

    fuse_ticket_drop(ftick);

    return 0;
}
Пример #5
0
static void
handle_capabilities_and_attributes(mount_t mp, struct vfs_attr *attr)
{

    struct fuse_data *data = fuse_get_mpdata(mp);
    if (!data) {
        panic("fuse4x: no private data for mount point?");
    }

    attr->f_capabilities.capabilities[VOL_CAPABILITIES_FORMAT] = 0
//      | VOL_CAP_FMT_PERSISTENTOBJECTIDS
            | VOL_CAP_FMT_SYMBOLICLINKS

            /*
             * Note that we don't really have hard links in a fuse4x file system
             * unless the user file system daemon provides persistent/consistent
             * inode numbers. Maybe instead of returning the "wrong" answer here
             * we should just deny knowledge of this capability in the valid bits
             * below.
             */
            | VOL_CAP_FMT_HARDLINKS
//      | VOL_CAP_FMT_JOURNAL
//      | VOL_CAP_FMT_JOURNAL_ACTIVE
            | VOL_CAP_FMT_NO_ROOT_TIMES
//      | VOL_CAP_FMT_SPARSE_FILES
//      | VOL_CAP_FMT_ZERO_RUNS
//      | VOL_CAP_FMT_CASE_SENSITIVE
            | VOL_CAP_FMT_CASE_PRESERVING
            | VOL_CAP_FMT_FAST_STATFS
            | VOL_CAP_FMT_2TB_FILESIZE
//      | VOL_CAP_FMT_OPENDENYMODES
//      | VOL_CAP_FMT_HIDDEN_FILES
//      | VOL_CAP_FMT_PATH_FROM_ID
            ;

    attr->f_capabilities.valid[VOL_CAPABILITIES_FORMAT] = 0
            | VOL_CAP_FMT_PERSISTENTOBJECTIDS
            | VOL_CAP_FMT_SYMBOLICLINKS
            | VOL_CAP_FMT_HARDLINKS
            | VOL_CAP_FMT_JOURNAL
            | VOL_CAP_FMT_JOURNAL_ACTIVE
            | VOL_CAP_FMT_NO_ROOT_TIMES
            | VOL_CAP_FMT_SPARSE_FILES
            | VOL_CAP_FMT_ZERO_RUNS
            | VOL_CAP_FMT_CASE_SENSITIVE
            | VOL_CAP_FMT_CASE_PRESERVING
            | VOL_CAP_FMT_FAST_STATFS
            | VOL_CAP_FMT_2TB_FILESIZE
            | VOL_CAP_FMT_OPENDENYMODES
            | VOL_CAP_FMT_HIDDEN_FILES
            | VOL_CAP_FMT_PATH_FROM_ID
            ;

    attr->f_capabilities.capabilities[VOL_CAPABILITIES_INTERFACES] = 0
//      | VOL_CAP_INT_SEARCHFS
            | VOL_CAP_INT_ATTRLIST
//      | VOL_CAP_INT_NFSEXPORT
//      | VOL_CAP_INT_READDIRATTR
//      | VOL_CAP_INT_EXCHANGEDATA
//      | VOL_CAP_INT_COPYFILE
//      | VOL_CAP_INT_ALLOCATE
//      | VOL_CAP_INT_VOL_RENAME
            | VOL_CAP_INT_ADVLOCK
            | VOL_CAP_INT_FLOCK
            | VOL_CAP_INT_EXTENDED_SECURITY
//      | VOL_CAP_INT_USERACCESS
//      | VOL_CAP_INT_MANLOCK
//      | VOL_CAP_INT_EXTENDED_ATTR
//      | VOL_CAP_INT_NAMEDSTREAMS
            ;

    if (data->dataflags & FSESS_NATIVE_XATTR) {
        attr->f_capabilities.capabilities[VOL_CAPABILITIES_INTERFACES] |=
            VOL_CAP_INT_EXTENDED_ATTR;
    }

    /* Don't set the EXCHANGEDATA capability if it's known not to be
     * implemented in the FUSE daemon. */
    if (fuse_implemented(data, FSESS_NOIMPLBIT(EXCHANGE))) {
        attr->f_capabilities.capabilities[VOL_CAPABILITIES_INTERFACES] |=
            VOL_CAP_INT_EXCHANGEDATA;
    }

    attr->f_capabilities.valid[VOL_CAPABILITIES_INTERFACES] = 0
            | VOL_CAP_INT_SEARCHFS
            | VOL_CAP_INT_ATTRLIST
            | VOL_CAP_INT_NFSEXPORT
            | VOL_CAP_INT_READDIRATTR
            | VOL_CAP_INT_EXCHANGEDATA
            | VOL_CAP_INT_COPYFILE
            | VOL_CAP_INT_ALLOCATE
            | VOL_CAP_INT_VOL_RENAME
            | VOL_CAP_INT_ADVLOCK
            | VOL_CAP_INT_FLOCK
            | VOL_CAP_INT_EXTENDED_SECURITY
            | VOL_CAP_INT_USERACCESS
            | VOL_CAP_INT_MANLOCK
            | VOL_CAP_INT_EXTENDED_ATTR
            | VOL_CAP_INT_NAMEDSTREAMS
            ;

    attr->f_capabilities.capabilities[VOL_CAPABILITIES_RESERVED1] = 0;
    attr->f_capabilities.valid[VOL_CAPABILITIES_RESERVED1] = 0;
    attr->f_capabilities.capabilities[VOL_CAPABILITIES_RESERVED2] = 0;
    attr->f_capabilities.valid[VOL_CAPABILITIES_RESERVED2] = 0;
    VFSATTR_SET_SUPPORTED(attr, f_capabilities);

    attr->f_attributes.validattr.commonattr = 0
            | ATTR_CMN_NAME
            | ATTR_CMN_DEVID
            | ATTR_CMN_FSID
            | ATTR_CMN_OBJTYPE
//      | ATTR_CMN_OBJTAG
            | ATTR_CMN_OBJID
//      | ATTR_CMN_OBJPERMANENTID
            | ATTR_CMN_PAROBJID
//      | ATTR_CMN_SCRIPT
//      | ATTR_CMN_CRTIME
            | ATTR_CMN_MODTIME
//      | ATTR_CMN_CHGTIME
//      | ATTR_CMN_ACCTIME
//      | ATTR_CMN_BKUPTIME
//      | ATTR_CMN_FNDRINFO
            | ATTR_CMN_OWNERID
            | ATTR_CMN_GRPID
            | ATTR_CMN_ACCESSMASK
            | ATTR_CMN_FLAGS
//      | ATTR_CMN_USERACCESS
            | ATTR_CMN_EXTENDED_SECURITY
//      | ATTR_CMN_UUID
//      | ATTR_CMN_GRPUUID
//      | ATTR_CMN_FILEID
//      | ATTR_CMN_PARENTID
            ;

    attr->f_attributes.validattr.volattr = 0
                                           | ATTR_VOL_FSTYPE
                                           | ATTR_VOL_SIGNATURE
                                           | ATTR_VOL_SIZE
                                           | ATTR_VOL_SPACEFREE
                                           | ATTR_VOL_SPACEAVAIL
//      | ATTR_VOL_MINALLOCATION
//      | ATTR_VOL_ALLOCATIONCLUMP
                                           | ATTR_VOL_IOBLOCKSIZE
//      | ATTR_VOL_OBJCOUNT
                                           | ATTR_VOL_FILECOUNT
//      | ATTR_VOL_DIRCOUNT
//      | ATTR_VOL_MAXOBJCOUNT
                                           | ATTR_VOL_MOUNTPOINT
                                           | ATTR_VOL_NAME
                                           | ATTR_VOL_MOUNTFLAGS
                                           | ATTR_VOL_MOUNTEDDEVICE
//      | ATTR_VOL_ENCODINGSUSED
                                           | ATTR_VOL_CAPABILITIES
                                           | ATTR_VOL_ATTRIBUTES
//      | ATTR_VOL_INFO
                                           ;
    attr->f_attributes.validattr.dirattr = 0
                                           | ATTR_DIR_LINKCOUNT
//      | ATTR_DIR_ENTRYCOUNT
//      | ATTR_DIR_MOUNTSTATUS
                                           ;
    attr->f_attributes.validattr.fileattr = 0
                                            | ATTR_FILE_LINKCOUNT
                                            | ATTR_FILE_TOTALSIZE
                                            | ATTR_FILE_ALLOCSIZE
                                            | ATTR_FILE_IOBLOCKSIZE
                                            | ATTR_FILE_DEVTYPE
//      | ATTR_FILE_FORKCOUNT
//      | ATTR_FILE_FORKLIST
                                            | ATTR_FILE_DATALENGTH
                                            | ATTR_FILE_DATAALLOCSIZE
//      | ATTR_FILE_RSRCLENGTH
//      | ATTR_FILE_RSRCALLOCSIZE
                                            ;

    attr->f_attributes.validattr.forkattr = 0;
//      | ATTR_FORK_TOTALSIZE
//      | ATTR_FORK_ALLOCSIZE
    ;

    // Handle some special cases

    if (!(data->dataflags & FSESS_CASE_INSENSITIVE)) {
        attr->f_capabilities.capabilities[VOL_CAPABILITIES_FORMAT] |=
            VOL_CAP_FMT_CASE_SENSITIVE;
    }

    /*
    if (data->dataflags & FSESS_VOL_RENAME) {
        attr->f_capabilities.capabilities[VOL_CAPABILITIES_INTERFACES] |=
            VOL_CAP_INT_VOL_RENAME;
    } else {
        fuse_clear_implemented(data, FSESS_NOIMPLBIT(SETVOLNAME));
    }
    */
    /* Not yet. */
    fuse_clear_implemented(data, FSESS_NOIMPLBIT(SETVOLNAME));

    if (data->dataflags & FSESS_XTIMES) {
        attr->f_attributes.validattr.commonattr |=
            (ATTR_CMN_BKUPTIME | ATTR_CMN_CHGTIME | ATTR_CMN_CRTIME);
    } else {
        fuse_clear_implemented(data, FSESS_NOIMPLBIT(GETXTIMES));
    }

    // All attributes that we do support, we support natively.

    attr->f_attributes.nativeattr.commonattr = \
            attr->f_attributes.validattr.commonattr;
    attr->f_attributes.nativeattr.volattr    = \
            attr->f_attributes.validattr.volattr;
    attr->f_attributes.nativeattr.dirattr    = \
            attr->f_attributes.validattr.dirattr;
    attr->f_attributes.nativeattr.fileattr   = \
            attr->f_attributes.validattr.fileattr;
    attr->f_attributes.nativeattr.forkattr   = \
            attr->f_attributes.validattr.forkattr;

    VFSATTR_SET_SUPPORTED(attr, f_attributes);
}
Пример #6
0
static errno_t
fuse_vfsop_setattr(mount_t mp, struct vfs_attr *fsap, vfs_context_t context)
{
    int error = 0;

    fuse_trace_printf_vfsop();

    kauth_cred_t cred = vfs_context_ucred(context);

    if (!fuse_vfs_context_issuser(context) &&
            (kauth_cred_getuid(cred) != vfs_statfs(mp)->f_owner)) {
        return EACCES;
    }

    struct fuse_data *data = fuse_get_mpdata(mp);

    if (VFSATTR_IS_ACTIVE(fsap, f_vol_name)) {

        if (!fuse_implemented(data, FSESS_NOIMPLBIT(SETVOLNAME))) {
            error = ENOTSUP;
            goto out;
        }

        if (fsap->f_vol_name[0] == 0) {
            error = EINVAL;
            goto out;
        }

        size_t namelen = strlen(fsap->f_vol_name);
        if (namelen >= MAXPATHLEN) {
            error = ENAMETOOLONG;
            goto out;
        }

        vnode_t root_vp;

        error = fuse_vfsop_root(mp, &root_vp, context);
        if (error) {
            goto out;
        }

        struct fuse_dispatcher fdi;
        fdisp_init(&fdi, namelen + 1);
        fdisp_make_vp(&fdi, FUSE_SETVOLNAME, root_vp, context);
        memcpy((char *)fdi.indata, fsap->f_vol_name, namelen);
        ((char *)fdi.indata)[namelen] = '\0';

        if (!(error = fdisp_wait_answ(&fdi))) {
            fuse_ticket_drop(fdi.tick);
        }

        (void)vnode_put(root_vp);

        if (error) {
            if (error == ENOSYS) {
                error = ENOTSUP;
                fuse_clear_implemented(data, FSESS_NOIMPLBIT(SETVOLNAME));
            }
            goto out;
        }

        copystr(fsap->f_vol_name, data->volname, MAXPATHLEN - 1, &namelen);
        bzero(data->volname + namelen, MAXPATHLEN - namelen);

        VFSATTR_SET_SUPPORTED(fsap, f_vol_name);
    }

out:
    return error;
}
Пример #7
0
__private_extern__
int
fuse_internal_access(vnode_t                   vp,
                     int                       action,
                     vfs_context_t             context,
                     struct fuse_access_param *facp)
{
    int err = 0;
    int default_error = 0;
    uint32_t mask = 0;
    int dataflags;
    mount_t mp;
    struct fuse_dispatcher fdi;
    struct fuse_access_in *fai;
    struct fuse_data      *data;

    fuse_trace_printf_func();

    mp = vnode_mount(vp);

    data = fuse_get_mpdata(mp);
    dataflags = data->dataflags;

    /* Allow for now; let checks be handled inline later. */
    if (fuse_isdeferpermissions_mp(mp)) {
        return 0;
    }

    if (facp->facc_flags & FACCESS_FROM_VNOP) {
        default_error = ENOTSUP;
    }

    /*
     * (action & KAUTH_VNODE_GENERIC_WRITE_BITS) on a read-only file system
     * would have been handled by higher layers.
     */

    if (!fuse_implemented(data, FSESS_NOIMPLBIT(ACCESS))) {
        return default_error;
    }

    /* Unless explicitly permitted, deny everyone except the fs owner. */
    if (!vnode_isvroot(vp) && !(facp->facc_flags & FACCESS_NOCHECKSPY)) {
        if (!(dataflags & FSESS_ALLOW_OTHER)) {
            int denied = fuse_match_cred(data->daemoncred,
                                         vfs_context_ucred(context));
            if (denied) {
                return EPERM;
            }
        }
        facp->facc_flags |= FACCESS_NOCHECKSPY;
    }

    if (!(facp->facc_flags & FACCESS_DO_ACCESS)) {
        return default_error;
    }

    if (vnode_isdir(vp)) {
        if (action & (KAUTH_VNODE_LIST_DIRECTORY   |
                      KAUTH_VNODE_READ_EXTATTRIBUTES)) {
            mask |= R_OK;
        }
        if (action & (KAUTH_VNODE_ADD_FILE         |
                      KAUTH_VNODE_ADD_SUBDIRECTORY |
                      KAUTH_VNODE_DELETE_CHILD)) {
            mask |= W_OK;
        }
        if (action & KAUTH_VNODE_SEARCH) {
            mask |= X_OK;
        }
    } else {
        if (action & (KAUTH_VNODE_READ_DATA | KAUTH_VNODE_READ_EXTATTRIBUTES)) {
            mask |= R_OK;
        }
        if (action & (KAUTH_VNODE_WRITE_DATA | KAUTH_VNODE_APPEND_DATA)) {
            mask |= W_OK;
        }
        if (action & KAUTH_VNODE_EXECUTE) {
            mask |= X_OK;
        }
    }

    if (action & (KAUTH_VNODE_WRITE_ATTRIBUTES    |
                  KAUTH_VNODE_WRITE_EXTATTRIBUTES |
                  KAUTH_VNODE_WRITE_SECURITY)) {
        mask |= W_OK;
    }

    bzero(&fdi, sizeof(fdi));

    fdisp_init(&fdi, sizeof(*fai));
    fdisp_make_vp(&fdi, FUSE_ACCESS, vp, context);

    fai = fdi.indata;
    fai->mask = F_OK;
    fai->mask |= mask;

    if (!(err = fdisp_wait_answ(&fdi))) {
        fuse_ticket_drop(fdi.tick);
    }

    if (err == ENOSYS) {
        /*
         * Make sure we don't come in here again.
         */
        vfs_clearauthopaque(mp);
        fuse_clear_implemented(data, FSESS_NOIMPLBIT(ACCESS));
        err = default_error;
    }

    if (err == ENOENT) {

        const char *vname = NULL;

#if M_MACFUSE_ENABLE_UNSUPPORTED
        vname = vnode_getname(vp);
#endif /* M_MACFUSE_ENABLE_UNSUPPORTED */

        IOLog("MacFUSE: disappearing vnode %p (name=%s type=%d action=%x)\n",
              vp, (vname) ? vname : "?", vnode_vtype(vp), action);

#if M_MACFUSE_ENABLE_UNSUPPORTED
        if (vname) {
            vnode_putname(vname);
        }
#endif /* M_MACFUSE_ENABLE_UNSUPPORTED */

        /*
         * On 10.4, I think I can get Finder to lock because of /.Trashes/<uid>
         * unless I use REVOKE_NONE here.
         */
         
#if M_MACFUSE_ENABLE_INTERIM_FSNODE_LOCK && !M_MACFUSE_ENABLE_HUGE_LOCK
        fuse_biglock_unlock(data->biglock);
#endif
        fuse_internal_vnode_disappear(vp, context, REVOKE_SOFT);
#if M_MACFUSE_ENABLE_INTERIM_FSNODE_LOCK && !M_MACFUSE_ENABLE_HUGE_LOCK
        fuse_biglock_lock(data->biglock);
#endif
    }

    return err;
}
Пример #8
0
/* getattr sidekicks */
__private_extern__
int
fuse_internal_loadxtimes(vnode_t vp, struct vnode_attr *out_vap,
                         vfs_context_t context)
{
    struct vnode_attr *in_vap = VTOVA(vp);
    struct fuse_data *data = fuse_get_mpdata(vnode_mount(vp));
    struct fuse_dispatcher fdi;
    struct fuse_getxtimes_out *fgxo = NULL;
    int isvroot = vnode_isvroot(vp);
    struct timespec t = { 0, 0 };
    const struct timespec kZeroTime = { 0, 0 };
    int err = 0;

    if (!(data->dataflags & FSESS_XTIMES)) {
        /* We don't return anything. */
        goto out;
    }

    if (VTOFUD(vp)->c_flag & C_XTIMES_VALID) {
        VATTR_RETURN(out_vap, va_backup_time, in_vap->va_backup_time);
        VATTR_RETURN(out_vap, va_create_time, in_vap->va_create_time);
        goto out;
    }

    if (!fuse_implemented(data, FSESS_NOIMPLBIT(GETXTIMES))) {
        goto fake;
    }

    if (fuse_isdeadfs(vp) && isvroot) {
        goto fake;
    }

    if (!(data->dataflags & FSESS_INITED) && isvroot) {
        goto fake;
    }

    err = fdisp_simple_putget_vp(&fdi, FUSE_GETXTIMES, vp, context);
    if (err) {
        /* We don't ever treat this as a hard error. */
        err = 0;
        goto fake;
    }

    fgxo = (struct fuse_getxtimes_out *)fdi.answ;

    t.tv_sec = (time_t)fgxo->bkuptime; /* XXX: truncation */
    t.tv_nsec = fgxo->bkuptimensec;
    VATTR_RETURN(in_vap, va_backup_time, t);
    VATTR_RETURN(out_vap, va_backup_time, t);

    t.tv_sec = (time_t)fgxo->crtime; /* XXX: truncation */
    t.tv_nsec = fgxo->crtimensec;
    VATTR_RETURN(in_vap, va_create_time, t);
    VATTR_RETURN(out_vap, va_create_time, t);

    fuse_ticket_drop(fdi.tick);

    VTOFUD(vp)->c_flag |= C_XTIMES_VALID;

    goto out;

fake:
    VATTR_RETURN(out_vap, va_backup_time, kZeroTime);
    VATTR_RETURN(out_vap, va_create_time, kZeroTime);

out:
    return err;
}