Пример #1
0
static int
zfs_vfs_mount(struct mount *mp, vnode_t devvp, user_addr_t data, vfs_context_t context)
{
	char	*osname = NULL;
	size_t  osnamelen = 0;
	int		error = 0;
	int		canwrite;
	/*
	 * Get the objset name (the "special" mount argument).
	 * The filesystem that we mount as root is defined in the
	 * "zfs-bootfs" property. 
	 */
	if (data) {
		user_addr_t fspec = USER_ADDR_NULL;
#ifndef __APPLE__
		if (ddi_prop_lookup_string(DDI_DEV_T_ANY, ddi_root_node(),
		    DDI_PROP_DONTPASS, "zfs-bootfs", &zfs_bootpath) !=
		    DDI_SUCCESS)
			return (EIO);

		error = parse_bootpath(zfs_bootpath, rootfs.bo_name);
		ddi_prop_free(zfs_bootpath);
#endif
		osname = kmem_alloc(MAXPATHLEN, KM_SLEEP);

		if (vfs_context_is64bit(context)) {
			if ( (error = copyin(data, (caddr_t)&fspec, sizeof(fspec))) )
				goto out;	
		} else {
#ifdef ZFS_LEOPARD_ONLY
			char *tmp;
#else
			user32_addr_t tmp;
#endif
			if ( (error = copyin(data, (caddr_t)&tmp, sizeof(tmp))) )
				goto out;	
			/* munge into LP64 addr */
			fspec = CAST_USER_ADDR_T(tmp);
		}
		if ( (error = copyinstr(fspec, osname, MAXPATHLEN, &osnamelen)) )
			goto out;
	}

#if 0
	if (mvp->v_type != VDIR)
		return (ENOTDIR);

	mutex_enter(&mvp->v_lock);
	if ((uap->flags & MS_REMOUNT) == 0 &&
	    (uap->flags & MS_OVERLAY) == 0 &&
	    (mvp->v_count != 1 || (mvp->v_flag & VROOT))) {
		mutex_exit(&mvp->v_lock);
		return (EBUSY);
	}
	mutex_exit(&mvp->v_lock);

	/*
	 * ZFS does not support passing unparsed data in via MS_DATA.
	 * Users should use the MS_OPTIONSTR interface; this means
	 * that all option parsing is already done and the options struct
	 * can be interrogated.
	 */
	if ((uap->flags & MS_DATA) && uap->datalen > 0)
		return (EINVAL);

	/*
	 * Get the objset name (the "special" mount argument).
	 */
	if (error = pn_get(uap->spec, fromspace, &spn))
		return (error);

	osname = spn.pn_path;
#endif
	/*
	 * Check for mount privilege?
	 *
	 * If we don't have privilege then see if
	 * we have local permission to allow it
	 */
#ifndef __APPLE__
	error = secpolicy_fs_mount(cr, mvp, vfsp);
	if (error) {
		error = dsl_deleg_access(osname, ZFS_DELEG_PERM_MOUNT, cr);
		if (error == 0) {
			vattr_t		vattr;

			/*
			 * Make sure user is the owner of the mount point
			 * or has sufficient privileges.
			 */

			vattr.va_mask = AT_UID;

			if (error = VOP_GETATTR(mvp, &vattr, 0, cr)) {
				goto out;
			}

			if (error = secpolicy_vnode_owner(cr, vattr.va_uid)) {
				goto out;
			}

			if (error = VOP_ACCESS(mvp, VWRITE, 0, cr)) {
				goto out;
			}

			secpolicy_fs_mount_clearopts(cr, vfsp);
		} else {
			goto out;
		}
	}
#endif

	error = zfs_domount(mp, 0, osname, context);
	if (error)
		printf("zfs_vfs_mount: error %d\n", error);
	if (error == 0) {
		zfsvfs_t *zfsvfs = NULL;

		/* Make the Finder treat sub file systems just like a folder */
		if (strpbrk(osname, "/"))
			vfs_setflags(mp, (u_int64_t)((unsigned int)MNT_DONTBROWSE));

		/* Indicate to VFS that we support ACLs. */
		vfs_setextendedsecurity(mp);

		/* Advisory locking should be handled at the VFS layer */
		vfs_setlocklocal(mp);

		/*
		 * Mac OS X needs a file system modify time
		 *
		 * We use the mtime of the "com.apple.system.mtime" 
		 * extended attribute, which is associated with the
		 * file system root directory.
		 *
		 * Here we need to take a ref on z_mtime_vp to keep it around.
		 * If the attribute isn't there, attempt to create it.
		 */
		zfsvfs = vfs_fsprivate(mp);
		if (zfsvfs->z_mtime_vp == NULL) {
			struct vnode * rvp;
			struct vnode *xdvp = NULLVP;
			struct vnode *xvp = NULLVP;
			znode_t *rootzp;
			timestruc_t modify_time;
			cred_t  *cr;
			timestruc_t  now;
			int flag;
			int result;

			if (zfs_zget(zfsvfs, zfsvfs->z_root, &rootzp) != 0) {
				goto out;
			}
			rvp = ZTOV(rootzp);
			cr = (cred_t *)vfs_context_ucred(context);

			/* Grab the hidden attribute directory vnode. */
			result = zfs_get_xattrdir(rootzp, &xdvp, cr, CREATE_XATTR_DIR);
			vnode_put(rvp);	/* all done with root vnode */
			rvp = NULL;
			if (result) {
				goto out;
			}

			/*
			 * HACK - workaround missing vnode_setnoflush() KPI...
			 *
			 * We tag zfsvfs so that zfs_attach_vnode() can then set
			 * vnfs_marksystem when the vnode gets created.
			 */
			zfsvfs->z_last_unmount_time = 0xBADC0DE;
			zfsvfs->z_last_mtime_synced = VTOZ(xdvp)->z_id;
			flag = vfs_isrdonly(mp) ? 0 : ZEXISTS;
			/* Lookup or create the named attribute. */
			if ( zfs_obtain_xattr(VTOZ(xdvp), ZFS_MTIME_XATTR,
			                          S_IRUSR | S_IWUSR, cr, &xvp,
			                          flag) ) {
					zfsvfs->z_last_unmount_time = 0;
					zfsvfs->z_last_mtime_synced = 0;
					vnode_put(xdvp);
					goto out;
				}
				gethrestime(&now);
			ZFS_TIME_ENCODE(&now, VTOZ(xvp)->z_phys->zp_mtime);
			vnode_put(xdvp);
			vnode_ref(xvp);

			zfsvfs->z_mtime_vp = xvp;
			ZFS_TIME_DECODE(&modify_time, VTOZ(xvp)->z_phys->zp_mtime);
			zfsvfs->z_last_unmount_time = modify_time.tv_sec;
			zfsvfs->z_last_mtime_synced = modify_time.tv_sec;

			/*
			 * Keep this referenced vnode from impeding an unmount.
			 *
			 * XXX vnode_setnoflush() is MIA from KPI (see workaround above).
			 */
#if 0
			vnode_setnoflush(xvp);
#endif
			vnode_put(xvp);
		}
	}
out:
	if (osname) {
		kmem_free(osname, MAXPATHLEN);
	}
	return (error);
}
Пример #2
0
/*
 * Called from user land so we always have a reference on the share.
 */
