void smb_rq_wend(struct smb_rq *rqp) { if (rqp->sr_wcount == NULL) { SMBERROR("no wcount\n"); /* actually panic */ return; } if (rqp->sr_rq.mb_count & 1) SMBERROR("odd word count\n"); *rqp->sr_wcount = rqp->sr_rq.mb_count / 2; }
static int smbfs_node_alloc(struct mount *mp, struct vnode *dvp, const char *name, int nmlen, struct smbfattr *fap, struct vnode **vpp) { struct thread *td = curthread; /* XXX */ struct smbmount *smp = VFSTOSMBFS(mp); struct smbnode_hashhead *nhpp; struct smbnode *np, *np2, *dnp; struct vnode *vp; u_long hashval; int error; *vpp = NULL; if (smp->sm_root != NULL && dvp == NULL) { SMBERROR("do not allocate root vnode twice!\n"); return EINVAL; } if (nmlen == 2 && bcmp(name, "..", 2) == 0) { if (dvp == NULL) return EINVAL; vp = VTOSMB(dvp)->n_parent->n_vnode; error = vget(vp, LK_EXCLUSIVE, td); if (error == 0) *vpp = vp; return error; } else if (nmlen == 1 && name[0] == '.') { SMBERROR("do not call me with dot!\n"); return EINVAL; } dnp = dvp ? VTOSMB(dvp) : NULL; if (dnp == NULL && dvp != NULL) { vprint("smbfs_node_alloc: dead parent vnode", dvp); return EINVAL; } hashval = smbfs_hash(name, nmlen); retry: smbfs_hash_lock(smp, td); loop: nhpp = SMBFS_NOHASH(smp, hashval); LIST_FOREACH(np, nhpp, n_hash) { vp = SMBTOV(np); if (np->n_parent != dnp || np->n_nmlen != nmlen || bcmp(name, np->n_name, nmlen) != 0) continue; VI_LOCK(vp); smbfs_hash_unlock(smp, td); if (vget(vp, LK_EXCLUSIVE | LK_INTERLOCK, td) != 0) goto retry; *vpp = vp; return 0; }
void smb_rq_bend(struct smb_rq *rqp) { int bcnt; if (rqp->sr_bcount == NULL) { SMBERROR("no bcount\n"); /* actually panic */ return; } bcnt = rqp->sr_rq.mb_count; if (bcnt > 0xffff) SMBERROR("byte count too large (%d)\n", bcnt); le16enc(rqp->sr_bcount, bcnt); }
int smb_write(struct smb_share *ssp, u_int16_t fid, struct uio *uio, struct smb_cred *scred) { int error = 0, len, tsize, resid; struct uio olduio; /* * review: manage iov more precisely */ if (uio->uio_iovcnt != 1) { SMBERROR("can't handle iovcnt > 1\n"); return EIO; } tsize = uio->uio_resid; olduio = *uio; while (tsize > 0) { len = tsize; error = smb_smb_write(ssp, fid, &len, &resid, uio, scred); if (error) break; if (resid < len) { error = EIO; break; } tsize -= resid; } if (error) { *uio = olduio; } return error; }
int smbfs_writevnode(struct vnode *vp, struct uio *uiop, struct ucred *cred, int ioflag) { struct smbmount *smp = VTOSMBFS(vp); struct smbnode *np = VTOSMB(vp); struct smb_cred *scred; struct thread *td; int error = 0; if (vp->v_type != VREG) { SMBERROR("vn types other than VREG unsupported !\n"); return EIO; } SMBVDEBUG("ofs=%jd,resid=%zd\n", (intmax_t)uiop->uio_offset, uiop->uio_resid); if (uiop->uio_offset < 0) return EINVAL; /* if (uiop->uio_offset + uiop->uio_resid > smp->nm_maxfilesize) return (EFBIG);*/ td = uiop->uio_td; if (ioflag & (IO_APPEND | IO_SYNC)) { if (np->n_flag & NMODIFIED) { smbfs_attr_cacheremove(vp); error = smbfs_vinvalbuf(vp, td); if (error) return error; } if (ioflag & IO_APPEND) { #ifdef notyet /* * File size can be changed by another client */ smbfs_attr_cacheremove(vp); error = VOP_GETATTR(vp, &vattr, cred); if (error) return (error); #endif uiop->uio_offset = np->n_size; } } if (uiop->uio_resid == 0) return 0; if (vn_rlimit_fsize(vp, uiop, td)) return (EFBIG); scred = smbfs_malloc_scred(); smb_makescred(scred, td, cred); error = smb_write(smp->sm_share, np->n_fid, uiop, scred); smbfs_free_scred(scred); SMBVDEBUG("after: ofs=%jd,resid=%zd\n", (intmax_t)uiop->uio_offset, uiop->uio_resid); if (!error) { if (uiop->uio_offset > np->n_size) { np->n_size = uiop->uio_offset; vnode_pager_setsize(vp, np->n_size); } } return error; }
uint64_t smb2fs_smb_file_id_get(struct smbmount *smp, uint64_t ino, char *name) { uint64_t ret_ino; if (ino == smp->sm_root_ino) { /* If its the root File ID, then return SMBFS_ROOT_INO */ ret_ino = SMBFS_ROOT_INO; } else { /* * If actual File ID is SMBFS_ROOT_INO, then return the root File ID * instead. */ if (ino == SMBFS_ROOT_INO) { ret_ino = smp->sm_root_ino; } else { if (ino == 0) { /* This should never happen */ SMBERROR("File ID of 0 in <%s>? \n", ((name != NULL) ? name : "unknown name")); ret_ino = SMBFS_ROOT_INO; } else { ret_ino = ino; } } } return (ret_ino); }
static int smb_smb_nomux(struct smb_vc *vcp, struct smb_cred *scred, const char *name) { if (scred->scr_td == vcp->vc_iod->iod_td) return 0; SMBERROR("wrong function called(%s)\n", name); return EINVAL; }
int smbfs_writevnode(struct vnode *vp, struct uio *uiop, struct ucred *cred, int ioflag) { struct thread *td; struct smbmount *smp = VTOSMBFS(vp); struct smbnode *np = VTOSMB(vp); struct smb_cred scred; int error = 0; if (vp->v_type != VREG) { SMBERROR("vn types other than VREG unsupported !\n"); return EIO; } SMBVDEBUG("ofs=%d,resid=%d\n",(int)uiop->uio_offset, uiop->uio_resid); if (uiop->uio_offset < 0) return EINVAL; td = uiop->uio_td; if (ioflag & (IO_APPEND | IO_SYNC)) { if (np->n_flag & NMODIFIED) { smbfs_attr_cacheremove(vp); error = smbfs_vinvalbuf(vp, V_SAVE, 1); if (error) return error; } if (ioflag & IO_APPEND) { #if 0 /* notyet */ /* * File size can be changed by another client */ smbfs_attr_cacheremove(vp); error = VOP_GETATTR(vp, &vattr); if (error) return (error); #endif uiop->uio_offset = np->n_size; } } if (uiop->uio_resid == 0) return 0; if (td->td_proc && uiop->uio_offset + uiop->uio_resid > td->td_proc->p_rlimit[RLIMIT_FSIZE].rlim_cur) { lwpsignal(td->td_proc, td->td_lwp, SIGXFSZ); return EFBIG; } smb_makescred(&scred, td, cred); error = smb_write(smp->sm_share, np->n_fid, uiop, &scred); SMBVDEBUG("after: ofs=%d,resid=%d\n",(int)uiop->uio_offset, uiop->uio_resid); if (!error) { if (uiop->uio_offset > np->n_size) { np->n_size = uiop->uio_offset; vnode_pager_setsize(vp, np->n_size); } } return error; }
static int smb_rq_getenv(struct smb_connobj *layer, struct smb_vc **vcpp, struct smb_share **sspp) { struct smb_vc *vcp = NULL; struct smb_share *ssp = NULL; struct smb_connobj *cp; int error = 0; switch (layer->co_level) { case SMBL_VC: vcp = CPTOVC(layer); if (layer->co_parent == NULL) { SMBERROR("zombie VC %s\n", vcp->vc_srvname); error = EINVAL; break; } break; case SMBL_SHARE: ssp = CPTOSS(layer); cp = layer->co_parent; if (cp == NULL) { SMBERROR("zombie share %s\n", ssp->ss_name); error = EINVAL; break; } error = smb_rq_getenv(cp, &vcp, NULL); if (error) break; break; default: SMBERROR("invalid layer %d passed\n", layer->co_level); error = EINVAL; } if (vcpp) *vcpp = vcp; if (sspp) *sspp = ssp; return error; }
/* * Set DOS file attributes. mtime should be NULL for dialects above lm10 */ int smbfs_smb_setpattr(struct smbnode *np, u_int16_t attr, struct timespec *mtime, struct smb_cred *scred) { struct smb_rq *rqp; struct smb_share *ssp = np->n_mount->sm_share; struct mbchain *mbp; u_long time; int error, svtz; rqp = malloc(sizeof(struct smb_rq), M_SMBFSDATA, M_WAITOK); error = smb_rq_init(rqp, SSTOCP(ssp), SMB_COM_SET_INFORMATION, scred); if (error) { free(rqp, M_SMBFSDATA); return error; } svtz = SSTOVC(ssp)->vc_sopt.sv_tz; smb_rq_getrequest(rqp, &mbp); smb_rq_wstart(rqp); mb_put_uint16le(mbp, attr); if (mtime) { smb_time_local2server(mtime, svtz, &time); } else time = 0; mb_put_uint32le(mbp, time); /* mtime */ mb_put_mem(mbp, NULL, 5 * 2, MB_MZERO); smb_rq_wend(rqp); smb_rq_bstart(rqp); mb_put_uint8(mbp, SMB_DT_ASCII); do { error = smbfs_fullpath(mbp, SSTOVC(ssp), np, NULL, 0); if (error) break; mb_put_uint8(mbp, SMB_DT_ASCII); if (SMB_UNICODE_STRINGS(SSTOVC(ssp))) { mb_put_padbyte(mbp); mb_put_uint8(mbp, 0); /* 1st byte of NULL Unicode char */ } mb_put_uint8(mbp, 0); smb_rq_bend(rqp); error = smb_rq_simple(rqp); if (error) { SMBERROR("smb_rq_simple(rqp) => error %d\n", error); break; } } while(0); smb_rq_done(rqp); free(rqp, M_SMBFSDATA); return error; }
static int nsmb_dev_open_nolock(dev_t dev, int oflags, int devtype, struct proc *p) { #pragma unused(oflags, devtype, p) struct smb_dev *sdp; kauth_cred_t cred = vfs_context_ucred(vfs_context_current()); sdp = SMB_GETDEV(dev); if (sdp && (sdp->sd_flags & NSMBFL_OPEN)) return (EBUSY); if (!sdp || minor(dev) == 0) { int avail_minor; for (avail_minor = 1; avail_minor < SMBMINORS; avail_minor++) if (!SMB_GETDEV(avail_minor)) break; if (avail_minor >= SMBMINORS) { SMBERROR("Too many minor devices, %d >= %d !", avail_minor, SMBMINORS); return (ENOMEM); } SMB_MALLOC(sdp, struct smb_dev *, sizeof(*sdp), M_NSMBDEV, M_WAITOK); bzero(sdp, sizeof(*sdp)); dev = makedev(smb_major, avail_minor); sdp->sd_devfs = devfs_make_node(dev, DEVFS_CHAR, kauth_cred_getuid(cred), kauth_cred_getgid(cred), 0700, "nsmb%x", avail_minor); if (!sdp->sd_devfs) { SMBERROR("devfs_make_node failed %d\n", avail_minor); SMB_FREE(sdp, M_NSMBDEV); return (ENOMEM); } if (avail_minor > smb_minor_hiwat) smb_minor_hiwat = avail_minor; SMB_GETDEV(dev) = sdp; return (EBUSY); }
/* * Called from user land so we always have a reference on the share. */ int smb_usr_close(struct smb_share *share, struct smb2ioc_close *close_ioc, vfs_context_t context) { int error; struct smb2_close_rq *closep = NULL; SMB_MALLOC(closep, struct smb2_close_rq *, sizeof(struct smb2_close_rq), M_SMBTEMP, M_WAITOK | M_ZERO); if (closep == NULL) { SMBERROR("SMB_MALLOC failed\n"); error = ENOMEM; goto bad; } closep->share = share; closep->flags = close_ioc->ioc_flags; closep->fid = close_ioc->ioc_fid; /* Now do the real work */ error = smb2_smb_close(share, closep, NULL, context); /* always return the ntstatus error */ close_ioc->ioc_ret_ntstatus = closep->ret_ntstatus; if (error) { goto bad; } /* Fill in return parameters if they wanted them */ if (close_ioc->ioc_flags & SMB2_CLOSE_FLAG_POSTQUERY_ATTRIB) { close_ioc->ioc_ret_attributes = closep->ret_attributes; close_ioc->ioc_ret_create_time = closep->ret_create_time; close_ioc->ioc_ret_access_time = closep->ret_access_time; close_ioc->ioc_ret_write_time = closep->ret_write_time; close_ioc->ioc_ret_change_time = closep->ret_change_time; close_ioc->ioc_ret_alloc_size = closep->ret_alloc_size; close_ioc->ioc_ret_eof = closep->ret_eof; } bad: if (closep != NULL) { SMB_FREE(closep, M_SMBTEMP); } return error; }
static int smb_usr_vc2spec(struct smbioc_ossn *dp, struct smb_vcspec *spec) { int flags = 0; bzero(spec, sizeof(*spec)); #ifdef NETSMB_NO_ANON_USER if (dp->ioc_user[0] == 0) return EINVAL; #endif if (dp->ioc_server == NULL) return EINVAL; if (dp->ioc_localcs[0] == 0) { SMBERROR("no local charset ?\n"); return EINVAL; } spec->sap = smb_memdupin(dp->ioc_server, dp->ioc_svlen); if (spec->sap == NULL) return ENOMEM; if (dp->ioc_local) { spec->lap = smb_memdupin(dp->ioc_local, dp->ioc_lolen); if (spec->lap == NULL) { smb_usr_vcspec_free(spec); return ENOMEM; } } spec->srvname = dp->ioc_srvname; spec->pass = dp->ioc_password; spec->domain = dp->ioc_workgroup; spec->username = dp->ioc_user; spec->mode = dp->ioc_mode; spec->rights = dp->ioc_rights; spec->owner = dp->ioc_owner; spec->group = dp->ioc_group; spec->localcs = dp->ioc_localcs; spec->servercs = dp->ioc_servercs; if (dp->ioc_opt & SMBVOPT_PRIVATE) flags |= SMBV_PRIVATE; if (dp->ioc_opt & SMBVOPT_SINGLESHARE) flags |= SMBV_PRIVATE | SMBV_SINGLESHARE; spec->flags = flags; return 0; }
/* * Set DOS file attributes. mtime should be NULL for dialects above lm10 */ int smbfs_smb_setpattr(struct smbnode *np, u_int16_t attr, struct timespec *mtime, struct smb_cred *scred) { struct smb_rq rq, *rqp = &rq; struct smb_share *ssp = np->n_mount->sm_share; struct mbchain *mbp; u_long time; int error, svtz; error = smb_rq_init(rqp, SSTOCP(ssp), SMB_COM_SET_INFORMATION, scred); if (error) return error; svtz = SSTOVC(ssp)->vc_sopt.sv_tz; smb_rq_getrequest(rqp, &mbp); smb_rq_wstart(rqp); mb_put_uint16le(mbp, attr); if (mtime) { smb_time_local2server(mtime, svtz, &time); } else time = 0; mb_put_uint32le(mbp, time); /* mtime */ mb_put_mem(mbp, NULL, 5 * 2, MB_MZERO); smb_rq_wend(rqp); smb_rq_bstart(rqp); mb_put_uint8(mbp, SMB_DT_ASCII); do { error = smbfs_fullpath(mbp, SSTOVC(ssp), np, NULL, 0); if (error) break; mb_put_uint8(mbp, SMB_DT_ASCII); mb_put_uint8(mbp, 0); smb_rq_bend(rqp); error = smb_rq_simple(rqp); if (error) { SMBERROR("smb_rq_simple(rqp) => error %d\n", error); break; } } while(0); smb_rq_done(rqp); return error; }
/* * Return locked root vnode of a filesystem */ static int smbfs_root(struct mount *mp, struct vnode **vpp) { struct thread *td = curthread; /* XXX */ struct smbmount *smp = VFSTOSMBFS(mp); struct vnode *vp; struct smbnode *np; struct smbfattr fattr; struct ucred *cred; struct smb_cred scred; int error; if (smp == NULL) { SMBERROR("smp == NULL (bug in umount)\n"); return EINVAL; } if (smp->sm_root) { *vpp = SMBTOV(smp->sm_root); return vget(*vpp, LK_EXCLUSIVE | LK_RETRY); } if (td->td_proc) cred = td->td_proc->p_ucred; else cred = proc0.p_ucred; smb_makescred(&scred, td, cred); error = smbfs_smb_lookup(NULL, NULL, 0, &fattr, &scred); if (error) return error; error = smbfs_nget(mp, NULL, "TheRooT", 7, &fattr, &vp); if (error) return error; vsetflags(vp, VROOT); np = VTOSMB(vp); smp->sm_root = np; *vpp = vp; return 0; }
static int smb_t2_reply(struct smb_t2rq *t2p) { struct mdchain *mdp; struct smb_rq *rqp = t2p->t2_rq; int error, totpgot, totdgot; u_int16_t totpcount, totdcount, pcount, poff, doff, pdisp, ddisp; u_int16_t tmp, bc, dcount; u_int8_t wc; error = smb_rq_reply(rqp); if (error) return error; if ((t2p->t2_flags & SMBT2_ALLSENT) == 0) { /* * this is an interim response, ignore it. */ SMBRQ_SLOCK(rqp); md_next_record(&rqp->sr_rp); SMBRQ_SUNLOCK(rqp); return 0; } /* * Now we have to get all subsequent responses. The CIFS specification * says that they can be disordered which is weird. * TODO: timo */ totpgot = totdgot = 0; totpcount = totdcount = 0xffff; mdp = &rqp->sr_rp; for (;;) { m_dumpm(mdp->md_top); if ((error = md_get_uint8(mdp, &wc)) != 0) break; if (wc < 10) { error = ENOENT; break; } if ((error = md_get_uint16le(mdp, &tmp)) != 0) break; if (totpcount > tmp) totpcount = tmp; md_get_uint16le(mdp, &tmp); if (totdcount > tmp) totdcount = tmp; if ((error = md_get_uint16le(mdp, &tmp)) != 0 || /* reserved */ (error = md_get_uint16le(mdp, &pcount)) != 0 || (error = md_get_uint16le(mdp, &poff)) != 0 || (error = md_get_uint16le(mdp, &pdisp)) != 0) break; if (pcount != 0 && pdisp != totpgot) { SMBERROR("Can't handle disordered parameters %d:%d\n", pdisp, totpgot); error = EINVAL; break; } if ((error = md_get_uint16le(mdp, &dcount)) != 0 || (error = md_get_uint16le(mdp, &doff)) != 0 || (error = md_get_uint16le(mdp, &ddisp)) != 0) break; if (dcount != 0 && ddisp != totdgot) { SMBERROR("Can't handle disordered data\n"); error = EINVAL; break; } md_get_uint8(mdp, &wc); md_get_uint8(mdp, NULL); tmp = wc; while (tmp--) md_get_uint16(mdp, NULL); if ((error = md_get_uint16le(mdp, &bc)) != 0) break; /* tmp = SMB_HDRLEN + 1 + 10 * 2 + 2 * wc + 2;*/ if (dcount) { error = smb_t2_placedata(mdp->md_top, doff, dcount, &t2p->t2_rdata); if (error) break; } if (pcount) { error = smb_t2_placedata(mdp->md_top, poff, pcount, &t2p->t2_rparam); if (error) break; } totpgot += pcount; totdgot += dcount; if (totpgot >= totpcount && totdgot >= totdcount) { error = 0; t2p->t2_flags |= SMBT2_ALLRECV; break; } /* * We're done with this reply, look for the next one. */ SMBRQ_SLOCK(rqp); md_next_record(&rqp->sr_rp); SMBRQ_SUNLOCK(rqp); error = smb_rq_reply(rqp); if (error) break; } 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; }
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; }
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; }
static int smbfs_mount(struct mount *mp) { struct smbmount *smp = NULL; struct smb_vc *vcp; struct smb_share *ssp = NULL; struct vnode *vp; struct thread *td; struct smb_dev *dev; struct smb_cred *scred; int error, v; char *pc, *pe; dev = NULL; td = curthread; if (mp->mnt_flag & (MNT_UPDATE | MNT_ROOTFS)) return EOPNOTSUPP; if (vfs_filteropt(mp->mnt_optnew, smbfs_opts)) { vfs_mount_error(mp, "%s", "Invalid option"); return (EINVAL); } scred = smbfs_malloc_scred(); smb_makescred(scred, td, td->td_ucred); /* Ask userspace of `fd`, the file descriptor of this session */ if (1 != vfs_scanopt(mp->mnt_optnew, "fd", "%d", &v)) { vfs_mount_error(mp, "No fd option"); smbfs_free_scred(scred); return (EINVAL); } error = smb_dev2share(v, SMBM_EXEC, scred, &ssp, &dev); smp = malloc(sizeof(*smp), M_SMBFSDATA, M_WAITOK | M_ZERO); if (error) { printf("invalid device handle %d (%d)\n", v, error); vfs_mount_error(mp, "invalid device handle %d %d\n", v, error); smbfs_free_scred(scred); free(smp, M_SMBFSDATA); return error; } vcp = SSTOVC(ssp); smb_share_unlock(ssp); mp->mnt_stat.f_iosize = SSTOVC(ssp)->vc_txmax; mp->mnt_data = smp; smp->sm_share = ssp; smp->sm_root = NULL; smp->sm_dev = dev; if (1 != vfs_scanopt(mp->mnt_optnew, "caseopt", "%d", &smp->sm_caseopt)) { vfs_mount_error(mp, "Invalid caseopt"); error = EINVAL; goto bad; } if (1 != vfs_scanopt(mp->mnt_optnew, "uid", "%d", &v)) { vfs_mount_error(mp, "Invalid uid"); error = EINVAL; goto bad; } smp->sm_uid = v; if (1 != vfs_scanopt(mp->mnt_optnew, "gid", "%d", &v)) { vfs_mount_error(mp, "Invalid gid"); error = EINVAL; goto bad; } smp->sm_gid = v; if (1 != vfs_scanopt(mp->mnt_optnew, "file_mode", "%d", &v)) { vfs_mount_error(mp, "Invalid file_mode"); error = EINVAL; goto bad; } smp->sm_file_mode = (v & (S_IRWXU|S_IRWXG|S_IRWXO)) | S_IFREG; if (1 != vfs_scanopt(mp->mnt_optnew, "dir_mode", "%d", &v)) { vfs_mount_error(mp, "Invalid dir_mode"); error = EINVAL; goto bad; } smp->sm_dir_mode = (v & (S_IRWXU|S_IRWXG|S_IRWXO)) | S_IFDIR; vfs_flagopt(mp->mnt_optnew, "nolong", &smp->sm_flags, SMBFS_MOUNT_NO_LONG); pc = mp->mnt_stat.f_mntfromname; pe = pc + sizeof(mp->mnt_stat.f_mntfromname); bzero(pc, MNAMELEN); *pc++ = '/'; *pc++ = '/'; pc = strchr(strncpy(pc, vcp->vc_username, pe - pc - 2), 0); if (pc < pe-1) { *(pc++) = '@'; pc = strchr(strncpy(pc, vcp->vc_srvname, pe - pc - 2), 0); if (pc < pe - 1) { *(pc++) = '/'; strncpy(pc, ssp->ss_name, pe - pc - 2); } } vfs_getnewfsid(mp); error = smbfs_root(mp, LK_EXCLUSIVE, &vp); if (error) { vfs_mount_error(mp, "smbfs_root error: %d", error); goto bad; } VOP_UNLOCK(vp, 0); SMBVDEBUG("root.v_usecount = %d\n", vrefcnt(vp)); #ifdef DIAGNOSTIC SMBERROR("mp=%p\n", mp); #endif smbfs_free_scred(scred); return error; bad: if (ssp) smb_share_put(ssp, scred); smbfs_free_scred(scred); SMB_LOCK(); if (error && smp->sm_dev == dev) { smp->sm_dev = NULL; sdp_trydestroy(dev); } SMB_UNLOCK(); free(smp, M_SMBFSDATA); return error; }
static int smbfs_node_alloc(struct mount *mp, struct vnode *dvp, const char *dirnm, int dirlen, const char *name, int nmlen, char sep, struct smbfattr *fap, struct vnode **vpp) { struct vattr vattr; struct thread *td = curthread; /* XXX */ struct smbmount *smp = VFSTOSMBFS(mp); struct smbnode *np, *dnp; struct vnode *vp, *vp2; struct smbcmp sc; char *p, *rpath; int error, rplen; sc.n_parent = dvp; sc.n_nmlen = nmlen; sc.n_name = name; if (smp->sm_root != NULL && dvp == NULL) { SMBERROR("do not allocate root vnode twice!\n"); return EINVAL; } if (nmlen == 2 && bcmp(name, "..", 2) == 0) { if (dvp == NULL) return EINVAL; vp = VTOSMB(VTOSMB(dvp)->n_parent)->n_vnode; error = vget(vp, LK_EXCLUSIVE, td); if (error == 0) *vpp = vp; return error; } else if (nmlen == 1 && name[0] == '.') { SMBERROR("do not call me with dot!\n"); return EINVAL; } dnp = dvp ? VTOSMB(dvp) : NULL; if (dnp == NULL && dvp != NULL) { vprint("smbfs_node_alloc: dead parent vnode", dvp); return EINVAL; } error = vfs_hash_get(mp, smbfs_hash(name, nmlen), LK_EXCLUSIVE, td, vpp, smbfs_vnode_cmp, &sc); if (error) return (error); if (*vpp) { np = VTOSMB(*vpp); /* Force cached attributes to be refreshed if stale. */ (void)VOP_GETATTR(*vpp, &vattr, td->td_ucred); /* * If the file type on the server is inconsistent with * what it was when we created the vnode, kill the * bogus vnode now and fall through to the code below * to create a new one with the right type. */ if (((*vpp)->v_type == VDIR && (np->n_dosattr & SMB_FA_DIR) == 0) || ((*vpp)->v_type == VREG && (np->n_dosattr & SMB_FA_DIR) != 0)) { vgone(*vpp); vput(*vpp); } else { SMBVDEBUG("vnode taken from the hashtable\n"); return (0); } } /* * If we don't have node attributes, then it is an explicit lookup * for an existing vnode. */ if (fap == NULL) return ENOENT; error = getnewvnode("smbfs", mp, &smbfs_vnodeops, vpp); if (error) return (error); vp = *vpp; np = malloc(sizeof *np, M_SMBNODE, M_WAITOK | M_ZERO); rplen = dirlen; if (sep != '\0') rplen++; rplen += nmlen; rpath = malloc(rplen + 1, M_SMBNODENAME, M_WAITOK); p = rpath; bcopy(dirnm, p, dirlen); p += dirlen; if (sep != '\0') *p++ = sep; if (name != NULL) { bcopy(name, p, nmlen); p += nmlen; } *p = '\0'; MPASS(p == rpath + rplen); lockmgr(vp->v_vnlock, LK_EXCLUSIVE, NULL); /* Vnode initialization */ vp->v_type = fap->fa_attr & SMB_FA_DIR ? VDIR : VREG; vp->v_data = np; np->n_vnode = vp; np->n_mount = VFSTOSMBFS(mp); np->n_rpath = rpath; np->n_rplen = rplen; np->n_nmlen = nmlen; np->n_name = smbfs_name_alloc(name, nmlen); np->n_ino = fap->fa_ino; if (dvp) { ASSERT_VOP_LOCKED(dvp, "smbfs_node_alloc"); np->n_parent = dvp; np->n_parentino = VTOSMB(dvp)->n_ino; if (/*vp->v_type == VDIR &&*/ (dvp->v_vflag & VV_ROOT) == 0) { vref(dvp); np->n_flag |= NREFPARENT; } } else if (vp->v_type == VREG) SMBERROR("new vnode '%s' born without parent ?\n", np->n_name); error = insmntque(vp, mp); if (error) { free(np, M_SMBNODE); return (error); } error = vfs_hash_insert(vp, smbfs_hash(name, nmlen), LK_EXCLUSIVE, td, &vp2, smbfs_vnode_cmp, &sc); if (error) return (error); if (vp2 != NULL) *vpp = vp2; return (0); }
int smb_maperror(int eclass, int eno) { if (eclass == 0 && eno == 0) return 0; switch (eclass) { case ERRDOS: switch (eno) { case ERRbadfunc: case ERRbadmcb: case ERRbadenv: case ERRbadformat: case ERRrmuns: return EINVAL; case ERRbadfile: case ERRbadpath: case ERRremcd: case 66: /* nt returns it when share not available */ case 67: /* observed from nt4sp6 when sharename wrong */ return ENOENT; case ERRnofids: return EMFILE; case ERRnoaccess: case ERRbadshare: return EACCES; case ERRbadfid: return EBADF; case ERRnomem: return ENOMEM; /* actually remote no mem... */ case ERRbadmem: return EFAULT; case ERRbadaccess: return EACCES; case ERRbaddata: return E2BIG; case ERRbaddrive: case ERRnotready: /* nt */ return ENXIO; case ERRdiffdevice: return EXDEV; case ERRnofiles: return 0; /* eeof ? */ return ETXTBSY; case ERRlock: return EDEADLK; case ERRfilexists: return EEXIST; case 123: /* dunno what is it, but samba maps as noent */ return ENOENT; case 145: /* samba */ return ENOTEMPTY; case ERRnotlocked: return 0; /* file become unlocked */ case 183: return EEXIST; case ERRquota: return EDQUOT; } break; case ERRSRV: switch (eno) { case ERRerror: return EINVAL; case ERRbadpw: case ERRpasswordExpired: return EAUTH; case ERRaccess: return EACCES; case ERRinvnid: return ENETRESET; case ERRinvnetname: SMBERROR("NetBIOS name is invalid\n"); return EAUTH; case 3: /* reserved and returned */ return EIO; case ERRaccountExpired: case ERRbadClient: case ERRbadLogonTime: return EPERM; case ERRnosupport: return EBADRPC; } break; case ERRHRD: switch (eno) { case ERRnowrite: return EROFS; case ERRbadunit: return ENODEV; case ERRnotready: case ERRbadcmd: case ERRdata: return EIO; case ERRbadreq: return EBADRPC; case ERRbadshare: return ETXTBSY; case ERRlock: return EDEADLK; } break; } SMBERROR("Unmapped error %d:%d\n", eclass, eno); return EBADRPC; }
static int smbfs_node_alloc(struct mount *mp, struct vnode *dvp, const char *name, int nmlen, struct smbfattr *fap, struct vnode **vpp) { struct vattr vattr; struct thread *td = curthread; /* XXX */ struct smbmount *smp = VFSTOSMBFS(mp); struct smbnode_hashhead *nhpp; struct smbnode *np, *np2, *dnp; struct vnode *vp; u_long hashval; int error; *vpp = NULL; if (smp->sm_root != NULL && dvp == NULL) { SMBERROR("do not allocate root vnode twice!\n"); return EINVAL; } if (nmlen == 2 && bcmp(name, "..", 2) == 0) { if (dvp == NULL) return EINVAL; vp = VTOSMB(VTOSMB(dvp)->n_parent)->n_vnode; error = vget(vp, LK_EXCLUSIVE, td); if (error == 0) *vpp = vp; return error; } else if (nmlen == 1 && name[0] == '.') { SMBERROR("do not call me with dot!\n"); return EINVAL; } dnp = dvp ? VTOSMB(dvp) : NULL; if (dnp == NULL && dvp != NULL) { vprint("smbfs_node_alloc: dead parent vnode", dvp); return EINVAL; } hashval = smbfs_hash(name, nmlen); retry: smbfs_hash_lock(smp); loop: nhpp = SMBFS_NOHASH(smp, hashval); LIST_FOREACH(np, nhpp, n_hash) { vp = SMBTOV(np); if (np->n_parent != dvp || np->n_nmlen != nmlen || bcmp(name, np->n_name, nmlen) != 0) continue; VI_LOCK(vp); smbfs_hash_unlock(smp); if (vget(vp, LK_EXCLUSIVE | LK_INTERLOCK, td) != 0) goto retry; /* Force cached attributes to be refreshed if stale. */ (void)VOP_GETATTR(vp, &vattr, td->td_ucred); /* * If the file type on the server is inconsistent with * what it was when we created the vnode, kill the * bogus vnode now and fall through to the code below * to create a new one with the right type. */ if ((vp->v_type == VDIR && (np->n_dosattr & SMB_FA_DIR) == 0) || (vp->v_type == VREG && (np->n_dosattr & SMB_FA_DIR) != 0)) { vgone(vp); vput(vp); break; } *vpp = vp; return 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; }
/* * 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_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 smbfs_findnextLM2(struct smbfs_fctx *ctx, int limit) { struct mdchain *mbp; struct smb_t2rq *t2p; char *cp; u_int8_t tb; u_int16_t date, time, wattr; u_int32_t size, next, dattr; int64_t lint; int error, svtz, cnt, fxsz, nmlen, recsz; if (ctx->f_ecnt == 0) { if (ctx->f_flags & SMBFS_RDD_EOF) return ENOENT; ctx->f_left = ctx->f_limit = limit; error = smbfs_smb_trans2find2(ctx); if (error) return error; } t2p = ctx->f_t2; mbp = &t2p->t2_rdata; svtz = SSTOVC(ctx->f_ssp)->vc_sopt.sv_tz; switch (ctx->f_infolevel) { case SMB_INFO_STANDARD: next = 0; fxsz = 0; md_get_uint16le(mbp, &date); md_get_uint16le(mbp, &time); /* creation time */ md_get_uint16le(mbp, &date); md_get_uint16le(mbp, &time); /* access time */ smb_dos2unixtime(date, time, 0, svtz, &ctx->f_attr.fa_atime); md_get_uint16le(mbp, &date); md_get_uint16le(mbp, &time); /* access time */ smb_dos2unixtime(date, time, 0, svtz, &ctx->f_attr.fa_mtime); md_get_uint32le(mbp, &size); ctx->f_attr.fa_size = size; md_get_uint32(mbp, NULL); /* allocation size */ md_get_uint16le(mbp, &wattr); ctx->f_attr.fa_attr = wattr; md_get_uint8(mbp, &tb); size = nmlen = tb; fxsz = 23; recsz = next = 24 + nmlen; /* docs misses zero byte at end */ break; case SMB_FIND_FILE_DIRECTORY_INFO: md_get_uint32le(mbp, &next); md_get_uint32(mbp, NULL); /* file index */ md_get_int64(mbp, NULL); /* creation time */ md_get_int64le(mbp, &lint); smb_time_NT2local(lint, svtz, &ctx->f_attr.fa_atime); md_get_int64le(mbp, &lint); smb_time_NT2local(lint, svtz, &ctx->f_attr.fa_mtime); md_get_int64le(mbp, &lint); smb_time_NT2local(lint, svtz, &ctx->f_attr.fa_ctime); md_get_int64le(mbp, &lint); /* file size */ ctx->f_attr.fa_size = lint; md_get_int64(mbp, NULL); /* real size (should use) */ md_get_uint32le(mbp, &dattr); /* EA */ ctx->f_attr.fa_attr = dattr; md_get_uint32le(mbp, &size); /* name len */ fxsz = 64; recsz = next ? next : fxsz + size; break; default: SMBERROR("unexpected info level %d\n", ctx->f_infolevel); return EINVAL; } if (SMB_UNICODE_STRINGS(SSTOVC(ctx->f_ssp))) { nmlen = min(size, SMB_MAXFNAMELEN * 2); } else nmlen = min(size, SMB_MAXFNAMELEN); cp = ctx->f_name; error = md_get_mem(mbp, cp, nmlen, MB_MSYSTEM); if (error) return error; if (next) { cnt = next - nmlen - fxsz; if (cnt > 0) md_get_mem(mbp, NULL, cnt, MB_MSYSTEM); else if (cnt < 0) { SMBERROR("out of sync\n"); return EBADRPC; } } if (SMB_UNICODE_STRINGS(SSTOVC(ctx->f_ssp))) { if (nmlen > 1 && cp[nmlen - 1] == 0 && cp[nmlen - 2] == 0) nmlen -= 2; } else if (nmlen && cp[nmlen - 1] == 0) nmlen--; if (nmlen == 0) return EBADRPC; next = ctx->f_eofs + recsz; if (ctx->f_rnameofs && (ctx->f_flags & SMBFS_RDD_GOTRNAME) == 0 && (ctx->f_rnameofs >= ctx->f_eofs && ctx->f_rnameofs < next)) { /* * Server needs a resume filename. */ if (ctx->f_rnamelen <= nmlen) { if (ctx->f_rname) free(ctx->f_rname, M_SMBFSDATA); ctx->f_rname = malloc(nmlen + 1, M_SMBFSDATA, M_WAITOK); ctx->f_rnamelen = nmlen; } bcopy(ctx->f_name, ctx->f_rname, nmlen); ctx->f_rname[nmlen] = 0; ctx->f_flags |= SMBFS_RDD_GOTRNAME; } ctx->f_nmlen = nmlen; ctx->f_eofs = next; ctx->f_ecnt--; ctx->f_left--; return 0; }
int smb_smb_negotiate(struct smb_vc *vcp, struct smb_cred *scred) { struct smb_dialect *dp; struct smb_sopt *sp = NULL; struct smb_rq *rqp; struct mbchain *mbp; struct mdchain *mdp; u_int8_t wc, stime[8], sblen; u_int16_t dindex, tw, tw1, swlen, bc; int error, maxqsz; if (smb_smb_nomux(vcp, scred, __func__) != 0) return EINVAL; vcp->vc_hflags = 0; vcp->vc_hflags2 = 0; vcp->obj.co_flags &= ~(SMBV_ENCRYPT); sp = &vcp->vc_sopt; bzero(sp, sizeof(struct smb_sopt)); error = smb_rq_alloc(VCTOCP(vcp), SMB_COM_NEGOTIATE, scred, &rqp); if (error) return error; smb_rq_getrequest(rqp, &mbp); smb_rq_wstart(rqp); smb_rq_wend(rqp); smb_rq_bstart(rqp); for(dp = smb_dialects; dp->d_id != -1; dp++) { mb_put_uint8(mbp, SMB_DT_DIALECT); smb_put_dstring(mbp, vcp, dp->d_name, SMB_CS_NONE); } smb_rq_bend(rqp); error = smb_rq_simple(rqp); SMBSDEBUG("%d\n", error); if (error) goto bad; smb_rq_getreply(rqp, &mdp); do { error = md_get_uint8(mdp, &wc); if (error) break; error = md_get_uint16le(mdp, &dindex); if (error) break; if (dindex > 7) { SMBERROR("Don't know how to talk with server %s (%d)\n", "xxx", dindex); error = EBADRPC; break; } dp = smb_dialects + dindex; sp->sv_proto = dp->d_id; SMBSDEBUG("Dialect %s (%d, %d)\n", dp->d_name, dindex, wc); error = EBADRPC; if (dp->d_id >= SMB_DIALECT_NTLM0_12) { if (wc != 17) break; md_get_uint8(mdp, &sp->sv_sm); md_get_uint16le(mdp, &sp->sv_maxmux); md_get_uint16le(mdp, &sp->sv_maxvcs); md_get_uint32le(mdp, &sp->sv_maxtx); md_get_uint32le(mdp, &sp->sv_maxraw); md_get_uint32le(mdp, &sp->sv_skey); md_get_uint32le(mdp, &sp->sv_caps); md_get_mem(mdp, stime, 8, MB_MSYSTEM); md_get_uint16le(mdp, (u_int16_t*)&sp->sv_tz); md_get_uint8(mdp, &sblen); if (sblen && (sp->sv_sm & SMB_SM_ENCRYPT)) { if (sblen != SMB_MAXCHALLENGELEN) { SMBERROR("Unexpected length of security blob (%d)\n", sblen); break; } error = md_get_uint16(mdp, &bc); if (error) break; if (sp->sv_caps & SMB_CAP_EXT_SECURITY) md_get_mem(mdp, NULL, 16, MB_MSYSTEM); error = md_get_mem(mdp, vcp->vc_ch, sblen, MB_MSYSTEM); if (error) break; vcp->vc_chlen = sblen; vcp->obj.co_flags |= SMBV_ENCRYPT; } vcp->vc_hflags2 |= SMB_FLAGS2_KNOWS_LONG_NAMES; if (dp->d_id == SMB_DIALECT_NTLM0_12 && sp->sv_maxtx < 4096 && (sp->sv_caps & SMB_CAP_NT_SMBS) == 0) { vcp->obj.co_flags |= SMBV_WIN95; SMBSDEBUG("Win95 detected\n"); } } else if (dp->d_id > SMB_DIALECT_CORE) { md_get_uint16le(mdp, &tw); sp->sv_sm = tw; md_get_uint16le(mdp, &tw); sp->sv_maxtx = tw; md_get_uint16le(mdp, &sp->sv_maxmux); md_get_uint16le(mdp, &sp->sv_maxvcs); md_get_uint16le(mdp, &tw); /* rawmode */ md_get_uint32le(mdp, &sp->sv_skey); if (wc == 13) { /* >= LANMAN1 */ md_get_uint16(mdp, &tw); /* time */ md_get_uint16(mdp, &tw1); /* date */ md_get_uint16le(mdp, (u_int16_t*)&sp->sv_tz); md_get_uint16le(mdp, &swlen); if (swlen > SMB_MAXCHALLENGELEN) break; md_get_uint16(mdp, NULL); /* mbz */ if (md_get_uint16(mdp, &bc) != 0) break; if (bc < swlen) break; if (swlen && (sp->sv_sm & SMB_SM_ENCRYPT)) { error = md_get_mem(mdp, vcp->vc_ch, swlen, MB_MSYSTEM); if (error) break; vcp->vc_chlen = swlen; vcp->obj.co_flags |= SMBV_ENCRYPT; } } vcp->vc_hflags2 |= SMB_FLAGS2_KNOWS_LONG_NAMES; } else { /* an old CORE protocol */ sp->sv_maxmux = 1; } error = 0; } while (0); if (error == 0) { vcp->vc_maxvcs = sp->sv_maxvcs; if (vcp->vc_maxvcs <= 1) { if (vcp->vc_maxvcs == 0) vcp->vc_maxvcs = 1; } if (sp->sv_maxtx <= 0 || sp->sv_maxtx > 0xffff) sp->sv_maxtx = 1024; SMB_TRAN_GETPARAM(vcp, SMBTP_SNDSZ, &maxqsz); vcp->vc_txmax = min(sp->sv_maxtx, maxqsz); SMBSDEBUG("TZ = %d\n", sp->sv_tz); SMBSDEBUG("CAPS = %x\n", sp->sv_caps); SMBSDEBUG("MAXMUX = %d\n", sp->sv_maxmux); SMBSDEBUG("MAXVCS = %d\n", sp->sv_maxvcs); SMBSDEBUG("MAXRAW = %d\n", sp->sv_maxraw); SMBSDEBUG("MAXTX = %d\n", sp->sv_maxtx); } bad: smb_rq_done(rqp); return error; }
int smb_smb_treeconnect(struct smb_share *ssp, struct smb_cred *scred) { struct smb_vc *vcp; struct smb_rq *rqp = NULL; struct mbchain *mbp; struct mdchain *mdp; char *pbuf, *unc_name = NULL; int error, tlen, plen, unc_len; uint16_t bcnt, options; uint8_t wc; vcp = SSTOVC(ssp); /* * Make this a "VC-level" request, so it will have * rqp->sr_share == NULL, and smb_iod_sendrq() * will send it with TID = SMB_TID_UNKNOWN * * This also serves to bypass the wait for * share state changes, which this call is * trying to carry out. */ error = smb_rq_alloc(VCTOCP(vcp), SMB_COM_TREE_CONNECT_ANDX, scred, &rqp); if (error) return (error); /* * Build the UNC name, i.e. "//server/share" * but with backslashes of course. * size math: three slashes, one null. */ unc_len = 4 + strlen(vcp->vc_srvname) + strlen(ssp->ss_name); unc_name = kmem_alloc(unc_len, KM_SLEEP); (void) snprintf(unc_name, unc_len, "\\\\%s\\%s", vcp->vc_srvname, ssp->ss_name); SMBSDEBUG("unc_name: \"%s\"", unc_name); /* * The password is now pre-computed in the * user-space helper process. */ plen = ssp->ss_pwlen; pbuf = ssp->ss_pass; /* * Build the request. */ mbp = &rqp->sr_rq; smb_rq_wstart(rqp); mb_put_uint8(mbp, 0xff); mb_put_uint8(mbp, 0); mb_put_uint16le(mbp, 0); mb_put_uint16le(mbp, 0); /* Flags */ mb_put_uint16le(mbp, plen); smb_rq_wend(rqp); smb_rq_bstart(rqp); /* Tree connect password, if any */ error = mb_put_mem(mbp, pbuf, plen, MB_MSYSTEM); if (error) goto out; /* UNC resource name */ error = smb_put_dstring(mbp, vcp, unc_name, SMB_CS_NONE); if (error) goto out; /* * Put the type string (always ASCII), * including the null. */ tlen = strlen(ssp->ss_type_req) + 1; error = mb_put_mem(mbp, ssp->ss_type_req, tlen, MB_MSYSTEM); if (error) goto out; smb_rq_bend(rqp); /* * Run the request. * * Using NOINTR_RECV because we don't want to risk * missing a successful tree connect response, * which would "leak" Tree IDs. */ rqp->sr_flags |= SMBR_NOINTR_RECV; error = smb_rq_simple(rqp); SMBSDEBUG("%d\n", error); if (error) { /* * If we get the server name wrong, i.e. due to * mis-configured name services, this will be * NT_STATUS_DUPLICATE_NAME. Log this error. */ SMBERROR("(%s) failed, status=0x%x", unc_name, rqp->sr_error); goto out; } /* * Parse the TCON response */ smb_rq_getreply(rqp, &mdp); md_get_uint8(mdp, &wc); if (wc != 3 && wc != 7) { error = EBADRPC; goto out; } md_get_uint16le(mdp, NULL); /* AndX cmd */ md_get_uint16le(mdp, NULL); /* AndX off */ md_get_uint16le(mdp, &options); /* option bits (DFS, search) */ if (wc == 7) { md_get_uint32le(mdp, NULL); /* MaximalShareAccessRights */ md_get_uint32le(mdp, NULL); /* GuestMaximalShareAcc... */ } error = md_get_uint16le(mdp, &bcnt); /* byte count */ if (error) goto out; /* * Get the returned share type string, * i.e. "IPC" or whatever. Don't care * if we get an error reading the type. */ tlen = sizeof (ssp->ss_type_ret); bzero(ssp->ss_type_ret, tlen--); if (tlen > bcnt) tlen = bcnt; md_get_mem(mdp, ssp->ss_type_ret, tlen, MB_MSYSTEM); /* Success! */ SMB_SS_LOCK(ssp); ssp->ss_tid = rqp->sr_rptid; ssp->ss_vcgenid = vcp->vc_genid; ssp->ss_options = options; ssp->ss_flags |= SMBS_CONNECTED; SMB_SS_UNLOCK(ssp); out: if (unc_name) kmem_free(unc_name, unc_len); smb_rq_done(rqp); return (error); }
static int smbfs_smb_qpathinfo(struct smbnode *np, struct smbfattr *fap, struct smb_cred *scred, short infolevel) { struct smb_share *ssp = np->n_mount->sm_share; struct smb_vc *vcp = SSTOVC(ssp); struct smb_t2rq *t2p; int error, svtz, timesok = 1; struct mbchain *mbp; struct mdchain *mdp; u_int16_t date, time, wattr; int64_t lint; u_int32_t size, dattr; error = smb_t2_alloc(SSTOCP(ssp), SMB_TRANS2_QUERY_PATH_INFORMATION, scred, &t2p); if (error) return error; mbp = &t2p->t2_tparam; mb_init(mbp); if (!infolevel) { if (SMB_DIALECT(vcp) < SMB_DIALECT_NTLM0_12) infolevel = SMB_QUERY_FILE_STANDARD; else infolevel = SMB_QUERY_FILE_BASIC_INFO; } mb_put_uint16le(mbp, infolevel); mb_put_uint32le(mbp, 0); /* mb_put_uint8(mbp, SMB_DT_ASCII); specs are wrong */ error = smbfs_fullpath(mbp, vcp, np, NULL, 0); if (error) { smb_t2_done(t2p); return error; } t2p->t2_maxpcount = 2; t2p->t2_maxdcount = vcp->vc_txmax; error = smb_t2_request(t2p); if (error) { smb_t2_done(t2p); if (infolevel == SMB_QUERY_FILE_STANDARD || error != EINVAL) return error; return smbfs_smb_qpathinfo(np, fap, scred, SMB_QUERY_FILE_STANDARD); } mdp = &t2p->t2_rdata; svtz = vcp->vc_sopt.sv_tz; switch (infolevel) { case SMB_QUERY_FILE_STANDARD: timesok = 0; md_get_uint16le(mdp, NULL); md_get_uint16le(mdp, NULL); /* creation time */ md_get_uint16le(mdp, &date); md_get_uint16le(mdp, &time); /* access time */ if (date || time) { timesok++; smb_dos2unixtime(date, time, 0, svtz, &fap->fa_atime); } md_get_uint16le(mdp, &date); md_get_uint16le(mdp, &time); /* modify time */ if (date || time) { timesok++; smb_dos2unixtime(date, time, 0, svtz, &fap->fa_mtime); } md_get_uint32le(mdp, &size); fap->fa_size = size; md_get_uint32(mdp, NULL); /* allocation size */ md_get_uint16le(mdp, &wattr); fap->fa_attr = wattr; break; case SMB_QUERY_FILE_BASIC_INFO: timesok = 0; md_get_int64(mdp, NULL); /* creation time */ md_get_int64le(mdp, &lint); if (lint) { timesok++; smb_time_NT2local(lint, svtz, &fap->fa_atime); } md_get_int64le(mdp, &lint); if (lint) { timesok++; smb_time_NT2local(lint, svtz, &fap->fa_mtime); } md_get_int64le(mdp, &lint); if (lint) { timesok++; smb_time_NT2local(lint, svtz, &fap->fa_ctime); } md_get_uint32le(mdp, &dattr); fap->fa_attr = dattr; md_get_uint32(mdp, NULL); /* XXX could use ALL_INFO to get size */ break; default: SMBERROR("unexpected info level %d\n", infolevel); error = EINVAL; } smb_t2_done(t2p); /* * if all times are zero (observed with FAT on NT4SP6) * then fall back to older info level */ if (!timesok) { if (infolevel != SMB_QUERY_FILE_STANDARD) return smbfs_smb_qpathinfo(np, fap, scred, SMB_QUERY_FILE_STANDARD); error = EINVAL; } return error; }