| Commit | Line | Data |
|---|---|---|
| 984263bc MD |
1 | /* |
| 2 | * Copyright (c) 2000-2001 Boris Popov | |
| 3 | * All rights reserved. | |
| 4 | * | |
| 5 | * Redistribution and use in source and binary forms, with or without | |
| 6 | * modification, are permitted provided that the following conditions | |
| 7 | * are met: | |
| 8 | * 1. Redistributions of source code must retain the above copyright | |
| 9 | * notice, this list of conditions and the following disclaimer. | |
| 10 | * 2. Redistributions in binary form must reproduce the above copyright | |
| 11 | * notice, this list of conditions and the following disclaimer in the | |
| 12 | * documentation and/or other materials provided with the distribution. | |
| 13 | * 3. All advertising materials mentioning features or use of this software | |
| 14 | * must display the following acknowledgement: | |
| 15 | * This product includes software developed by Boris Popov. | |
| 16 | * 4. Neither the name of the author nor the names of any co-contributors | |
| 17 | * may be used to endorse or promote products derived from this software | |
| 18 | * without specific prior written permission. | |
| 19 | * | |
| 20 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND | |
| 21 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
| 22 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
| 23 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE | |
| 24 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
| 25 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
| 26 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
| 27 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
| 28 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
| 29 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
| 30 | * SUCH DAMAGE. | |
| 31 | * | |
| 32 | * $FreeBSD: src/sys/netsmb/smb_dev.c,v 1.2.2.1 2001/05/22 08:32:33 bp Exp $ | |
| 028066b1 | 33 | * $DragonFly: src/sys/netproto/smb/smb_dev.c,v 1.18 2007/05/08 02:31:42 dillon Exp $ |
| 984263bc MD |
34 | */ |
| 35 | #include <sys/param.h> | |
| 36 | #include <sys/kernel.h> | |
| 37 | #include <sys/systm.h> | |
| 38 | #include <sys/conf.h> | |
| fef8985e | 39 | #include <sys/device.h> |
| 984263bc | 40 | #include <sys/fcntl.h> |
| 984263bc MD |
41 | #include <sys/malloc.h> |
| 42 | #include <sys/file.h> /* Must come after sys/malloc.h */ | |
| 43 | #include <sys/poll.h> | |
| 44 | #include <sys/proc.h> | |
| 45 | #include <sys/select.h> | |
| 46 | #include <sys/socket.h> | |
| 47 | #include <sys/socketvar.h> | |
| 48 | #include <sys/sysctl.h> | |
| 49 | #include <sys/uio.h> | |
| 50 | #include <sys/vnode.h> | |
| 636ae7e8 | 51 | #include <sys/thread2.h> |
| 984263bc MD |
52 | |
| 53 | #include <net/if.h> | |
| 54 | ||
| 1f2de5d4 MD |
55 | #include "smb.h" |
| 56 | #include "smb_conn.h" | |
| 57 | #include "smb_subr.h" | |
| 58 | #include "smb_dev.h" | |
| 984263bc MD |
59 | |
| 60 | #define SMB_GETDEV(dev) ((struct smb_dev*)(dev)->si_drv1) | |
| 61 | #define SMB_CHECKMINOR(dev) do { \ | |
| 62 | sdp = SMB_GETDEV(dev); \ | |
| 63 | if (sdp == NULL) return ENXIO; \ | |
| 64 | } while(0) | |
| 65 | ||
| 66 | static d_open_t nsmb_dev_open; | |
| 67 | static d_close_t nsmb_dev_close; | |
| 68 | static d_read_t nsmb_dev_read; | |
| 69 | static d_write_t nsmb_dev_write; | |
| 70 | static d_ioctl_t nsmb_dev_ioctl; | |
| 71 | static d_poll_t nsmb_dev_poll; | |
| 72 | ||
| 73 | MODULE_VERSION(netsmb, NSMB_VERSION); | |
| 74 | ||
| 75 | #define SI_NAMED 0 | |
| 76 | ||
| 77 | static int smb_version = NSMB_VERSION; | |
| 78 | ||
| 79 | ||
| 80 | SYSCTL_DECL(_net_smb); | |
| 81 | SYSCTL_INT(_net_smb, OID_AUTO, version, CTLFLAG_RD, &smb_version, 0, ""); | |
| 82 | ||
| 83 | static MALLOC_DEFINE(M_NSMBDEV, "NETSMBDEV", "NET/SMB device"); | |
| 84 | ||
| 85 | ||
| 86 | /* | |
| 87 | int smb_dev_queue(struct smb_dev *ndp, struct smb_rq *rqp, int prio); | |
| 88 | */ | |
| 89 | ||
| fef8985e MD |
90 | static struct dev_ops nsmb_ops = { |
| 91 | { NSMB_NAME, NSMB_MAJOR, 0 }, | |
| 92 | .d_open = nsmb_dev_open, | |
| 93 | .d_close = nsmb_dev_close, | |
| 94 | .d_read = nsmb_dev_read, | |
| 95 | .d_write = nsmb_dev_write, | |
| 96 | .d_ioctl = nsmb_dev_ioctl, | |
| 97 | .d_poll = nsmb_dev_poll, | |
| 984263bc MD |
98 | }; |
| 99 | ||
| 100 | ||
| 101 | static int | |
| fef8985e | 102 | nsmb_dev_open(struct dev_open_args *ap) |
| 984263bc | 103 | { |
| b13267a5 | 104 | cdev_t dev = ap->a_head.a_dev; |
| 984263bc | 105 | struct smb_dev *sdp; |
| dadab5e9 | 106 | |
| 984263bc MD |
107 | sdp = SMB_GETDEV(dev); |
| 108 | if (sdp && (sdp->sd_flags & NSMBFL_OPEN)) | |
| 109 | return EBUSY; | |
| 110 | if (sdp == NULL) { | |
| efda3bd0 | 111 | sdp = kmalloc(sizeof(*sdp), M_NSMBDEV, M_WAITOK); |
| 984263bc MD |
112 | dev->si_drv1 = (void*)sdp; |
| 113 | } | |
| 114 | /* | |
| e4c9c0c8 MD |
115 | * XXX: this is just crazy - make a device for an already passed |
| 116 | * device... someone should take care of it. | |
| 984263bc | 117 | */ |
| e4c9c0c8 | 118 | if ((dev->si_flags & SI_NAMED) == 0) { |
| fef8985e MD |
119 | make_dev(&nsmb_ops, minor(dev), |
| 120 | ap->a_cred->cr_uid, ap->a_cred->cr_gid, | |
| e4c9c0c8 MD |
121 | 0700, NSMB_NAME"%d", lminor(dev)); |
| 122 | } | |
| 984263bc MD |
123 | bzero(sdp, sizeof(*sdp)); |
| 124 | /* | |
| 125 | STAILQ_INIT(&sdp->sd_rqlist); | |
| 126 | STAILQ_INIT(&sdp->sd_rplist); | |
| 127 | bzero(&sdp->sd_pollinfo, sizeof(struct selinfo)); | |
| 128 | */ | |
| 636ae7e8 | 129 | crit_enter(); |
| 984263bc MD |
130 | sdp->sd_level = -1; |
| 131 | sdp->sd_flags |= NSMBFL_OPEN; | |
| 636ae7e8 | 132 | crit_exit(); |
| 984263bc MD |
133 | return 0; |
| 134 | } | |
| 135 | ||
| 136 | static int | |
| fef8985e | 137 | nsmb_dev_close(struct dev_close_args *ap) |
| 984263bc | 138 | { |
| b13267a5 | 139 | cdev_t dev = ap->a_head.a_dev; |
| 984263bc MD |
140 | struct smb_dev *sdp; |
| 141 | struct smb_vc *vcp; | |
| 142 | struct smb_share *ssp; | |
| 143 | struct smb_cred scred; | |
| 984263bc MD |
144 | |
| 145 | SMB_CHECKMINOR(dev); | |
| 636ae7e8 | 146 | crit_enter(); |
| 984263bc | 147 | if ((sdp->sd_flags & NSMBFL_OPEN) == 0) { |
| 636ae7e8 | 148 | crit_exit(); |
| 984263bc MD |
149 | return EBADF; |
| 150 | } | |
| fef8985e | 151 | smb_makescred(&scred, curthread, NULL); |
| 984263bc MD |
152 | ssp = sdp->sd_share; |
| 153 | if (ssp != NULL) | |
| 154 | smb_share_rele(ssp, &scred); | |
| 155 | vcp = sdp->sd_vc; | |
| 156 | if (vcp != NULL) | |
| 157 | smb_vc_rele(vcp, &scred); | |
| 158 | /* | |
| 159 | smb_flushq(&sdp->sd_rqlist); | |
| 160 | smb_flushq(&sdp->sd_rplist); | |
| 161 | */ | |
| 162 | dev->si_drv1 = NULL; | |
| efda3bd0 | 163 | kfree(sdp, M_NSMBDEV); |
| 636ae7e8 | 164 | crit_exit(); |
| 984263bc MD |
165 | return 0; |
| 166 | } | |
| 167 | ||
| 168 | ||
| 169 | static int | |
| fef8985e | 170 | nsmb_dev_ioctl(struct dev_ioctl_args *ap) |
| 984263bc | 171 | { |
| b13267a5 | 172 | cdev_t dev = ap->a_head.a_dev; |
| fef8985e | 173 | caddr_t data = ap->a_data; |
| 984263bc MD |
174 | struct smb_dev *sdp; |
| 175 | struct smb_vc *vcp; | |
| 176 | struct smb_share *ssp; | |
| 177 | struct smb_cred scred; | |
| 178 | int error = 0; | |
| 179 | ||
| 180 | SMB_CHECKMINOR(dev); | |
| 181 | if ((sdp->sd_flags & NSMBFL_OPEN) == 0) | |
| 182 | return EBADF; | |
| 183 | ||
| fef8985e MD |
184 | smb_makescred(&scred, NULL, ap->a_cred); |
| 185 | switch (ap->a_cmd) { | |
| 984263bc MD |
186 | case SMBIOC_OPENSESSION: |
| 187 | if (sdp->sd_vc) | |
| 188 | return EISCONN; | |
| 189 | error = smb_usr_opensession((struct smbioc_ossn*)data, | |
| 190 | &scred, &vcp); | |
| 191 | if (error) | |
| 192 | break; | |
| 193 | sdp->sd_vc = vcp; | |
| fef8985e | 194 | smb_vc_unlock(vcp, 0); |
| 984263bc MD |
195 | sdp->sd_level = SMBL_VC; |
| 196 | break; | |
| 197 | case SMBIOC_OPENSHARE: | |
| 198 | if (sdp->sd_share) | |
| 199 | return EISCONN; | |
| 200 | if (sdp->sd_vc == NULL) | |
| 201 | return ENOTCONN; | |
| 202 | error = smb_usr_openshare(sdp->sd_vc, | |
| 203 | (struct smbioc_oshare*)data, &scred, &ssp); | |
| 204 | if (error) | |
| 205 | break; | |
| 206 | sdp->sd_share = ssp; | |
| fef8985e | 207 | smb_share_unlock(ssp, 0); |
| 984263bc MD |
208 | sdp->sd_level = SMBL_SHARE; |
| 209 | break; | |
| 210 | case SMBIOC_REQUEST: | |
| 211 | if (sdp->sd_share == NULL) | |
| 212 | return ENOTCONN; | |
| 213 | error = smb_usr_simplerequest(sdp->sd_share, | |
| 214 | (struct smbioc_rq*)data, &scred); | |
| 215 | break; | |
| 216 | case SMBIOC_T2RQ: | |
| 217 | if (sdp->sd_share == NULL) | |
| 218 | return ENOTCONN; | |
| 219 | error = smb_usr_t2request(sdp->sd_share, | |
| 220 | (struct smbioc_t2rq*)data, &scred); | |
| 221 | break; | |
| 222 | case SMBIOC_SETFLAGS: { | |
| 223 | struct smbioc_flags *fl = (struct smbioc_flags*)data; | |
| 224 | int on; | |
| 225 | ||
| 226 | if (fl->ioc_level == SMBL_VC) { | |
| 227 | if (fl->ioc_mask & SMBV_PERMANENT) { | |
| 228 | on = fl->ioc_flags & SMBV_PERMANENT; | |
| 229 | if ((vcp = sdp->sd_vc) == NULL) | |
| 230 | return ENOTCONN; | |
| 231 | error = smb_vc_get(vcp, LK_EXCLUSIVE, &scred); | |
| 232 | if (error) | |
| 233 | break; | |
| 234 | if (on && (vcp->obj.co_flags & SMBV_PERMANENT) == 0) { | |
| 235 | vcp->obj.co_flags |= SMBV_PERMANENT; | |
| fef8985e | 236 | smb_vc_ref(vcp); |
| 984263bc MD |
237 | } else if (!on && (vcp->obj.co_flags & SMBV_PERMANENT)) { |
| 238 | vcp->obj.co_flags &= ~SMBV_PERMANENT; | |
| 239 | smb_vc_rele(vcp, &scred); | |
| 240 | } | |
| 241 | smb_vc_put(vcp, &scred); | |
| 242 | } else | |
| 243 | error = EINVAL; | |
| 244 | } else if (fl->ioc_level == SMBL_SHARE) { | |
| 245 | if (fl->ioc_mask & SMBS_PERMANENT) { | |
| 246 | on = fl->ioc_flags & SMBS_PERMANENT; | |
| 247 | if ((ssp = sdp->sd_share) == NULL) | |
| 248 | return ENOTCONN; | |
| 249 | error = smb_share_get(ssp, LK_EXCLUSIVE, &scred); | |
| 250 | if (error) | |
| 251 | break; | |
| 252 | if (on && (ssp->obj.co_flags & SMBS_PERMANENT) == 0) { | |
| 253 | ssp->obj.co_flags |= SMBS_PERMANENT; | |
| fef8985e | 254 | smb_share_ref(ssp); |
| 984263bc MD |
255 | } else if (!on && (ssp->obj.co_flags & SMBS_PERMANENT)) { |
| 256 | ssp->obj.co_flags &= ~SMBS_PERMANENT; | |
| 257 | smb_share_rele(ssp, &scred); | |
| 258 | } | |
| 259 | smb_share_put(ssp, &scred); | |
| 260 | } else | |
| 261 | error = EINVAL; | |
| 262 | break; | |
| 263 | } else | |
| 264 | error = EINVAL; | |
| 265 | break; | |
| 266 | } | |
| 267 | case SMBIOC_LOOKUP: | |
| 268 | if (sdp->sd_vc || sdp->sd_share) | |
| 269 | return EISCONN; | |
| 270 | vcp = NULL; | |
| 271 | ssp = NULL; | |
| 272 | error = smb_usr_lookup((struct smbioc_lookup*)data, &scred, &vcp, &ssp); | |
| 273 | if (error) | |
| 274 | break; | |
| 275 | if (vcp) { | |
| 276 | sdp->sd_vc = vcp; | |
| fef8985e | 277 | smb_vc_unlock(vcp, 0); |
| 984263bc MD |
278 | sdp->sd_level = SMBL_VC; |
| 279 | } | |
| 280 | if (ssp) { | |
| 281 | sdp->sd_share = ssp; | |
| fef8985e | 282 | smb_share_unlock(ssp, 0); |
| 984263bc MD |
283 | sdp->sd_level = SMBL_SHARE; |
| 284 | } | |
| 285 | break; | |
| 286 | case SMBIOC_READ: case SMBIOC_WRITE: { | |
| 287 | struct smbioc_rw *rwrq = (struct smbioc_rw*)data; | |
| 288 | struct uio auio; | |
| 289 | struct iovec iov; | |
| 290 | ||
| 291 | if ((ssp = sdp->sd_share) == NULL) | |
| 292 | return ENOTCONN; | |
| 293 | iov.iov_base = rwrq->ioc_base; | |
| 294 | iov.iov_len = rwrq->ioc_cnt; | |
| 295 | auio.uio_iov = &iov; | |
| 296 | auio.uio_iovcnt = 1; | |
| 297 | auio.uio_offset = rwrq->ioc_offset; | |
| 298 | auio.uio_resid = rwrq->ioc_cnt; | |
| 299 | auio.uio_segflg = UIO_USERSPACE; | |
| fef8985e MD |
300 | auio.uio_rw = (ap->a_cmd == SMBIOC_READ) ? UIO_READ : UIO_WRITE; |
| 301 | auio.uio_td = curthread; | |
| 302 | if (ap->a_cmd == SMBIOC_READ) | |
| 984263bc MD |
303 | error = smb_read(ssp, rwrq->ioc_fh, &auio, &scred); |
| 304 | else | |
| 305 | error = smb_write(ssp, rwrq->ioc_fh, &auio, &scred); | |
| 306 | rwrq->ioc_cnt -= auio.uio_resid; | |
| 307 | break; | |
| 308 | } | |
| 309 | default: | |
| 310 | error = ENODEV; | |
| 311 | } | |
| 312 | return error; | |
| 313 | } | |
| 314 | ||
| 315 | static int | |
| fef8985e | 316 | nsmb_dev_read(struct dev_read_args *ap) |
| 984263bc MD |
317 | { |
| 318 | return EACCES; | |
| 319 | } | |
| 320 | ||
| 321 | static int | |
| fef8985e | 322 | nsmb_dev_write(struct dev_write_args *ap) |
| 984263bc MD |
323 | { |
| 324 | return EACCES; | |
| 325 | } | |
| 326 | ||
| 327 | static int | |
| fef8985e | 328 | nsmb_dev_poll(struct dev_poll_args *ap) |
| 984263bc MD |
329 | { |
| 330 | return ENODEV; | |
| 331 | } | |
| 332 | ||
| 333 | static int | |
| 334 | nsmb_dev_load(module_t mod, int cmd, void *arg) | |
| 335 | { | |
| 336 | int error = 0; | |
| 337 | ||
| 338 | switch (cmd) { | |
| 339 | case MOD_LOAD: | |
| 984263bc MD |
340 | error = smb_sm_init(); |
| 341 | if (error) | |
| 342 | break; | |
| 343 | error = smb_iod_init(); | |
| 344 | if (error) { | |
| 345 | smb_sm_done(); | |
| 346 | break; | |
| 347 | } | |
| fef8985e | 348 | dev_ops_add(&nsmb_ops, 0, 0); |
| a6ec04bc | 349 | kprintf("netsmb_dev: loaded\n"); |
| 984263bc MD |
350 | break; |
| 351 | case MOD_UNLOAD: | |
| 352 | smb_iod_done(); | |
| 353 | error = smb_sm_done(); | |
| 354 | error = 0; | |
| cd29885a | 355 | dev_ops_remove_all(&nsmb_ops); |
| a6ec04bc | 356 | kprintf("netsmb_dev: unloaded\n"); |
| 984263bc MD |
357 | break; |
| 358 | default: | |
| 359 | error = EINVAL; | |
| 360 | break; | |
| 361 | } | |
| 362 | return error; | |
| 363 | } | |
| 364 | ||
| 365 | DEV_MODULE (dev_netsmb, nsmb_dev_load, 0); | |
| 366 | ||
| 984263bc MD |
367 | int |
| 368 | smb_dev2share(int fd, int mode, struct smb_cred *scred, | |
| 369 | struct smb_share **sspp) | |
| 370 | { | |
| 371 | struct file *fp; | |
| 372 | struct vnode *vp; | |
| 373 | struct smb_dev *sdp; | |
| 374 | struct smb_share *ssp; | |
| b13267a5 | 375 | cdev_t dev; |
| 984263bc MD |
376 | int error; |
| 377 | ||
| dadab5e9 MD |
378 | KKASSERT(scred->scr_td->td_proc); |
| 379 | ||
| 228b401d MD |
380 | fp = holdfp(scred->scr_td->td_proc->p_fd, fd, FREAD|FWRITE); |
| 381 | if (fp == NULL) | |
| 984263bc | 382 | return EBADF; |
| 228b401d | 383 | |
| 984263bc | 384 | vp = (struct vnode*)fp->f_data; |
| 228b401d MD |
385 | if (vp == NULL) { |
| 386 | error = EBADF; | |
| 387 | goto done; | |
| 388 | } | |
| 984263bc | 389 | dev = vn_todev(vp); |
| 028066b1 | 390 | if (dev == NULL) { |
| 228b401d MD |
391 | error = EBADF; |
| 392 | goto done; | |
| 393 | } | |
| 984263bc MD |
394 | SMB_CHECKMINOR(dev); |
| 395 | ssp = sdp->sd_share; | |
| 228b401d MD |
396 | if (ssp == NULL) { |
| 397 | error = ENOTCONN; | |
| 398 | goto done; | |
| 399 | } | |
| 984263bc | 400 | error = smb_share_get(ssp, LK_EXCLUSIVE, scred); |
| 228b401d MD |
401 | if (error == 0) |
| 402 | *sspp = ssp; | |
| 403 | done: | |
| 404 | fdrop(fp); | |
| 405 | return (error); | |
| 984263bc MD |
406 | } |
| 407 |