int 
smb_usr_read_write(struct smb_share *share, u_long cmd, struct smb2ioc_rw *rw_ioc, vfs_context_t context)
{
	int error;
 	struct smb2_rw_rq *read_writep = NULL;
    
    SMB_MALLOC(read_writep,
               struct smb2_rw_rq *, 
               sizeof(struct smb2_rw_rq), 
               M_SMBTEMP, 
               M_WAITOK | M_ZERO);
    if (read_writep == NULL) {
        SMBERROR("SMB_MALLOC failed\n");
        error = ENOMEM;
        goto bad;
    }
    
    /* Take 32 bit world pointers and convert them to user_addr_t. */
    if (vfs_context_is64bit(context)) {
        read_writep->auio = uio_create(1,
                            rw_ioc->ioc_offset,
                            UIO_USERSPACE64,
                            (cmd == SMB2IOC_READ) ? UIO_READ : UIO_WRITE);
    }
    else {
        rw_ioc->ioc_kern_base = CAST_USER_ADDR_T(rw_ioc->ioc_base);
        read_writep->auio = uio_create(1,
                            rw_ioc->ioc_offset,
                            UIO_USERSPACE32,
                            (cmd == SMB2IOC_READ) ? UIO_READ : UIO_WRITE);
    }
    
    if (read_writep->auio) {
        /* <14516550> All IO requests from user space are done synchronously */
        read_writep->flags |= SMB2_SYNC_IO;
        
        uio_addiov(read_writep->auio, rw_ioc->ioc_kern_base, rw_ioc->ioc_len);
        
        read_writep->remaining = rw_ioc->ioc_remaining;
        read_writep->write_flags = rw_ioc->ioc_write_flags;
        read_writep->fid = rw_ioc->ioc_fid;
        
        /* Now do the real work */
        if (cmd == SMB2IOC_READ) {
            error = smb2_smb_read(share, read_writep, context);
        } 
        else {
            error = smb2_smb_write(share, read_writep, context);
        }
        
        /* always return the ntstatus error */
        rw_ioc->ioc_ret_ntstatus = read_writep->ret_ntstatus;
        if (error) {
            goto bad;
        }
        
        /* Fill in actual bytes read or written */
        rw_ioc->ioc_ret_len = read_writep->ret_len;
    } 
    else {
        error = ENOMEM;
    }
    
bad:    
    if (read_writep != NULL) {
        if (read_writep->auio != NULL) {
            uio_free(read_writep->auio);
        }
        SMB_FREE(read_writep, M_SMBTEMP);
    }
    
	return error;
}
Пример #3
0
static int
vfs_mount_9p(mount_t mp, vnode_t devvp, user_addr_t data, vfs_context_t ctx)
{
#pragma unused(devvp)
	struct sockaddr *addr, *authaddr;
	struct vfsstatfs *sp;
	char authkey[DESKEYLEN+1];
	kauth_cred_t cred;
	user_args_9p args;
	mount_9p *nmp;
	size_t size;
	fid_9p fid;
	qid_9p qid;
	char *vers;
	int e;

	TRACE();
	nmp = NULL;
	addr = NULL;
	authaddr = NULL;
	fid = NOFID;

	if (vfs_isupdate(mp))
		return ENOTSUP;

	if (vfs_context_is64bit(ctx)) {
		if ((e=copyin(data, &args, sizeof(args))))
			goto error;
	} else {
		args_9p args32;
		if ((e=copyin(data, &args32, sizeof(args32))))
			goto error;
		args.spec			= CAST_USER_ADDR_T(args32.spec);
		args.addr			= CAST_USER_ADDR_T(args32.addr);
		args.addrlen		= args32.addrlen;
		args.authaddr		= CAST_USER_ADDR_T(args32.authaddr);
		args.authaddrlen	= args32.authaddrlen;
		args.volume			= CAST_USER_ADDR_T(args32.volume);
		args.uname			= CAST_USER_ADDR_T(args32.uname);
		args.aname			= CAST_USER_ADDR_T(args32.aname);
		args.authkey		= CAST_USER_ADDR_T(args32.authkey);
		args.flags			= args32.flags;
	}
	e = ENOMEM;
	nmp = malloc_9p(sizeof(*nmp));
	if (nmp == NULL)
		return e;

	nmp->mp = mp;
	TAILQ_INIT(&nmp->req);
	nmp->lck = lck_mtx_alloc_init(lck_grp_9p, LCK_ATTR_NULL);
	nmp->reqlck = lck_mtx_alloc_init(lck_grp_9p, LCK_ATTR_NULL);
	nmp->nodelck = lck_mtx_alloc_init(lck_grp_9p, LCK_ATTR_NULL);
	nmp->node = hashinit(desiredvnodes, M_TEMP, &nmp->nodelen);
	if (nmp->lck==NULL || nmp->reqlck==NULL || nmp->nodelck==NULL || nmp->node==NULL)
		goto error;

	if ((e=nameget_9p(args.volume, &nmp->volume)))
		goto error;
	if ((e=nameget_9p(args.uname, &nmp->uname)))
		goto error;
	if ((e=nameget_9p(args.aname, &nmp->aname)))
		goto error;

	cred = vfs_context_ucred(ctx);
	if (IS_VALID_CRED(cred)) {
		nmp->uid = kauth_cred_getuid(cred);
		nmp->gid = kauth_cred_getgid(cred);
	} else {
		nmp->uid = KAUTH_UID_NONE;
		nmp->gid = KAUTH_GID_NONE;
	}
	
	vfs_getnewfsid(mp);
	vfs_setfsprivate(mp, nmp);
	
	nmp->flags = args.flags;
	if ((e=addrget_9p(args.addr, args.addrlen, &addr)))
		goto error;
	if ((e=connect_9p(nmp, addr)))
		goto error;

	vers = VERSION9P;
	if (ISSET(nmp->flags, FLAG_DOTU))
		vers = VERSION9PDOTU;
	if ((e=version_9p(nmp, vers, &nmp->version)))
		goto error;
	if (ISSET(nmp->flags, FLAG_DOTU) && strcmp(VERSION9PDOTU, nmp->version)==0)
		SET(nmp->flags, F_DOTU);

	nmp->afid = NOFID;
	if (args.authaddr && args.authaddrlen && args.authkey) {
		if ((e=copyin(args.authkey, authkey, DESKEYLEN)))
			goto error;
		if ((e=addrget_9p(args.authaddr, args.authaddrlen, &authaddr)))
			goto error;
		if ((e=auth_9p(nmp, nmp->uname, nmp->aname, nmp->uid, &nmp->afid, &qid)))
			goto error;
		if (nmp->afid!=NOFID &&
			(e=authp9any_9p(nmp, nmp->afid, authaddr, nmp->uname, authkey)))
			goto error;
		bzero(authkey, DESKEYLEN);
	}
	if ((e=attach_9p(nmp, nmp->uname, nmp->aname, nmp->afid, nmp->uid, &fid, &qid)))
		goto error;

	if ((e=nget_9p(nmp, fid, qid, NULL, &nmp->root, NULL, ctx)))
		goto error;

	nunlock_9p(NTO9P(nmp->root));
	e = vnode_ref(nmp->root);
	vnode_put(nmp->root);
	if (e)
		goto error;

	vfs_setauthopaque(mp);
	vfs_clearauthopaqueaccess(mp);
	vfs_setlocklocal(mp);

	// init stats
	sp = vfs_statfs(nmp->mp);
	copyinstr(args.spec, sp->f_mntfromname, MNAMELEN-1, &size);
	bzero(sp->f_mntfromname+size, MNAMELEN-size);
	sp->f_bsize = PAGE_SIZE;
	sp->f_iosize = nmp->msize-IOHDRSZ;
	sp->f_blocks = sp->f_bfree = sp->f_bavail = sp->f_bused = 0;
	sp->f_files = 65535;
	sp->f_ffree = sp->f_files-2;
	sp->f_flags = vfs_flags(mp);
	
	free_9p(addr);
	free_9p(authaddr);
	return 0;

error:
	bzero(authkey, DESKEYLEN);
	free_9p(addr);
	free_9p(authaddr);
	if (nmp->so) {
		clunk_9p(nmp, fid);
		disconnect_9p(nmp);
	}
	freemount_9p(nmp);
	vfs_setfsprivate(mp, NULL);
	return e;
}
Пример #4
0
/*
 * Called from user land so we always have a reference on the share.
 */
