diff --git a/vboxvfs.h b/vboxvfs.h index 4bd6e34..0c77793 100644 --- a/vboxvfs.h +++ b/vboxvfs.h @@ -224,6 +224,7 @@ struct vboxfs_node { struct vnode *sf_vnode; /* vnode if active */ sfp_file_t *sf_file; /* non NULL if open */ struct vboxfs_node *sf_parent; /* parent sfnode of this one */ + uint32_t sf_opencnt; /* sf_file reference counter */ uint16_t sf_children; /* number of children sfnodes */ uint8_t sf_type; /* VDIR or VREG */ uint8_t sf_vpstate; /* XXX: ADD COMMENT */ @@ -232,7 +233,7 @@ struct vboxfs_node { uint64_t sf_stat_time; /* last-modified time of sf_stat */ sffs_dirents_t *sf_dir_list; /* list of entries for this directory */ - /* interlock to protect sf_vpstate */ + /* interlock to protect sf_vpstate, sf_file and sf_opencnt */ struct mtx sf_interlock; }; @@ -340,7 +341,7 @@ typedef struct sffs_fsinfo { extern int sfprov_get_fsinfo(sfp_mount_t *, sffs_fsinfo_t *); extern int sfprov_create(sfp_mount_t *, char *path, mode_t mode, - sfp_file_t **fp, sffs_stat_t *stat); + sffs_stat_t *stat); extern int sfprov_open(sfp_mount_t *, char *path, sfp_file_t **fp); extern int sfprov_close(sfp_file_t *fp); extern int sfprov_read(sfp_file_t *, char * buffer, uint64_t offset, @@ -370,7 +371,7 @@ extern int sfprov_set_size(sfp_mount_t *, char *, uint64_t); extern int sfprov_trunc(sfp_mount_t *, char *); extern int sfprov_remove(sfp_mount_t *, char *path, u_int is_link); extern int sfprov_mkdir(sfp_mount_t *, char *path, mode_t mode, - sfp_file_t **fp, sffs_stat_t *stat); + sffs_stat_t *stat); extern int sfprov_rmdir(sfp_mount_t *, char *path); extern int sfprov_rename(sfp_mount_t *, char *from, char *to, u_int is_dir); diff --git a/vboxvfs_prov.c b/vboxvfs_prov.c index da90630..6933321 100644 --- a/vboxvfs_prov.c +++ b/vboxvfs_prov.c @@ -305,7 +305,6 @@ sfprov_create( sfp_mount_t *mnt, char *path, mode_t mode, - sfp_file_t **fp, sffs_stat_t *stat) { int rc; @@ -330,10 +329,7 @@ sfprov_create( return (EEXIST); return (ENOENT); } - newfp = malloc(sizeof(sfp_file_t), M_VBOXVFS, M_WAITOK | M_ZERO); - newfp->handle = parms.Handle; - newfp->map = mnt->map; - *fp = newfp; + (void)VbglR0SfClose(&vbox_client, &mnt->map, parms.Handle); sfprov_stat_from_info(stat, &parms.Info); return (0); } @@ -710,14 +706,12 @@ sfprov_mkdir( sfp_mount_t *mnt, char *path, mode_t mode, - sfp_file_t **fp, sffs_stat_t *stat) { int rc; SHFLCREATEPARMS parms; SHFLSTRING *str; int size; - sfp_file_t *newfp; str = sfprov_string(path, &size); parms.Handle = SHFL_HANDLE_NIL; @@ -735,10 +729,7 @@ sfprov_mkdir( return (EEXIST); return (ENOENT); } - newfp = malloc(sizeof(sfp_file_t), M_VBOXVFS, M_WAITOK | M_ZERO); - newfp->handle = parms.Handle; - newfp->map = mnt->map; - *fp = newfp; + (void)VbglR0SfClose(&vbox_client, &mnt->map, parms.Handle); sfprov_stat_from_info(stat, &parms.Info); return (0); } diff --git a/vboxvfs_vnops.c b/vboxvfs_vnops.c index ef05842..a6725be 100644 --- a/vboxvfs_vnops.c +++ b/vboxvfs_vnops.c @@ -55,7 +55,8 @@ static vop_write_t vboxfs_write; static vop_fsync_t vboxfs_fsync; static vop_remove_t vboxfs_remove; static vop_link_t vboxfs_link; -static vop_cachedlookup_t vboxfs_lookup; +static vop_lookup_t vboxfs_lookup; +static vop_cachedlookup_t vboxfs_cachedlookup; static vop_rename_t vboxfs_rename; static vop_mkdir_t vboxfs_mkdir; static vop_rmdir_t vboxfs_rmdir; @@ -82,8 +83,8 @@ struct vop_vector vboxfs_vnodeops = { .vop_inactive = vboxfs_inactive, .vop_ioctl = vboxfs_ioctl, .vop_link = vboxfs_link, - .vop_lookup = vfs_cache_lookup, - .vop_cachedlookup = vboxfs_lookup, + .vop_lookup = vboxfs_lookup, + .vop_cachedlookup = vboxfs_cachedlookup, .vop_mkdir = vboxfs_mkdir, .vop_mknod = VOP_EOPNOTSUPP, .vop_open = vboxfs_open, @@ -386,13 +387,15 @@ static char * sfnode_construct_path(struct vboxfs_node *node, char *tail, int len) { char *p; + size_t dstsz; if (len <= 2 && tail[0] == '.' && (len == 1 || tail[1] == '.')) panic("construct path for %s", tail); - p = malloc(strlen(node->sf_path) + 1 + len + 1, M_VBOXVFS, M_WAITOK); + dstsz = strlen(node->sf_path) + 1 + len + 1; + p = malloc(dstsz, M_VBOXVFS, M_WAITOK); strcpy(p, node->sf_path); strcat(p, "/"); - strcat(p, tail); + strlcat(p, tail, dstsz); return (p); } @@ -449,23 +452,66 @@ vfsnode_clear_dir_list(struct vboxfs_node *np) } } +static int +vboxfs_get_sfp_file(struct vboxfs_node *np) +{ + sfp_file_t *fp; + int error; + + fp = NULL; + VBOXFS_NODE_LOCK(np); + for (;;) { + if (np->sf_file != NULL) { + if (fp != NULL) + (void) sfprov_close(fp); + np->sf_opencnt++; + fp = np->sf_file; + break; + } else if (fp != NULL) { + np->sf_file = fp; + KASSERT(np->sf_opencnt == 0, + ("np %p opencnt (%d) must be zero.", + np, np->sf_opencnt)); + np->sf_opencnt = 1; + break; + } + VBOXFS_NODE_UNLOCK(np); + error = sfprov_open(np->vboxfsmp->sf_handle, np->sf_path, &fp); + if (error != 0) + return (error); + VBOXFS_NODE_LOCK(np); + } + VBOXFS_NODE_UNLOCK(np); + + return (0); +} + +static void +vboxfs_put_sfp_file(struct vboxfs_node *np) +{ + VBOXFS_NODE_LOCK(np); + np->sf_opencnt--; + if (np->sf_opencnt == 0) { + (void) sfprov_close(np->sf_file); + np->sf_file = NULL; + } + VBOXFS_NODE_UNLOCK(np); +} + static int vboxfs_open(struct vop_open_args *ap) { struct vboxfs_node *np; - sfp_file_t *fp; int error; MPASS(VOP_ISLOCKED(vp)); np = VP_TO_VBOXFS_NODE(ap->a_vp); - error = sfprov_open(np->vboxfsmp->sf_handle, np->sf_path, &fp); + error = vboxfs_get_sfp_file(np); if (error != 0) goto out; - np->sf_file = fp; vnode_create_vobject(ap->a_vp, 0, ap->a_td); - out: MPASS(VOP_ISLOCKED(vp)); @@ -499,10 +545,7 @@ vboxfs_close(struct vop_close_args *ap) vfsnode_invalidate_stat_cache(np); - if (np->sf_file != NULL && vp->v_usecount <= 1) { - (void) sfprov_close(np->sf_file); - np->sf_file = NULL; - } + vboxfs_put_sfp_file(np); return (0); } @@ -684,6 +727,13 @@ vboxfs_read(struct vop_read_args *ap) if (tmpbuf == 0) return (ENOMEM); + /* + * XXX VOP_READ() is called without VOP_OPEN() on exec case. + * We need to ensure the file is opened here. + */ + error = vboxfs_get_sfp_file(np); + if (error != 0) /* Maybe removed on the host. */ + return (EIO); do { offset = uio->uio_offset; done = bytes = min(PAGE_SIZE, uio->uio_resid); @@ -692,6 +742,7 @@ vboxfs_read(struct vop_read_args *ap) if (error == 0 && done > 0) error = uiomove(tmpbuf, done, uio); } while (error == 0 && uio->uio_resid > 0 && done > 0); + vboxfs_put_sfp_file(np); contigfree(tmpbuf, PAGE_SIZE, M_DEVBUF); @@ -771,7 +822,6 @@ vboxfs_create(struct vop_create_args *ap) sffs_stat_t stat; char *fullpath = NULL; struct vboxfs_node *dir = VP_TO_VBOXFS_NODE(dvp); - sfp_file_t *fp; int error; struct vboxfs_mnt *vboxfsmp = dir->vboxfsmp; @@ -779,7 +829,7 @@ vboxfs_create(struct vop_create_args *ap) fullpath = sfnode_construct_path(dir, cnp->cn_nameptr, cnp->cn_namelen); error = sfprov_create(dir->vboxfsmp->sf_handle, fullpath, vap->va_mode, - &fp, &stat); + &stat); if (error) goto out; @@ -816,22 +866,6 @@ vboxfs_remove(struct vop_remove_args *ap) np = VP_TO_VBOXFS_NODE(vp); dir = VP_TO_VBOXFS_NODE(vp); - /* - * If anything else is using this vnode, then fail the remove. - * Why? Windows hosts can't sfprov_remove() a file that is open, - * so we have to sfprov_close() it first. - * There is no errno for this - since it's not a problem on UNIX, - * but ETXTBSY is the closest. - */ - if (np->sf_file != NULL) { - if (vp->v_usecount > 1) { - error = ETXTBSY; - goto out; - } - sfprov_close(np->sf_file); - np->sf_file = NULL; - } - error = sfprov_remove(np->vboxfsmp->sf_handle, np->sf_path, np->sf_type == VLNK); @@ -938,7 +972,6 @@ vboxfs_mkdir(struct vop_mkdir_args *ap) sffs_stat_t stat; char *fullpath = NULL; struct vboxfs_node *dir = VP_TO_VBOXFS_NODE(dvp); - sfp_file_t *fp; int error; struct vboxfs_mnt *vboxfsmp = dir->vboxfsmp; @@ -946,7 +979,7 @@ vboxfs_mkdir(struct vop_mkdir_args *ap) fullpath = sfnode_construct_path(dir, cnp->cn_nameptr, cnp->cn_namelen); error = sfprov_mkdir(dir->vboxfsmp->sf_handle, fullpath, vap->va_mode, - &fp, &stat); + &stat); if (error) goto out; @@ -980,22 +1013,6 @@ vboxfs_rmdir(struct vop_rmdir_args *ap) np = VP_TO_VBOXFS_NODE(vp); dir = VP_TO_VBOXFS_NODE(vp); - /* - * If anything else is using this vnode, then fail the remove. - * Why? Windows hosts can't sfprov_remove() a file that is open, - * so we have to sfprov_close() it first. - * There is no errno for this - since it's not a problem on UNIX, - * but ETXTBSY is the closest. - */ - if (np->sf_file != NULL) { - if (vp->v_usecount > 1) { - error = ETXTBSY; - goto out; - } - sfprov_close(np->sf_file); - np->sf_file = NULL; - } - error = sfprov_rmdir(np->vboxfsmp->sf_handle, np->sf_path); #if 0 @@ -1235,17 +1252,10 @@ vboxfs_ioctl(struct vop_ioctl_args *ap) * Lookup an entry in a directory and create a new vnode if found. */ static int -vboxfs_lookup(struct vop_cachedlookup_args /* { - struct vnodeop_desc *a_desc; - struct vnode *a_dvp; - struct vnode **a_vpp; - struct componentname *a_cnp; - } */ *ap) +vboxfs_lookup1(struct vnode *dvp, struct vnode **vpp, + struct componentname *cnp) { - struct componentname *cnp = ap->a_cnp; - struct vnode *dvp = ap->a_dvp; /* the directory vnode */ char *nameptr = cnp->cn_nameptr; /* the name of the file or directory */ - struct vnode **vpp = ap->a_vpp; /* the vnode we found or NULL */ struct vnode *tdp = NULL; struct vboxfs_node *node = VP_TO_VBOXFS_NODE(dvp); struct vboxfs_mnt *vboxfsmp = node->vboxfsmp; @@ -1258,6 +1268,7 @@ vboxfs_lookup(struct vop_cachedlookup_args /* { int lkflags = cnp->cn_lkflags; char *fullpath = NULL; + *vpp = NULLVP; error = ENOENT; if (cnp->cn_flags & ISDOTDOT) { error = vn_vget_ino_gen(dvp, vboxfs_vn_get_ino_alloc, @@ -1329,6 +1340,47 @@ out: return (error); } +static int +vboxfs_cachedlookup(struct vop_cachedlookup_args *ap) +{ + return (vboxfs_lookup1(ap->a_dvp, ap->a_vpp, ap->a_cnp)); +} + +static int +vboxfs_lookup(struct vop_lookup_args *ap) +{ + struct vnode *dvp = ap->a_dvp; + struct componentname *cnp = ap->a_cnp; + struct vboxfs_node *np = VP_TO_VBOXFS_NODE(dvp); + struct timespec mtime; + int flags = cnp->cn_flags; + int error; + + if (dvp->v_type != VDIR) + return (ENOTDIR); + + if ((flags & ISLASTCN) && (dvp->v_mount->mnt_flag & MNT_RDONLY) && + (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME)) + return (EROFS); + + error = vn_dir_check_exec(dvp, cnp); + if (error != 0) + return (error); + + /* Check if the directory is unmodified on the host. */ + mtime = np->sf_stat.sf_mtime; + error = vsfnode_update_stat_cache(np); + if (error == 0) { + if (mtime.tv_sec == np->sf_stat.sf_mtime.tv_sec && + mtime.tv_nsec == np->sf_stat.sf_mtime.tv_nsec) + return (vfs_cache_lookup(ap)); + } + + cache_purge(dvp); + + return (vboxfs_lookup1(ap->a_dvp, ap->a_vpp, ap->a_cnp)); +} + static int vboxfs_inactive(struct vop_inactive_args *ap) {