smb_dev.c revision 76166
175374Sbp/* 275374Sbp * Copyright (c) 2000-2001 Boris Popov 375374Sbp * All rights reserved. 475374Sbp * 575374Sbp * Redistribution and use in source and binary forms, with or without 675374Sbp * modification, are permitted provided that the following conditions 775374Sbp * are met: 875374Sbp * 1. Redistributions of source code must retain the above copyright 975374Sbp * notice, this list of conditions and the following disclaimer. 1075374Sbp * 2. Redistributions in binary form must reproduce the above copyright 1175374Sbp * notice, this list of conditions and the following disclaimer in the 1275374Sbp * documentation and/or other materials provided with the distribution. 1375374Sbp * 3. All advertising materials mentioning features or use of this software 1475374Sbp * must display the following acknowledgement: 1575374Sbp * This product includes software developed by Boris Popov. 1675374Sbp * 4. Neither the name of the author nor the names of any co-contributors 1775374Sbp * may be used to endorse or promote products derived from this software 1875374Sbp * without specific prior written permission. 1975374Sbp * 2075374Sbp * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 2175374Sbp * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2275374Sbp * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2375374Sbp * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 2475374Sbp * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2575374Sbp * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2675374Sbp * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2775374Sbp * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2875374Sbp * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2975374Sbp * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3075374Sbp * SUCH DAMAGE. 3175374Sbp * 3275374Sbp * $FreeBSD: head/sys/netsmb/smb_dev.c 76166 2001-05-01 08:13:21Z markm $ 3375374Sbp */ 3475374Sbp#include <sys/param.h> 3575374Sbp#include <sys/kernel.h> 3675374Sbp#include <sys/systm.h> 3776166Smarkm#include <sys/conf.h> 3876166Smarkm#include <sys/fcntl.h> 3975374Sbp#include <sys/ioccom.h> 4076166Smarkm#include <sys/lock.h> 4175374Sbp#include <sys/malloc.h> 4276166Smarkm#include <sys/file.h> /* Must come after sys/malloc.h */ 4375374Sbp#include <sys/mbuf.h> 4476166Smarkm#include <sys/poll.h> 4575374Sbp#include <sys/proc.h> 4676166Smarkm#include <sys/select.h> 4775374Sbp#include <sys/socket.h> 4875374Sbp#include <sys/socketvar.h> 4975374Sbp#include <sys/sysctl.h> 5076166Smarkm#include <sys/uio.h> 5175374Sbp#include <sys/vnode.h> 5275374Sbp 5375374Sbp#include <net/if.h> 5475374Sbp 5575374Sbp#include <netsmb/smb.h> 5675374Sbp#include <netsmb/smb_conn.h> 5775374Sbp#include <netsmb/smb_subr.h> 5875374Sbp#include <netsmb/smb_dev.h> 5975374Sbp 6075374Sbp#define SMB_GETDEV(dev) ((struct smb_dev*)(dev)->si_drv1) 6175374Sbp#define SMB_CHECKMINOR(dev) do { \ 6275374Sbp sdp = SMB_GETDEV(dev); \ 6375374Sbp if (sdp == NULL) return ENXIO; \ 6475374Sbp } while(0) 6575374Sbp 6675374Sbpstatic d_open_t nsmb_dev_open; 6775374Sbpstatic d_close_t nsmb_dev_close; 6875374Sbpstatic d_read_t nsmb_dev_read; 6975374Sbpstatic d_write_t nsmb_dev_write; 7075374Sbpstatic d_ioctl_t nsmb_dev_ioctl; 7175374Sbpstatic d_poll_t nsmb_dev_poll; 7275374Sbp 7375374SbpMODULE_DEPEND(netsmb, libiconv, 1, 1, 1); 7475374SbpMODULE_VERSION(netsmb, NSMB_VERSION); 7575374Sbp 7675374Sbpstatic int smb_version = NSMB_VERSION; 7775374Sbp 7875374Sbp 7975374SbpSYSCTL_DECL(_net_smb); 8075374SbpSYSCTL_INT(_net_smb, OID_AUTO, version, CTLFLAG_RD, &smb_version, 0, ""); 8175374Sbp 8275374Sbpstatic MALLOC_DEFINE(M_NSMBDEV, "NETSMBDEV", "NET/SMB device"); 8375374Sbp 8475374Sbp 8575374Sbp/* 8675374Sbpint smb_dev_queue(struct smb_dev *ndp, struct smb_rq *rqp, int prio); 8775374Sbp*/ 8875374Sbp 8975374Sbpstatic struct cdevsw nsmb_cdevsw = { 9075374Sbp /* open */ nsmb_dev_open, 9175374Sbp /* close */ nsmb_dev_close, 9275374Sbp /* read */ nsmb_dev_read, 9375374Sbp /* write */ nsmb_dev_write, 9475374Sbp /* ioctl */ nsmb_dev_ioctl, 9575374Sbp /* poll */ nsmb_dev_poll, 9675374Sbp /* mmap */ nommap, 9775374Sbp /* strategy */ nostrategy, 9875374Sbp /* name */ NSMB_NAME, 9975374Sbp /* maj */ NSMB_MAJOR, 10075374Sbp /* dump */ nodump, 10175374Sbp /* psize */ nopsize, 10275374Sbp /* flags */ 0, 10375374Sbp#ifndef FB_CURRENT 10475374Sbp /* bmaj */ -1 10575374Sbp#endif 10675374Sbp}; 10775374Sbp 10875374Sbpstatic eventhandler_tag nsmb_dev_tag; 10975374Sbp 11075374Sbpstatic void 11175374Sbpnsmb_dev_clone(void *arg, char *name, int namelen, dev_t *dev) 11275374Sbp{ 11375374Sbp int min; 11475374Sbp 11575374Sbp if (*dev != NODEV) 11675374Sbp return; 11775374Sbp if (dev_stdclone(name, NULL, NSMB_NAME, &min) != 1) 11875374Sbp return; 11975374Sbp *dev = make_dev(&nsmb_cdevsw, min, 0, 0, 0600, NSMB_NAME"%d", min); 12075374Sbp} 12175374Sbp 12275374Sbpstatic int 12375374Sbpnsmb_dev_open(dev_t dev, int oflags, int devtype, struct proc *p) 12475374Sbp{ 12575374Sbp struct smb_dev *sdp; 12675374Sbp struct ucred *cred = p->p_ucred; 12775374Sbp int s; 12875374Sbp 12975374Sbp sdp = SMB_GETDEV(dev); 13075374Sbp if (sdp && (sdp->sd_flags & NSMBFL_OPEN)) 13175374Sbp return EBUSY; 13275374Sbp if (sdp == NULL) { 13375374Sbp sdp = malloc(sizeof(*sdp), M_NSMBDEV, M_WAITOK); 13475374Sbp dev->si_drv1 = (void*)sdp; 13575374Sbp } 13675374Sbp /* 13775374Sbp * XXX: this is just crazy - make a device for an already passed device... 13875374Sbp * someone should take care of it. 13975374Sbp */ 14075374Sbp if ((dev->si_flags & SI_NAMED) == 0) 14175374Sbp make_dev(&nsmb_cdevsw, minor(dev), cred->cr_uid, cred->cr_gid, 0700, 14275374Sbp NSMB_NAME"%d", dev2unit(dev)); 14375374Sbp bzero(sdp, sizeof(*sdp)); 14475374Sbp/* 14575374Sbp STAILQ_INIT(&sdp->sd_rqlist); 14675374Sbp STAILQ_INIT(&sdp->sd_rplist); 14775374Sbp bzero(&sdp->sd_pollinfo, sizeof(struct selinfo)); 14875374Sbp*/ 14975374Sbp s = splimp(); 15075374Sbp sdp->sd_level = -1; 15175374Sbp sdp->sd_flags |= NSMBFL_OPEN; 15275374Sbp splx(s); 15375374Sbp return 0; 15475374Sbp} 15575374Sbp 15675374Sbpstatic int 15775374Sbpnsmb_dev_close(dev_t dev, int flag, int fmt, struct proc *p) 15875374Sbp{ 15975374Sbp struct smb_dev *sdp; 16075374Sbp struct smb_vc *vcp; 16175374Sbp struct smb_share *ssp; 16275374Sbp struct smb_cred scred; 16375374Sbp int s; 16475374Sbp 16575374Sbp SMB_CHECKMINOR(dev); 16675374Sbp s = splimp(); 16775374Sbp if ((sdp->sd_flags & NSMBFL_OPEN) == 0) { 16875374Sbp splx(s); 16975374Sbp return EBADF; 17075374Sbp } 17175374Sbp smb_makescred(&scred, p, NULL); 17275374Sbp ssp = sdp->sd_share; 17375374Sbp if (ssp != NULL) 17475374Sbp smb_share_rele(ssp, &scred); 17575374Sbp vcp = sdp->sd_vc; 17675374Sbp if (vcp != NULL) 17775374Sbp smb_vc_rele(vcp, &scred); 17875374Sbp/* 17975374Sbp smb_flushq(&sdp->sd_rqlist); 18075374Sbp smb_flushq(&sdp->sd_rplist); 18175374Sbp*/ 18275374Sbp#if __FreeBSD_version > 400001 18375374Sbp dev->si_drv1 = NULL; 18475374Sbp free(sdp, M_NSMBDEV); 18575374Sbp destroy_dev(dev); 18675374Sbp#else 18775374Sbp sdp->sd_flags &= ~NSMBFL_OPEN; 18875374Sbp#endif 18975374Sbp splx(s); 19075374Sbp return 0; 19175374Sbp} 19275374Sbp 19375374Sbp 19475374Sbpstatic int 19575374Sbpnsmb_dev_ioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p) 19675374Sbp{ 19775374Sbp struct smb_dev *sdp; 19875374Sbp struct smb_vc *vcp; 19975374Sbp struct smb_share *ssp; 20075374Sbp struct smb_cred scred; 20175374Sbp int error = 0; 20275374Sbp 20375374Sbp SMB_CHECKMINOR(dev); 20475374Sbp if ((sdp->sd_flags & NSMBFL_OPEN) == 0) 20575374Sbp return EBADF; 20675374Sbp 20775374Sbp smb_makescred(&scred, p, NULL); 20875374Sbp switch (cmd) { 20975374Sbp case SMBIOC_OPENSESSION: 21075374Sbp if (sdp->sd_vc) 21175374Sbp return EISCONN; 21275374Sbp error = smb_usr_opensession((struct smbioc_ossn*)data, 21375374Sbp &scred, &vcp); 21475374Sbp if (error) 21575374Sbp break; 21675374Sbp sdp->sd_vc = vcp; 21775374Sbp smb_vc_unlock(vcp, 0, p); 21875374Sbp sdp->sd_level = SMBL_VC; 21975374Sbp break; 22075374Sbp case SMBIOC_OPENSHARE: 22175374Sbp if (sdp->sd_share) 22275374Sbp return EISCONN; 22375374Sbp if (sdp->sd_vc == NULL) 22475374Sbp return ENOTCONN; 22575374Sbp error = smb_usr_openshare(sdp->sd_vc, 22675374Sbp (struct smbioc_oshare*)data, &scred, &ssp); 22775374Sbp if (error) 22875374Sbp break; 22975374Sbp sdp->sd_share = ssp; 23075374Sbp smb_share_unlock(ssp, 0, p); 23175374Sbp sdp->sd_level = SMBL_SHARE; 23275374Sbp break; 23375374Sbp case SMBIOC_REQUEST: 23475374Sbp if (sdp->sd_share == NULL) 23575374Sbp return ENOTCONN; 23675374Sbp error = smb_usr_simplerequest(sdp->sd_share, 23775374Sbp (struct smbioc_rq*)data, &scred); 23875374Sbp break; 23975374Sbp case SMBIOC_T2RQ: 24075374Sbp if (sdp->sd_share == NULL) 24175374Sbp return ENOTCONN; 24275374Sbp error = smb_usr_t2request(sdp->sd_share, 24375374Sbp (struct smbioc_t2rq*)data, &scred); 24475374Sbp break; 24575374Sbp case SMBIOC_SETFLAGS: { 24675374Sbp struct smbioc_flags *fl = (struct smbioc_flags*)data; 24775374Sbp int on; 24875374Sbp 24975374Sbp if (fl->ioc_level == SMBL_VC) { 25075374Sbp if (fl->ioc_mask & SMBV_PERMANENT) { 25175374Sbp on = fl->ioc_flags & SMBV_PERMANENT; 25275374Sbp if ((vcp = sdp->sd_vc) == NULL) 25375374Sbp return ENOTCONN; 25475374Sbp error = smb_vc_get(vcp, LK_EXCLUSIVE, &scred); 25575374Sbp if (error) 25675374Sbp break; 25775374Sbp if (on && (vcp->obj.co_flags & SMBV_PERMANENT) == 0) { 25875374Sbp vcp->obj.co_flags |= SMBV_PERMANENT; 25975374Sbp smb_vc_ref(vcp, p); 26075374Sbp } else if (!on && (vcp->obj.co_flags & SMBV_PERMANENT)) { 26175374Sbp vcp->obj.co_flags &= ~SMBV_PERMANENT; 26275374Sbp smb_vc_rele(vcp, &scred); 26375374Sbp } 26475374Sbp smb_vc_put(vcp, &scred); 26575374Sbp } else 26675374Sbp error = EINVAL; 26775374Sbp } else if (fl->ioc_level == SMBL_SHARE) { 26875374Sbp if (fl->ioc_mask & SMBS_PERMANENT) { 26975374Sbp on = fl->ioc_flags & SMBS_PERMANENT; 27075374Sbp if ((ssp = sdp->sd_share) == NULL) 27175374Sbp return ENOTCONN; 27275374Sbp error = smb_share_get(ssp, LK_EXCLUSIVE, &scred); 27375374Sbp if (error) 27475374Sbp break; 27575374Sbp if (on && (ssp->obj.co_flags & SMBS_PERMANENT) == 0) { 27675374Sbp ssp->obj.co_flags |= SMBS_PERMANENT; 27775374Sbp smb_share_ref(ssp, p); 27875374Sbp } else if (!on && (ssp->obj.co_flags & SMBS_PERMANENT)) { 27975374Sbp ssp->obj.co_flags &= ~SMBS_PERMANENT; 28075374Sbp smb_share_rele(ssp, &scred); 28175374Sbp } 28275374Sbp smb_share_put(ssp, &scred); 28375374Sbp } else 28475374Sbp error = EINVAL; 28575374Sbp break; 28675374Sbp } else 28775374Sbp error = EINVAL; 28875374Sbp break; 28975374Sbp } 29075374Sbp case SMBIOC_LOOKUP: 29175374Sbp if (sdp->sd_vc || sdp->sd_share) 29275374Sbp return EISCONN; 29375374Sbp vcp = NULL; 29475374Sbp ssp = NULL; 29575374Sbp error = smb_usr_lookup((struct smbioc_lookup*)data, &scred, &vcp, &ssp); 29675374Sbp if (error) 29775374Sbp break; 29875374Sbp if (vcp) { 29975374Sbp sdp->sd_vc = vcp; 30075374Sbp smb_vc_unlock(vcp, 0, p); 30175374Sbp sdp->sd_level = SMBL_VC; 30275374Sbp } 30375374Sbp if (ssp) { 30475374Sbp sdp->sd_share = ssp; 30575374Sbp smb_share_unlock(ssp, 0, p); 30675374Sbp sdp->sd_level = SMBL_SHARE; 30775374Sbp } 30875374Sbp break; 30975374Sbp case SMBIOC_READ: case SMBIOC_WRITE: { 31075374Sbp struct smbioc_rw *rwrq = (struct smbioc_rw*)data; 31175374Sbp struct uio auio; 31275374Sbp struct iovec iov; 31375374Sbp 31475374Sbp if ((ssp = sdp->sd_share) == NULL) 31575374Sbp return ENOTCONN; 31675374Sbp iov.iov_base = rwrq->ioc_base; 31775374Sbp iov.iov_len = rwrq->ioc_cnt; 31875374Sbp auio.uio_iov = &iov; 31975374Sbp auio.uio_iovcnt = 1; 32075374Sbp auio.uio_offset = rwrq->ioc_offset; 32175374Sbp auio.uio_resid = rwrq->ioc_cnt; 32275374Sbp auio.uio_segflg = UIO_USERSPACE; 32375374Sbp auio.uio_rw = (cmd == SMBIOC_READ) ? UIO_READ : UIO_WRITE; 32475374Sbp auio.uio_procp = p; 32575374Sbp if (cmd == SMBIOC_READ) 32675374Sbp error = smb_read(ssp, rwrq->ioc_fh, &auio, &scred); 32775374Sbp else 32875374Sbp error = smb_write(ssp, rwrq->ioc_fh, &auio, &scred); 32975374Sbp rwrq->ioc_cnt -= auio.uio_resid; 33075374Sbp break; 33175374Sbp } 33275374Sbp default: 33375374Sbp error = ENODEV; 33475374Sbp } 33575374Sbp return error; 33675374Sbp} 33775374Sbp 33875374Sbpstatic int 33975374Sbpnsmb_dev_read(dev_t dev, struct uio *uio, int flag) 34075374Sbp{ 34175374Sbp return EACCES; 34275374Sbp} 34375374Sbp 34475374Sbpstatic int 34575374Sbpnsmb_dev_write(dev_t dev, struct uio *uio, int flag) 34675374Sbp{ 34775374Sbp return EACCES; 34875374Sbp} 34975374Sbp 35075374Sbpstatic int 35175374Sbpnsmb_dev_poll(dev_t dev, int events, struct proc *p) 35275374Sbp{ 35375374Sbp return ENODEV; 35475374Sbp} 35575374Sbp 35675374Sbpstatic int 35775374Sbpnsmb_dev_load(module_t mod, int cmd, void *arg) 35875374Sbp{ 35975374Sbp int error = 0; 36075374Sbp 36175374Sbp switch (cmd) { 36275374Sbp case MOD_LOAD: 36375374Sbp error = smb_sm_init(); 36475374Sbp if (error) 36575374Sbp break; 36675374Sbp error = smb_iod_init(); 36775374Sbp if (error) { 36875374Sbp smb_sm_done(); 36975374Sbp break; 37075374Sbp } 37175374Sbp#if __FreeBSD_version > 400001 37275374Sbp cdevsw_add(&nsmb_cdevsw); 37375374Sbp#endif 37475374Sbp#if __FreeBSD_version > 500000 37575374Sbp nsmb_dev_tag = EVENTHANDLER_REGISTER(dev_clone, nsmb_dev_clone, 0, 1000); 37675374Sbp#endif 37775374Sbp printf("netsmb_dev: loaded\n"); 37875374Sbp break; 37975374Sbp case MOD_UNLOAD: 38075374Sbp smb_iod_done(); 38175374Sbp error = smb_sm_done(); 38275374Sbp error = 0; 38375374Sbp#if __FreeBSD_version > 500000 38475374Sbp EVENTHANDLER_DEREGISTER(dev_clone, nsmb_dev_tag); 38575374Sbp#endif 38675374Sbp#if __FreeBSD_version > 400001 38775374Sbp cdevsw_remove(&nsmb_cdevsw); 38875374Sbp#endif 38975374Sbp printf("netsmb_dev: unloaded\n"); 39075374Sbp break; 39175374Sbp default: 39275374Sbp error = EINVAL; 39375374Sbp break; 39475374Sbp } 39575374Sbp return error; 39675374Sbp} 39775374Sbp 39875374Sbp#if __FreeBSD_version > 400000 39975374SbpDEV_MODULE (dev_netsmb, nsmb_dev_load, 0); 40075374Sbp#else 40175374SbpCDEV_MODULE(dev_netsmb, NSMB_MAJOR, nsmb_cdevsw, nsmb_dev_load, 0); 40275374Sbp#endif 40375374Sbp 40475374Sbp 40575374Sbp/* 40675374Sbp * Convert a file descriptor to appropriate smb_share pointer 40775374Sbp */ 40875374Sbpstatic struct file* 40975374Sbpnsmb_getfp(struct filedesc* fdp, int fd, int flag) 41075374Sbp{ 41175374Sbp struct file* fp; 41275374Sbp 41375374Sbp if (((u_int)fd) >= fdp->fd_nfiles || 41475374Sbp (fp = fdp->fd_ofiles[fd]) == NULL || 41575374Sbp (fp->f_flag & flag) == 0) 41675374Sbp return (NULL); 41775374Sbp return (fp); 41875374Sbp} 41975374Sbp 42075374Sbpint 42175374Sbpsmb_dev2share(int fd, int mode, struct smb_cred *scred, 42275374Sbp struct smb_share **sspp) 42375374Sbp{ 42475374Sbp struct file *fp; 42575374Sbp struct vnode *vp; 42675374Sbp struct smb_dev *sdp; 42775374Sbp struct smb_share *ssp; 42875374Sbp dev_t dev; 42975374Sbp int error; 43075374Sbp 43175374Sbp if ((fp = nsmb_getfp(scred->scr_p->p_fd, fd, FREAD | FWRITE)) == NULL) 43275374Sbp return EBADF; 43375374Sbp vp = (struct vnode*)fp->f_data; 43475374Sbp if (vp == NULL) 43575374Sbp return EBADF; 43675374Sbp dev = vn_todev(vp); 43775374Sbp if (dev == NODEV) 43875374Sbp return EBADF; 43975374Sbp SMB_CHECKMINOR(dev); 44075374Sbp ssp = sdp->sd_share; 44175374Sbp if (ssp == NULL) 44275374Sbp return ENOTCONN; 44375374Sbp error = smb_share_get(ssp, LK_EXCLUSIVE, scred); 44475374Sbp if (error) 44575374Sbp return error; 44675374Sbp *sspp = ssp; 44775374Sbp return 0; 44875374Sbp} 44975374Sbp 450