int
smb_usr_query_dir(struct smb_share *share,
                  struct smb2ioc_query_dir *query_dir_ioc,
                  vfs_context_t context)
{
	int error;
 	struct smb2_query_dir_rq *queryp = NULL;

    SMB_MALLOC(queryp,
               struct smb2_query_dir_rq *,
               sizeof(struct smb2_query_dir_rq),
               M_SMBTEMP,
               M_WAITOK | M_ZERO);
    if (queryp == NULL) {
		SMBERROR("SMB_MALLOC failed\n");
        error = ENOMEM;
        goto bad;
    }
    
    /* Take 32 bit world pointers and convert them to user_addr_t. */
    if (query_dir_ioc->ioc_rcv_output_len > 0) {
        if (vfs_context_is64bit(context)) {
            queryp->rcv_output_uio = uio_create(1, 0, UIO_USERSPACE64, UIO_READ);
        }
        else {
            query_dir_ioc->ioc_kern_rcv_output =
            CAST_USER_ADDR_T(query_dir_ioc->ioc_rcv_output);
            queryp->rcv_output_uio = uio_create(1, 0, UIO_USERSPACE32, UIO_READ);
        }
        
        if (queryp->rcv_output_uio) {
            uio_addiov(queryp->rcv_output_uio,
                       query_dir_ioc->ioc_kern_rcv_output,
                       query_dir_ioc->ioc_rcv_output_len);
        }
        else {
            SMBERROR("uio_create failed\n");
            error = ENOMEM;
            goto bad;
        }
    }

	/* Take the 32 bit world pointers and convert them to user_addr_t. */
	if (!vfs_context_is64bit (context)) {
		query_dir_ioc->ioc_kern_name = CAST_USER_ADDR_T(query_dir_ioc->ioc_name);
	}
	
	/* ioc_name_len includes the null byte, ioc_kern_name is a c-style string */
	if (query_dir_ioc->ioc_kern_name && query_dir_ioc->ioc_name_len) {
        queryp->name_len = query_dir_ioc->ioc_name_len;
		queryp->namep = smb_memdupin(query_dir_ioc->ioc_kern_name,
                                      query_dir_ioc->ioc_name_len);
		if (queryp->namep == NULL) {
            SMBERROR("smb_memdupin failed\n");
			error = ENOMEM;
			goto bad;
		}
	}
    
	queryp->file_info_class = query_dir_ioc->ioc_file_info_class;
	queryp->flags = query_dir_ioc->ioc_flags;
	queryp->file_index = query_dir_ioc->ioc_file_index;
	queryp->output_buffer_len = query_dir_ioc->ioc_rcv_output_len;
	queryp->fid = query_dir_ioc->ioc_fid;
	queryp->name_len = query_dir_ioc->ioc_name_len;
	queryp->name_flags = query_dir_ioc->ioc_name_flags;
    /* 
     * Never used for user ioctl query dir. User must have already opened
     * the dir to be searched.
     */
	queryp->dnp = NULL; 
    
    /* 
     * Since this is from user space, there is no mounted file system, so 
     * there are no vnodes and thus no queryp->dnp. This means that namep 
     * must be non NULL.
     *
     * If ioc_rcv_output_len is not 0, then copy results directly to user 
     * buffer and let them parse it.
     */
    if ((queryp->namep == NULL) || (queryp->name_len == 0)) {
        SMBERROR("missing name \n");
        error = EINVAL;
        goto bad;
    }
    
	/* Now do the real work */
	error = smb2_smb_query_dir(share, queryp, NULL, context);
    
    /* always return the ntstatus error */
    query_dir_ioc->ioc_ret_ntstatus = queryp->ret_ntstatus;
	if (error) {
		goto bad;
	}
    
    /* Fill in amount of data returned in Query Dir reply */
    query_dir_ioc->ioc_ret_output_len = queryp->ret_buffer_len;
    
    /* Fill in actual amount of data returned */
    query_dir_ioc->ioc_rcv_output_len = queryp->output_buffer_len;
    
bad:
    if (queryp != NULL) {
        if (queryp->ret_rqp != NULL) {
            smb_rq_done(queryp->ret_rqp);
        }
        if (queryp->namep)
            SMB_FREE(queryp->namep, M_SMBSTR);
        if (queryp->rcv_output_uio != NULL) {
            uio_free(queryp->rcv_output_uio);
        }
        SMB_FREE(queryp, M_SMBTEMP);
    }
    
	return error;
}
Пример #5
0
/*
 * Called from user land so we always have a reference on the share.
 */
