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); }
/* * 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; }
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; }
/* * 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; }
/* * 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; }
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; }
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; }
/* * 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; }