int 
smb_usr_check_dir(struct smb_share *share, struct smb_vc *vcp,
                  struct smb2ioc_check_dir *check_dir_ioc,
                  vfs_context_t context)
{
	int error;
    char *local_pathp = NULL;
    uint32_t local_path_len = check_dir_ioc->ioc_path_len;
    uint32_t desired_access = SMB2_FILE_READ_ATTRIBUTES | SMB2_SYNCHRONIZE;
    uint32_t share_access = NTCREATEX_SHARE_ACCESS_ALL;
    /* Tell create, the namep is a path to an item */
    uint64_t create_flags = SMB2_CREATE_NAME_IS_PATH;
    struct smbfattr *fap = NULL;
	size_t network_path_len = PATH_MAX + 1;
	char *network_pathp = NULL;
    
    /* Assume no error */
    check_dir_ioc->ioc_ret_ntstatus = 0;
        
    /* 
     * Compound Create/Close call should be sufficient. 
     * If item exists, verify it is a dir.
     */
    
	/* Take the 32 bit world pointers and convert them to user_addr_t. */
	if (!vfs_context_is64bit(context)) {
		check_dir_ioc->ioc_kern_path = CAST_USER_ADDR_T(check_dir_ioc->ioc_path);
	}
	
    if (!(check_dir_ioc->ioc_kern_path)) {
        error = EINVAL;
        goto bad;
    }

    /* local_path_len includes the null byte, ioc_kern_path is a c-style string */
	if (check_dir_ioc->ioc_kern_path && local_path_len) {
		local_pathp = smb_memdupin(check_dir_ioc->ioc_kern_path,
                                   local_path_len);
	}

    if (local_pathp == NULL) {
        SMBERROR("smb_memdupin failed\n");
        error = ENOMEM;
        goto bad;
    }

    /*
     * Need to convert from local path to a network path 
     */
	SMB_MALLOC(network_pathp, char *, network_path_len, M_TEMP, M_WAITOK | M_ZERO);
	if (network_pathp == NULL) {
		error = ENOMEM;
		goto bad;		
	}

	error = smb_convert_path_to_network(local_pathp, local_path_len,
                                        network_pathp, &network_path_len,
										'\\', SMB_UTF_SFM_CONVERSIONS,
                                        SMB_UNICODE_STRINGS(vcp));
	if (error) {
		SMBERROR("smb_convert_path_to_network failed : %d\n", error);
		goto bad;
	}
    
    /* 
     * Set up for Compound Create/Close call 
     */
    SMB_MALLOC(fap,
               struct smbfattr *, 
               sizeof(struct smbfattr), 
               M_SMBTEMP, 
               M_WAITOK | M_ZERO);
    if (fap == NULL) {
        SMBERROR("SMB_MALLOC failed\n");
        error = ENOMEM;
        goto bad;
    }

    /* Send a Create/Close */
    error = smb2fs_smb_cmpd_create(share, NULL,
                                   network_pathp, network_path_len,
                                   NULL, 0,
                                   desired_access, VDIR,
                                   share_access, FILE_OPEN,
                                   create_flags, &check_dir_ioc->ioc_ret_ntstatus,
                                   NULL, fap,
                                   NULL, context);
	if (error) {
		goto bad;
	}
    
    /* found something, verify its a dir */
    if (!(fap->fa_attr & SMB_EFA_DIRECTORY)) {
        error = ENOTDIR;
        check_dir_ioc->ioc_ret_ntstatus = STATUS_NOT_A_DIRECTORY;
    }

bad:    
    if (local_pathp) {
        SMB_FREE(local_pathp, M_SMBSTR);
    }
    
    if (network_pathp) {
        SMB_FREE(network_pathp, M_SMBSTR);
    }

    if (fap) {
        SMB_FREE(fap, M_SMBTEMP);
    }

	return error;
}
Пример #6
0
int
smb_usr_ioctl(struct smb_share *share, struct smb_vc *vcp,
              struct smb2ioc_ioctl *ioctl_ioc, vfs_context_t context)
{
	int error;
 	struct smb2_ioctl_rq *ioctlp = NULL;
    
    SMB_MALLOC(ioctlp,
               struct smb2_ioctl_rq *, 
               sizeof(struct smb2_ioctl_rq), 
               M_SMBTEMP, 
               M_WAITOK | M_ZERO);
    if (ioctlp == NULL) {
		SMBERROR("SMB_MALLOC failed\n");
        error = ENOMEM;
        goto bad;
    }
    
again:
    ioctlp->share = share;
    ioctlp->ctl_code = ioctl_ioc->ioc_ctl_code;
    ioctlp->fid = ioctl_ioc->ioc_fid;
    
	ioctlp->snd_input_len = ioctl_ioc->ioc_snd_input_len;
	ioctlp->snd_output_len = ioctl_ioc->ioc_snd_output_len;
	ioctlp->rcv_input_len = ioctl_ioc->ioc_rcv_input_len;

    /* Handle servers that dislike large output buffer lengths */
    if (vcp->vc_misc_flags & SMBV_63K_IOCTL) {
        ioctlp->rcv_output_len = kSMB_63K;
    }
    else {
        ioctlp->rcv_output_len = ioctl_ioc->ioc_rcv_output_len;
    }

    /* Take 32 bit world pointers and convert them to user_addr_t. */
    if (ioctl_ioc->ioc_snd_input_len > 0) {
        if (vfs_context_is64bit(context)) {
            ioctlp->snd_input_uio = uio_create(1, 0, UIO_USERSPACE64, UIO_WRITE);
        }
        else {
            ioctl_ioc->ioc_kern_snd_input = 
                CAST_USER_ADDR_T(ioctl_ioc->ioc_snd_input);
            ioctlp->snd_input_uio = uio_create(1, 0, UIO_USERSPACE32, UIO_WRITE);
        }
        
        if (ioctlp->snd_input_uio) {
            uio_addiov(ioctlp->snd_input_uio,
                       ioctl_ioc->ioc_kern_snd_input, 
                       ioctl_ioc->ioc_snd_input_len);
        } 
        else {
            SMBERROR("uio_create failed\n");
            error = ENOMEM;
            goto bad;
        }
    }
    
    /* Take 32 bit world pointers and convert them to user_addr_t. */
    if (ioctl_ioc->ioc_snd_output_len > 0) {
        if (vfs_context_is64bit(context)) {
            ioctlp->snd_output_uio = uio_create(1, 0, UIO_USERSPACE64, UIO_WRITE);
        }
        else {
            ioctl_ioc->ioc_kern_snd_output =  
                CAST_USER_ADDR_T(ioctl_ioc->ioc_snd_output);
            ioctlp->snd_output_uio = uio_create(1, 0, UIO_USERSPACE32, UIO_WRITE);
        }
        
        if (ioctlp->snd_output_uio) {
            uio_addiov(ioctlp->snd_output_uio,
                       ioctl_ioc->ioc_kern_snd_output, 
                       ioctl_ioc->ioc_snd_output_len);
        } 
        else {
            SMBERROR("uio_create failed\n");
            error = ENOMEM;
            goto bad;
        }
    }
    
    /* Take 32 bit world pointers and convert them to user_addr_t. */
    if (ioctl_ioc->ioc_rcv_input_len > 0) {
        if (vfs_context_is64bit(context)) {
            ioctlp->rcv_input_uio = uio_create(1, 0, UIO_USERSPACE64, UIO_READ);
        }
        else {
            ioctl_ioc->ioc_kern_rcv_input = 
                CAST_USER_ADDR_T(ioctl_ioc->ioc_rcv_input);
            ioctlp->rcv_input_uio = uio_create(1, 0, UIO_USERSPACE32, UIO_READ);
        }
        
        if (ioctlp->rcv_input_uio) {
            uio_addiov(ioctlp->rcv_input_uio,
                       ioctl_ioc->ioc_kern_rcv_input, 
                       ioctl_ioc->ioc_rcv_input_len);
        } 
        else {
            SMBERROR("uio_create failed\n");
            error = ENOMEM;
            goto bad;
        }
    }

    /* Take 32 bit world pointers and convert them to user_addr_t. */
    if (ioctl_ioc->ioc_rcv_output_len > 0) {
        if (vfs_context_is64bit(context)) {
            ioctlp->rcv_output_uio = uio_create(1, 0, UIO_USERSPACE64, UIO_READ);
        }
        else {
            ioctl_ioc->ioc_kern_rcv_output = 
                CAST_USER_ADDR_T(ioctl_ioc->ioc_rcv_output);
            ioctlp->rcv_output_uio = uio_create(1, 0, UIO_USERSPACE32, UIO_READ);
        }
        
        if (ioctlp->rcv_output_uio) {
            uio_addiov(ioctlp->rcv_output_uio,
                       ioctl_ioc->ioc_kern_rcv_output, 
                       ioctl_ioc->ioc_rcv_output_len);
        } 
        else {
            SMBERROR("uio_create failed\n");
            error = ENOMEM;
            goto bad;
        }
    }
    
    /* Now do the real work */
	error = smb2_smb_ioctl(share, ioctlp, NULL, context);

    if ((error) &&
        (ioctlp->ret_ntstatus == STATUS_INVALID_PARAMETER) &&
        !(vcp->vc_misc_flags & SMBV_63K_IOCTL)) {
        /*
         * <14281932> Could this be a server that can not handle
         * larger than 65535 bytes in an IOCTL?
         */
        SMBWARNING("SMB 2/3 server cant handle large OutputBufferLength in IOCTL. Reducing to 63Kb.\n");
        vcp->vc_misc_flags |= SMBV_63K_IOCTL;
        
        ioctlp->ret_ntstatus = 0;
        
        if (ioctlp->snd_input_uio != NULL) {
            uio_free(ioctlp->snd_input_uio);
            ioctlp->snd_input_uio = NULL;
        }
        if (ioctlp->snd_output_uio != NULL) {
            uio_free(ioctlp->snd_output_uio);
            ioctlp->snd_output_uio = NULL;
        }
        if (ioctlp->rcv_input_uio != NULL) {
            uio_free(ioctlp->rcv_input_uio);
            ioctlp->rcv_input_uio = NULL;
        }
        if (ioctlp->rcv_output_uio != NULL) {
            uio_free(ioctlp->rcv_output_uio);
            ioctlp->rcv_output_uio = NULL;
        }

        goto again;
    }

    /* always return the ntstatus error */
    ioctl_ioc->ioc_ret_ntstatus = ioctlp->ret_ntstatus;
	if (error) {
		goto bad;
	}
    
    /* Fill in actual bytes returned */
    ioctl_ioc->ioc_ret_input_len = ioctlp->ret_input_len;
    ioctl_ioc->ioc_ret_output_len = ioctlp->ret_output_len;
    
bad:    
    if (ioctlp != NULL) {
        if (ioctlp->snd_input_uio != NULL) {
            uio_free(ioctlp->snd_input_uio);
        }
        if (ioctlp->snd_output_uio != NULL) {
            uio_free(ioctlp->snd_output_uio);
        }
        if (ioctlp->rcv_input_uio != NULL) {
            uio_free(ioctlp->rcv_input_uio);
        }
        if (ioctlp->rcv_output_uio != NULL) {
            uio_free(ioctlp->rcv_output_uio);
        }
        SMB_FREE(ioctlp, M_SMBTEMP);
    }
    
    return error;
}
Пример #7
0
int
smb_usr_get_dfs_referral(struct smb_share *share, struct smb_vc *vcp,
                         struct smb2ioc_get_dfs_referral *get_dfs_refer_ioc,
                         vfs_context_t context)
{
	int error;
 	struct smb2_ioctl_rq *ioctlp = NULL;
 	struct smb2_get_dfs_referral dfs_referral;
    char *local_pathp = NULL;
    uint32_t local_path_len = get_dfs_refer_ioc->ioc_file_name_len;
	size_t network_path_len = PATH_MAX + 1;
	char *network_pathp = NULL;
    
    SMB_MALLOC(ioctlp,
               struct smb2_ioctl_rq *, 
               sizeof(struct smb2_ioctl_rq), 
               M_SMBTEMP, 
               M_WAITOK | M_ZERO);
    if (ioctlp == NULL) {
		SMBERROR("SMB_MALLOC failed\n");
        error = ENOMEM;
        goto bad;
    }

again:
	/* Take the 32 bit world pointers and convert them to user_addr_t. */
    bzero(&dfs_referral, sizeof(dfs_referral));

    dfs_referral.file_namep = NULL;
    dfs_referral.max_referral_level = get_dfs_refer_ioc->ioc_max_referral_level;
    
	if (!vfs_context_is64bit (context)) {
		get_dfs_refer_ioc->ioc_kern_file_name = CAST_USER_ADDR_T(get_dfs_refer_ioc->ioc_file_name);
	}
	
    if (!(get_dfs_refer_ioc->ioc_kern_file_name)) {
        error = EINVAL;
        goto bad;
    }

	/* ioc_file_name_len includes the null byte, ioc_kern_file_name is a c-style string */
	if (get_dfs_refer_ioc->ioc_kern_file_name && get_dfs_refer_ioc->ioc_file_name_len) {
		local_pathp = smb_memdupin(get_dfs_refer_ioc->ioc_kern_file_name,
                                   local_path_len);
	}

    if (local_pathp == NULL) {
        SMBERROR("smb_memdupin failed\n");
        error = ENOMEM;
        goto bad;
    }

    /*
     * Need to convert from local path to a network path 
     */
	SMB_MALLOC(network_pathp, char *, network_path_len, M_TEMP, M_WAITOK | M_ZERO);
	if (network_pathp == NULL) {
		error = ENOMEM;
		goto bad;		
	}
    
	error = smb_convert_path_to_network(local_pathp, local_path_len,
                                        network_pathp, &network_path_len,
										'\\', SMB_UTF_SFM_CONVERSIONS,
                                        SMB_UNICODE_STRINGS(vcp));
	if (error) {
		SMBERROR("smb_convert_path_to_network failed : %d\n", error);
		goto bad;
	}

    dfs_referral.file_namep = network_pathp;
    dfs_referral.file_name_len = (uint32_t) network_path_len;
    
    /* Take 32 bit world pointers and convert them to user_addr_t. */
    if (get_dfs_refer_ioc->ioc_rcv_output_len > 0) {
        if (vfs_context_is64bit(context)) {
            ioctlp->rcv_output_uio = uio_create(1, 0, UIO_USERSPACE64, UIO_READ);
        }
        else {
            get_dfs_refer_ioc->ioc_kern_rcv_output = CAST_USER_ADDR_T(get_dfs_refer_ioc->ioc_rcv_output);
            ioctlp->rcv_output_uio = uio_create(1, 0, UIO_USERSPACE32, UIO_READ);
        }
        
        if (ioctlp->rcv_output_uio) {
            uio_addiov(ioctlp->rcv_output_uio,
                       get_dfs_refer_ioc->ioc_kern_rcv_output, 
                       get_dfs_refer_ioc->ioc_rcv_output_len);
        } 
        else {
            error = ENOMEM;
            SMBERROR("uio_create failed\n");
            goto bad;
        }
    }

    ioctlp->share = share;
    ioctlp->ctl_code = FSCTL_DFS_GET_REFERRALS;
    ioctlp->fid = -1;
    
	ioctlp->snd_input_buffer = (uint8_t *) &dfs_referral;
	ioctlp->snd_input_len = sizeof(struct smb2_get_dfs_referral);
	ioctlp->snd_output_len = 0;
    
	ioctlp->rcv_input_len = 0;
    
    /* Handle servers that dislike large output buffer lengths */
    if (vcp->vc_misc_flags & SMBV_63K_IOCTL) {
        ioctlp->rcv_output_len = kSMB_63K;
    }
    else {
        ioctlp->rcv_output_len = get_dfs_refer_ioc->ioc_rcv_output_len;
    }

    /* Now do the real work */
	error = smb2_smb_ioctl(share, ioctlp, NULL, context);
    
    if ((error) &&
        (ioctlp->ret_ntstatus == STATUS_INVALID_PARAMETER) &&
        !(vcp->vc_misc_flags & SMBV_63K_IOCTL)) {
        /*
         * <14281932> Could this be a server that can not handle
         * larger than 65535 bytes in an IOCTL? 
         */
        SMBWARNING("SMB 2/3 server cant handle large OutputBufferLength in DFS Referral. Reducing to 63Kb.\n");
        vcp->vc_misc_flags |= SMBV_63K_IOCTL;
        
        ioctlp->ret_ntstatus = 0;
        
        if (ioctlp->snd_input_uio != NULL) {
            uio_free(ioctlp->snd_input_uio);
            ioctlp->snd_input_uio = NULL;
        }
        if (ioctlp->snd_output_uio != NULL) {
            uio_free(ioctlp->snd_output_uio);
            ioctlp->snd_output_uio = NULL;
        }
        if (ioctlp->rcv_input_uio != NULL) {
            uio_free(ioctlp->rcv_input_uio);
            ioctlp->rcv_input_uio = NULL;
        }
        if (ioctlp->rcv_output_uio != NULL) {
            uio_free(ioctlp->rcv_output_uio);
            ioctlp->rcv_output_uio = NULL;
        }

        goto again;
    }    
    
    /* always return the ntstatus error */
    get_dfs_refer_ioc->ioc_ret_ntstatus = ioctlp->ret_ntstatus;
	if (error) {
		goto bad;
	}
    
    /* Fill in actual bytes returned */
    get_dfs_refer_ioc->ioc_ret_output_len = ioctlp->ret_output_len;
    
bad:    
    if (ioctlp != NULL) {
        if (ioctlp->snd_input_uio != NULL) {
            uio_free(ioctlp->snd_input_uio);
        }
        if (ioctlp->snd_output_uio != NULL) {
            uio_free(ioctlp->snd_output_uio);
        }
        if (ioctlp->rcv_input_uio != NULL) {
            uio_free(ioctlp->rcv_input_uio);
        }
        if (ioctlp->rcv_output_uio != NULL) {
            uio_free(ioctlp->rcv_output_uio);
        }
        SMB_FREE(ioctlp, M_SMBTEMP);
    }
    
    if (local_pathp) {
        SMB_FREE(local_pathp, M_SMBSTR);
    }
    
    if (network_pathp) {
        SMB_FREE(network_pathp, M_SMBSTR);
    }

    return error;
}
Пример #8
0
/*
 * Called from user land so we always have a reference on the share.
 */
int 
smb_usr_create(struct smb_share *share, struct smb2ioc_create *create_ioc,
               vfs_context_t context)
{
	int error;
 	struct smb2_create_rq *createp = NULL;
    
    SMB_MALLOC(createp, 
               struct smb2_create_rq *, 
               sizeof(struct smb2_create_rq), 
               M_SMBTEMP, 
               M_WAITOK | M_ZERO);
    if (createp == NULL) {
		SMBERROR("SMB_MALLOC failed\n");
        error = ENOMEM;
        goto bad;
    }
    
	/* Take the 32 bit world pointers and convert them to user_addr_t. */
	if (!vfs_context_is64bit (context)) {
		create_ioc->ioc_kern_name = CAST_USER_ADDR_T(create_ioc->ioc_name);
	}
	
	/* ioc_name_len includes the null byte, ioc_kern_name is a c-style string */
	if (create_ioc->ioc_kern_name && create_ioc->ioc_name_len) {
        createp->name_len = create_ioc->ioc_name_len;
		createp->namep = smb_memdupin(create_ioc->ioc_kern_name, 
                                   create_ioc->ioc_name_len);
		if (createp->namep == NULL) {
            SMBERROR("smb_memdupin failed\n");
			error = ENOMEM;
			goto bad;
		}
	}
    
    createp->flags = SMB2_CREATE_GET_MAX_ACCESS;
    createp->oplock_level = create_ioc->ioc_oplock_level;
    createp->impersonate_level = create_ioc->ioc_impersonate_level;
    createp->desired_access = create_ioc->ioc_desired_access;
    createp->file_attributes = create_ioc->ioc_file_attributes;
    createp->share_access = create_ioc->ioc_share_access;
    createp->disposition = create_ioc->ioc_disposition;
    createp->create_options = create_ioc->ioc_create_options;
    createp->dnp = NULL;
    
	/* Now do the real work */
	error = smb2_smb_create(share, createp, NULL, context);
    
    /* always return the ntstatus error */
    create_ioc->ioc_ret_ntstatus = createp->ret_ntstatus;
	if (error) {
		goto bad;
	}
    
    /* Fill in return parameters */
    create_ioc->ioc_ret_attributes = createp->ret_attributes;
    create_ioc->ioc_ret_oplock_level = createp->ret_oplock_level;
    create_ioc->ioc_ret_create_action = createp->ret_create_action;
    create_ioc->ioc_ret_create_time = createp->ret_create_time;
    create_ioc->ioc_ret_access_time = createp->ret_access_time;
    create_ioc->ioc_ret_write_time = createp->ret_write_time;
    create_ioc->ioc_ret_change_time = createp->ret_change_time;
    create_ioc->ioc_ret_alloc_size = createp->ret_alloc_size;
    create_ioc->ioc_ret_eof = createp->ret_eof;
    create_ioc->ioc_ret_fid = createp->ret_fid;
    create_ioc->ioc_ret_max_access = createp->ret_max_access;
    
bad:    
    if (createp != NULL) {
        if (createp->namep)
            SMB_FREE(createp->namep, M_SMBSTR);
        SMB_FREE(createp, M_SMBTEMP);
    }
    
	return error;
}