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$ 33 */ 34 #include <sys/param.h> 35 #include <sys/systm.h> 36 #include <sys/namei.h> 37 #include <sys/kernel.h> 38 #include <sys/proc.h> 39 #include <sys/bio.h> 40 #include <sys/buf.h> 41 #include <sys/fcntl.h> 42 #include <sys/mount.h> 43 #include <sys/unistd.h> 44 #include <sys/vnode.h> 45 #include <sys/limits.h> 46 #include <sys/lockf.h> 47 48 #include <vm/vm.h> 49 #include <vm/vm_extern.h> 50 51 52 #include <netsmb/smb.h> 53 #include <netsmb/smb_conn.h> 54 #include <netsmb/smb_subr.h> 55 56 #include <fs/smbfs/smbfs.h> 57 #include <fs/smbfs/smbfs_node.h> 58 #include <fs/smbfs/smbfs_subr.h> 59 60 /* 61 * Prototypes for SMBFS vnode operations 62 */ 63 static vop_create_t smbfs_create; 64 static vop_mknod_t smbfs_mknod; 65 static vop_open_t smbfs_open; 66 static vop_close_t smbfs_close; 67 static vop_access_t smbfs_access; 68 static vop_getattr_t smbfs_getattr; 69 static vop_setattr_t smbfs_setattr; 70 static vop_read_t smbfs_read; 71 static vop_write_t smbfs_write; 72 static vop_fsync_t smbfs_fsync; 73 static vop_remove_t smbfs_remove; 74 static vop_link_t smbfs_link; 75 static vop_lookup_t smbfs_lookup; 76 static vop_rename_t smbfs_rename; 77 static vop_mkdir_t smbfs_mkdir; 78 static vop_rmdir_t smbfs_rmdir; 79 static vop_symlink_t smbfs_symlink; 80 static vop_readdir_t smbfs_readdir; 81 static vop_strategy_t smbfs_strategy; 82 static vop_print_t smbfs_print; 83 static vop_pathconf_t smbfs_pathconf; 84 static vop_advlock_t smbfs_advlock; 85 static vop_getextattr_t smbfs_getextattr; 86 87 struct vop_vector smbfs_vnodeops = { 88 .vop_default = &default_vnodeops, 89 90 .vop_access = smbfs_access, 91 .vop_advlock = smbfs_advlock, 92 .vop_close = smbfs_close, 93 .vop_create = smbfs_create, 94 .vop_fsync = smbfs_fsync, 95 .vop_getattr = smbfs_getattr, 96 .vop_getextattr = smbfs_getextattr, 97 .vop_getpages = smbfs_getpages, 98 .vop_inactive = smbfs_inactive, 99 .vop_ioctl = smbfs_ioctl, 100 .vop_link = smbfs_link, 101 .vop_lookup = smbfs_lookup, 102 .vop_mkdir = smbfs_mkdir, 103 .vop_mknod = smbfs_mknod, 104 .vop_open = smbfs_open, 105 .vop_pathconf = smbfs_pathconf, 106 .vop_print = smbfs_print, 107 .vop_putpages = smbfs_putpages, 108 .vop_read = smbfs_read, 109 .vop_readdir = smbfs_readdir, 110 .vop_reclaim = smbfs_reclaim, 111 .vop_remove = smbfs_remove, 112 .vop_rename = smbfs_rename, 113 .vop_rmdir = smbfs_rmdir, 114 .vop_setattr = smbfs_setattr, 115 /* .vop_setextattr = smbfs_setextattr,*/ 116 .vop_strategy = smbfs_strategy, 117 .vop_symlink = smbfs_symlink, 118 .vop_write = smbfs_write, 119 }; 120 121 static int 122 smbfs_access(ap) 123 struct vop_access_args /* { 124 struct vnode *a_vp; 125 int a_mode; 126 struct ucred *a_cred; 127 struct thread *a_td; 128 } */ *ap; 129 { 130 struct vnode *vp = ap->a_vp; 131 mode_t mode = ap->a_mode; 132 mode_t mpmode; 133 struct smbmount *smp = VTOSMBFS(vp); 134 135 SMBVDEBUG("\n"); 136 if ((mode & VWRITE) && (vp->v_mount->mnt_flag & MNT_RDONLY)) { 137 switch (vp->v_type) { 138 case VREG: case VDIR: case VLNK: 139 return EROFS; 140 default: 141 break; 142 } 143 } 144 mpmode = vp->v_type == VREG ? smp->sm_file_mode : smp->sm_dir_mode; 145 return (vaccess(vp->v_type, mpmode, smp->sm_uid, 146 smp->sm_gid, ap->a_mode, ap->a_cred, NULL)); 147 } 148 149 /* ARGSUSED */ 150 static int 151 smbfs_open(ap) 152 struct vop_open_args /* { 153 struct vnode *a_vp; 154 int a_mode; 155 struct ucred *a_cred; 156 struct thread *a_td; 157 } */ *ap; 158 { 159 struct vnode *vp = ap->a_vp; 160 struct smbnode *np = VTOSMB(vp); 161 struct smb_cred scred; 162 struct vattr vattr; 163 int mode = ap->a_mode; 164 int error, accmode; 165 166 SMBVDEBUG("%s,%d\n", np->n_name, (np->n_flag & NOPEN) != 0); 167 if (vp->v_type != VREG && vp->v_type != VDIR) { 168 SMBFSERR("open eacces vtype=%d\n", vp->v_type); 169 return EACCES; 170 } 171 if (vp->v_type == VDIR) { 172 np->n_flag |= NOPEN; 173 return 0; 174 } 175 if (np->n_flag & NMODIFIED) { 176 if ((error = smbfs_vinvalbuf(vp, ap->a_td)) == EINTR) 177 return error; 178 smbfs_attr_cacheremove(vp); 179 error = VOP_GETATTR(vp, &vattr, ap->a_cred, ap->a_td); 180 if (error) 181 return error; 182 np->n_mtime.tv_sec = vattr.va_mtime.tv_sec; 183 } else { 184 error = VOP_GETATTR(vp, &vattr, ap->a_cred, ap->a_td); 185 if (error) 186 return error; 187 if (np->n_mtime.tv_sec != vattr.va_mtime.tv_sec) { 188 error = smbfs_vinvalbuf(vp, ap->a_td); 189 if (error == EINTR) 190 return error; 191 np->n_mtime.tv_sec = vattr.va_mtime.tv_sec; 192 } 193 } 194 if ((np->n_flag & NOPEN) != 0) 195 return 0; 196 /* 197 * Use DENYNONE to give unixy semantics of permitting 198 * everything not forbidden by permissions. Ie denial 199 * is up to server with clients/openers needing to use 200 * advisory locks for further control. 201 */ 202 accmode = SMB_SM_DENYNONE|SMB_AM_OPENREAD; 203 if ((vp->v_mount->mnt_flag & MNT_RDONLY) == 0) 204 accmode = SMB_SM_DENYNONE|SMB_AM_OPENRW; 205 smb_makescred(&scred, ap->a_td, ap->a_cred); 206 error = smbfs_smb_open(np, accmode, &scred); 207 if (error) { 208 if (mode & FWRITE) 209 return EACCES; 210 else if ((vp->v_mount->mnt_flag & MNT_RDONLY) == 0) { 211 accmode = SMB_SM_DENYNONE|SMB_AM_OPENREAD; 212 error = smbfs_smb_open(np, accmode, &scred); 213 } 214 } 215 if (error == 0) { 216 np->n_flag |= NOPEN; 217 vnode_create_vobject(ap->a_vp, vattr.va_size, ap->a_td); 218 } 219 smbfs_attr_cacheremove(vp); 220 return error; 221 } 222 223 static int 224 smbfs_close(ap) 225 struct vop_close_args /* { 226 struct vnodeop_desc *a_desc; 227 struct vnode *a_vp; 228 int a_fflag; 229 struct ucred *a_cred; 230 struct thread *a_td; 231 } */ *ap; 232 { 233 struct vnode *vp = ap->a_vp; 234 struct thread *td = ap->a_td; 235 struct smbnode *np = VTOSMB(vp); 236 struct smb_cred scred; 237 238 if (vp->v_type == VDIR && (np->n_flag & NOPEN) != 0 && 239 np->n_dirseq != NULL) { 240 smb_makescred(&scred, td, ap->a_cred); 241 smbfs_findclose(np->n_dirseq, &scred); 242 np->n_dirseq = NULL; 243 } 244 return 0; 245 } 246 247 /* 248 * smbfs_getattr call from vfs. 249 */ 250 static int 251 smbfs_getattr(ap) 252 struct vop_getattr_args /* { 253 struct vnode *a_vp; 254 struct vattr *a_vap; 255 struct ucred *a_cred; 256 struct thread *a_td; 257 } */ *ap; 258 { 259 struct vnode *vp = ap->a_vp; 260 struct smbnode *np = VTOSMB(vp); 261 struct vattr *va=ap->a_vap; 262 struct smbfattr fattr; 263 struct smb_cred scred; 264 u_quad_t oldsize; 265 int error; 266 267 SMBVDEBUG("%lx: '%s' %d\n", (long)vp, np->n_name, (vp->v_vflag & VV_ROOT) != 0); 268 error = smbfs_attr_cachelookup(vp, va); 269 if (!error) 270 return 0; 271 SMBVDEBUG("not in the cache\n"); 272 smb_makescred(&scred, ap->a_td, ap->a_cred); 273 oldsize = np->n_size; 274 error = smbfs_smb_lookup(np, NULL, 0, &fattr, &scred); 275 if (error) { 276 SMBVDEBUG("error %d\n", error); 277 return error; 278 } 279 smbfs_attr_cacheenter(vp, &fattr); 280 smbfs_attr_cachelookup(vp, va); 281 if (np->n_flag & NOPEN) 282 np->n_size = oldsize; 283 return 0; 284 } 285 286 static int 287 smbfs_setattr(ap) 288 struct vop_setattr_args /* { 289 struct vnode *a_vp; 290 struct vattr *a_vap; 291 struct ucred *a_cred; 292 struct thread *a_td; 293 } */ *ap; 294 { 295 struct vnode *vp = ap->a_vp; 296 struct smbnode *np = VTOSMB(vp); 297 struct vattr *vap = ap->a_vap; 298 struct timespec *mtime, *atime; 299 struct smb_cred scred; 300 struct smb_share *ssp = np->n_mount->sm_share; 301 struct smb_vc *vcp = SSTOVC(ssp); 302 u_quad_t tsize = 0; 303 int isreadonly, doclose, error = 0; 304 305 SMBVDEBUG("\n"); 306 if (vap->va_flags != VNOVAL) 307 return EOPNOTSUPP; 308 isreadonly = (vp->v_mount->mnt_flag & MNT_RDONLY); 309 /* 310 * Disallow write attempts if the filesystem is mounted read-only. 311 */ 312 if ((vap->va_uid != (uid_t)VNOVAL || vap->va_gid != (gid_t)VNOVAL || 313 vap->va_atime.tv_sec != VNOVAL || vap->va_mtime.tv_sec != VNOVAL || 314 vap->va_mode != (mode_t)VNOVAL) && isreadonly) 315 return EROFS; 316 smb_makescred(&scred, ap->a_td, ap->a_cred); 317 if (vap->va_size != VNOVAL) { 318 switch (vp->v_type) { 319 case VDIR: 320 return EISDIR; 321 case VREG: 322 break; 323 default: 324 return EINVAL; 325 }; 326 if (isreadonly) 327 return EROFS; 328 doclose = 0; 329 vnode_pager_setsize(vp, (u_long)vap->va_size); 330 tsize = np->n_size; 331 np->n_size = vap->va_size; 332 if ((np->n_flag & NOPEN) == 0) { 333 error = smbfs_smb_open(np, 334 SMB_SM_DENYNONE|SMB_AM_OPENRW, 335 &scred); 336 if (error == 0) 337 doclose = 1; 338 } 339 if (error == 0) 340 error = smbfs_smb_setfsize(np, vap->va_size, &scred); 341 if (doclose) 342 smbfs_smb_close(ssp, np->n_fid, NULL, &scred); 343 if (error) { 344 np->n_size = tsize; 345 vnode_pager_setsize(vp, (u_long)tsize); 346 return error; 347 } 348 } 349 mtime = atime = NULL; 350 if (vap->va_mtime.tv_sec != VNOVAL) 351 mtime = &vap->va_mtime; 352 if (vap->va_atime.tv_sec != VNOVAL) 353 atime = &vap->va_atime; 354 if (mtime != atime) { 355 if (ap->a_cred->cr_uid != VTOSMBFS(vp)->sm_uid && 356 (error = suser_cred(ap->a_cred, SUSER_ALLOWJAIL)) && 357 ((vap->va_vaflags & VA_UTIMES_NULL) == 0 || 358 (error = VOP_ACCESS(vp, VWRITE, ap->a_cred, ap->a_td)))) 359 return (error); 360 #if 0 361 if (mtime == NULL) 362 mtime = &np->n_mtime; 363 if (atime == NULL) 364 atime = &np->n_atime; 365 #endif 366 /* 367 * If file is opened, then we can use handle based calls. 368 * If not, use path based ones. 369 */ 370 if ((np->n_flag & NOPEN) == 0) { 371 if (vcp->vc_flags & SMBV_WIN95) { 372 error = VOP_OPEN(vp, FWRITE, ap->a_cred, ap->a_td, -1); 373 if (!error) { 374 /* error = smbfs_smb_setfattrNT(np, 0, mtime, atime, &scred); 375 VOP_GETATTR(vp, &vattr, ap->a_cred, ap->a_td);*/ 376 if (mtime) 377 np->n_mtime = *mtime; 378 VOP_CLOSE(vp, FWRITE, ap->a_cred, ap->a_td); 379 } 380 } else if ((vcp->vc_sopt.sv_caps & SMB_CAP_NT_SMBS)) { 381 error = smbfs_smb_setptime2(np, mtime, atime, 0, &scred); 382 /* error = smbfs_smb_setpattrNT(np, 0, mtime, atime, &scred);*/ 383 } else if (SMB_DIALECT(vcp) >= SMB_DIALECT_LANMAN2_0) { 384 error = smbfs_smb_setptime2(np, mtime, atime, 0, &scred); 385 } else { 386 error = smbfs_smb_setpattr(np, 0, mtime, &scred); 387 } 388 } else { 389 if (vcp->vc_sopt.sv_caps & SMB_CAP_NT_SMBS) { 390 error = smbfs_smb_setfattrNT(np, 0, mtime, atime, &scred); 391 } else if (SMB_DIALECT(vcp) >= SMB_DIALECT_LANMAN1_0) { 392 error = smbfs_smb_setftime(np, mtime, atime, &scred); 393 } else { 394 /* 395 * I have no idea how to handle this for core 396 * level servers. The possible solution is to 397 * update mtime after file is closed. 398 */ 399 SMBERROR("can't update times on an opened file\n"); 400 } 401 } 402 } 403 /* 404 * Invalidate attribute cache in case if server doesn't set 405 * required attributes. 406 */ 407 smbfs_attr_cacheremove(vp); /* invalidate cache */ 408 VOP_GETATTR(vp, vap, ap->a_cred, ap->a_td); 409 np->n_mtime.tv_sec = vap->va_mtime.tv_sec; 410 return error; 411 } 412 /* 413 * smbfs_read call. 414 */ 415 static int 416 smbfs_read(ap) 417 struct vop_read_args /* { 418 struct vnode *a_vp; 419 struct uio *a_uio; 420 int a_ioflag; 421 struct ucred *a_cred; 422 } */ *ap; 423 { 424 struct vnode *vp = ap->a_vp; 425 struct uio *uio = ap->a_uio; 426 427 SMBVDEBUG("\n"); 428 if (vp->v_type != VREG && vp->v_type != VDIR) 429 return EPERM; 430 return smbfs_readvnode(vp, uio, ap->a_cred); 431 } 432 433 static int 434 smbfs_write(ap) 435 struct vop_write_args /* { 436 struct vnode *a_vp; 437 struct uio *a_uio; 438 int a_ioflag; 439 struct ucred *a_cred; 440 } */ *ap; 441 { 442 struct vnode *vp = ap->a_vp; 443 struct uio *uio = ap->a_uio; 444 445 SMBVDEBUG("%d,ofs=%d,sz=%d\n",vp->v_type, (int)uio->uio_offset, uio->uio_resid); 446 if (vp->v_type != VREG) 447 return (EPERM); 448 return smbfs_writevnode(vp, uio, ap->a_cred,ap->a_ioflag); 449 } 450 /* 451 * smbfs_create call 452 * Create a regular file. On entry the directory to contain the file being 453 * created is locked. We must release before we return. We must also free 454 * the pathname buffer pointed at by cnp->cn_pnbuf, always on error, or 455 * only if the SAVESTART bit in cn_flags is clear on success. 456 */ 457 static int 458 smbfs_create(ap) 459 struct vop_create_args /* { 460 struct vnode *a_dvp; 461 struct vnode **a_vpp; 462 struct componentname *a_cnp; 463 struct vattr *a_vap; 464 } */ *ap; 465 { 466 struct vnode *dvp = ap->a_dvp; 467 struct vattr *vap = ap->a_vap; 468 struct vnode **vpp=ap->a_vpp; 469 struct componentname *cnp = ap->a_cnp; 470 struct smbnode *dnp = VTOSMB(dvp); 471 struct vnode *vp; 472 struct vattr vattr; 473 struct smbfattr fattr; 474 struct smb_cred scred; 475 char *name = cnp->cn_nameptr; 476 int nmlen = cnp->cn_namelen; 477 int error; 478 479 480 SMBVDEBUG("\n"); 481 *vpp = NULL; 482 if (vap->va_type != VREG) 483 return EOPNOTSUPP; 484 if ((error = VOP_GETATTR(dvp, &vattr, cnp->cn_cred, cnp->cn_thread))) 485 return error; 486 smb_makescred(&scred, cnp->cn_thread, cnp->cn_cred); 487 488 error = smbfs_smb_create(dnp, name, nmlen, &scred); 489 if (error) 490 return error; 491 error = smbfs_smb_lookup(dnp, name, nmlen, &fattr, &scred); 492 if (error) 493 return error; 494 error = smbfs_nget(VTOVFS(dvp), dvp, name, nmlen, &fattr, &vp); 495 if (error) 496 return error; 497 *vpp = vp; 498 if (cnp->cn_flags & MAKEENTRY) 499 cache_enter(dvp, vp, cnp); 500 return error; 501 } 502 503 static int 504 smbfs_remove(ap) 505 struct vop_remove_args /* { 506 struct vnodeop_desc *a_desc; 507 struct vnode * a_dvp; 508 struct vnode * a_vp; 509 struct componentname * a_cnp; 510 } */ *ap; 511 { 512 struct vnode *vp = ap->a_vp; 513 /* struct vnode *dvp = ap->a_dvp;*/ 514 struct componentname *cnp = ap->a_cnp; 515 struct smbnode *np = VTOSMB(vp); 516 struct smb_cred scred; 517 int error; 518 519 if (vp->v_type == VDIR || (np->n_flag & NOPEN) != 0 || vrefcnt(vp) != 1) 520 return EPERM; 521 smb_makescred(&scred, cnp->cn_thread, cnp->cn_cred); 522 error = smbfs_smb_delete(np, &scred); 523 if (error == 0) 524 np->n_flag |= NGONE; 525 cache_purge(vp); 526 return error; 527 } 528 529 /* 530 * smbfs_file rename call 531 */ 532 static int 533 smbfs_rename(ap) 534 struct vop_rename_args /* { 535 struct vnode *a_fdvp; 536 struct vnode *a_fvp; 537 struct componentname *a_fcnp; 538 struct vnode *a_tdvp; 539 struct vnode *a_tvp; 540 struct componentname *a_tcnp; 541 } */ *ap; 542 { 543 struct vnode *fvp = ap->a_fvp; 544 struct vnode *tvp = ap->a_tvp; 545 struct vnode *fdvp = ap->a_fdvp; 546 struct vnode *tdvp = ap->a_tdvp; 547 struct componentname *tcnp = ap->a_tcnp; 548 /* struct componentname *fcnp = ap->a_fcnp;*/ 549 struct smb_cred scred; 550 u_int16_t flags = 6; 551 int error=0; 552 553 /* Check for cross-device rename */ 554 if ((fvp->v_mount != tdvp->v_mount) || 555 (tvp && (fvp->v_mount != tvp->v_mount))) { 556 error = EXDEV; 557 goto out; 558 } 559 560 if (tvp && vrefcnt(tvp) > 1) { 561 error = EBUSY; 562 goto out; 563 } 564 flags = 0x10; /* verify all writes */ 565 if (fvp->v_type == VDIR) { 566 flags |= 2; 567 } else if (fvp->v_type == VREG) { 568 flags |= 1; 569 } else { 570 error = EINVAL; 571 goto out; 572 } 573 smb_makescred(&scred, tcnp->cn_thread, tcnp->cn_cred); 574 /* 575 * It seems that Samba doesn't implement SMB_COM_MOVE call... 576 */ 577 #ifdef notnow 578 if (SMB_DIALECT(SSTOCN(smp->sm_share)) >= SMB_DIALECT_LANMAN1_0) { 579 error = smbfs_smb_move(VTOSMB(fvp), VTOSMB(tdvp), 580 tcnp->cn_nameptr, tcnp->cn_namelen, flags, &scred); 581 } else 582 #endif 583 { 584 /* 585 * We have to do the work atomicaly 586 */ 587 if (tvp && tvp != fvp) { 588 error = smbfs_smb_delete(VTOSMB(tvp), &scred); 589 if (error) 590 goto out_cacherem; 591 VTOSMB(fvp)->n_flag |= NGONE; 592 } 593 error = smbfs_smb_rename(VTOSMB(fvp), VTOSMB(tdvp), 594 tcnp->cn_nameptr, tcnp->cn_namelen, &scred); 595 } 596 597 if (fvp->v_type == VDIR) { 598 if (tvp != NULL && tvp->v_type == VDIR) 599 cache_purge(tdvp); 600 cache_purge(fdvp); 601 } 602 603 out_cacherem: 604 smbfs_attr_cacheremove(fdvp); 605 smbfs_attr_cacheremove(tdvp); 606 out: 607 if (tdvp == tvp) 608 vrele(tdvp); 609 else 610 vput(tdvp); 611 if (tvp) 612 vput(tvp); 613 vrele(fdvp); 614 vrele(fvp); 615 #ifdef possible_mistake 616 vgone(fvp); 617 if (tvp) 618 vgone(tvp); 619 #endif 620 return error; 621 } 622 623 /* 624 * somtime it will come true... 625 */ 626 static int 627 smbfs_link(ap) 628 struct vop_link_args /* { 629 struct vnode *a_tdvp; 630 struct vnode *a_vp; 631 struct componentname *a_cnp; 632 } */ *ap; 633 { 634 return EOPNOTSUPP; 635 } 636 637 /* 638 * smbfs_symlink link create call. 639 * Sometime it will be functional... 640 */ 641 static int 642 smbfs_symlink(ap) 643 struct vop_symlink_args /* { 644 struct vnode *a_dvp; 645 struct vnode **a_vpp; 646 struct componentname *a_cnp; 647 struct vattr *a_vap; 648 char *a_target; 649 } */ *ap; 650 { 651 return EOPNOTSUPP; 652 } 653 654 static int 655 smbfs_mknod(ap) 656 struct vop_mknod_args /* { 657 } */ *ap; 658 { 659 return EOPNOTSUPP; 660 } 661 662 static int 663 smbfs_mkdir(ap) 664 struct vop_mkdir_args /* { 665 struct vnode *a_dvp; 666 struct vnode **a_vpp; 667 struct componentname *a_cnp; 668 struct vattr *a_vap; 669 } */ *ap; 670 { 671 struct vnode *dvp = ap->a_dvp; 672 /* struct vattr *vap = ap->a_vap;*/ 673 struct vnode *vp; 674 struct componentname *cnp = ap->a_cnp; 675 struct smbnode *dnp = VTOSMB(dvp); 676 struct vattr vattr; 677 struct smb_cred scred; 678 struct smbfattr fattr; 679 char *name = cnp->cn_nameptr; 680 int len = cnp->cn_namelen; 681 int error; 682 683 if ((error = VOP_GETATTR(dvp, &vattr, cnp->cn_cred, cnp->cn_thread))) { 684 return error; 685 } 686 if ((name[0] == '.') && ((len == 1) || ((len == 2) && (name[1] == '.')))) 687 return EEXIST; 688 smb_makescred(&scred, cnp->cn_thread, cnp->cn_cred); 689 error = smbfs_smb_mkdir(dnp, name, len, &scred); 690 if (error) 691 return error; 692 error = smbfs_smb_lookup(dnp, name, len, &fattr, &scred); 693 if (error) 694 return error; 695 error = smbfs_nget(VTOVFS(dvp), dvp, name, len, &fattr, &vp); 696 if (error) 697 return error; 698 *ap->a_vpp = vp; 699 return 0; 700 } 701 702 /* 703 * smbfs_remove directory call 704 */ 705 static int 706 smbfs_rmdir(ap) 707 struct vop_rmdir_args /* { 708 struct vnode *a_dvp; 709 struct vnode *a_vp; 710 struct componentname *a_cnp; 711 } */ *ap; 712 { 713 struct vnode *vp = ap->a_vp; 714 struct vnode *dvp = ap->a_dvp; 715 struct componentname *cnp = ap->a_cnp; 716 /* struct smbmount *smp = VTOSMBFS(vp);*/ 717 struct smbnode *dnp = VTOSMB(dvp); 718 struct smbnode *np = VTOSMB(vp); 719 struct smb_cred scred; 720 int error; 721 722 if (dvp == vp) 723 return EINVAL; 724 725 smb_makescred(&scred, cnp->cn_thread, cnp->cn_cred); 726 error = smbfs_smb_rmdir(np, &scred); 727 if (error == 0) 728 np->n_flag |= NGONE; 729 dnp->n_flag |= NMODIFIED; 730 smbfs_attr_cacheremove(dvp); 731 /* cache_purge(dvp);*/ 732 cache_purge(vp); 733 return error; 734 } 735 736 /* 737 * smbfs_readdir call 738 */ 739 static int 740 smbfs_readdir(ap) 741 struct vop_readdir_args /* { 742 struct vnode *a_vp; 743 struct uio *a_uio; 744 struct ucred *a_cred; 745 int *a_eofflag; 746 u_long *a_cookies; 747 int a_ncookies; 748 } */ *ap; 749 { 750 struct vnode *vp = ap->a_vp; 751 struct uio *uio = ap->a_uio; 752 int error; 753 754 if (vp->v_type != VDIR) 755 return (EPERM); 756 #ifdef notnow 757 if (ap->a_ncookies) { 758 printf("smbfs_readdir: no support for cookies now..."); 759 return (EOPNOTSUPP); 760 } 761 #endif 762 error = smbfs_readvnode(vp, uio, ap->a_cred); 763 return error; 764 } 765 766 /* ARGSUSED */ 767 static int 768 smbfs_fsync(ap) 769 struct vop_fsync_args /* { 770 struct vnodeop_desc *a_desc; 771 struct vnode * a_vp; 772 struct ucred * a_cred; 773 int a_waitfor; 774 struct thread * a_td; 775 } */ *ap; 776 { 777 /* return (smb_flush(ap->a_vp, ap->a_cred, ap->a_waitfor, ap->a_td, 1));*/ 778 return (0); 779 } 780 781 static 782 int smbfs_print (ap) 783 struct vop_print_args /* { 784 struct vnode *a_vp; 785 } */ *ap; 786 { 787 struct vnode *vp = ap->a_vp; 788 struct smbnode *np = VTOSMB(vp); 789 790 if (np == NULL) { 791 printf("no smbnode data\n"); 792 return (0); 793 } 794 printf("\tname = %s, parent = %p, open = %d\n", np->n_name, 795 np->n_parent ? np->n_parent : NULL, (np->n_flag & NOPEN) != 0); 796 return (0); 797 } 798 799 static int 800 smbfs_pathconf (ap) 801 struct vop_pathconf_args /* { 802 struct vnode *vp; 803 int name; 804 register_t *retval; 805 } */ *ap; 806 { 807 struct smbmount *smp = VFSTOSMBFS(VTOVFS(ap->a_vp)); 808 struct smb_vc *vcp = SSTOVC(smp->sm_share); 809 register_t *retval = ap->a_retval; 810 int error = 0; 811 812 switch (ap->a_name) { 813 case _PC_LINK_MAX: 814 *retval = 0; 815 break; 816 case _PC_NAME_MAX: 817 *retval = (vcp->vc_hflags2 & SMB_FLAGS2_KNOWS_LONG_NAMES) ? 255 : 12; 818 break; 819 case _PC_PATH_MAX: 820 *retval = 800; /* XXX: a correct one ? */ 821 break; 822 default: 823 error = EINVAL; 824 } 825 return error; 826 } 827 828 static int 829 smbfs_strategy (ap) 830 struct vop_strategy_args /* { 831 struct buf *a_bp 832 } */ *ap; 833 { 834 struct buf *bp=ap->a_bp; 835 struct ucred *cr; 836 struct thread *td; 837 int error = 0; 838 839 SMBVDEBUG("\n"); 840 if (bp->b_flags & B_ASYNC) 841 td = (struct thread *)0; 842 else 843 td = curthread; /* XXX */ 844 if (bp->b_iocmd == BIO_READ) 845 cr = bp->b_rcred; 846 else 847 cr = bp->b_wcred; 848 849 if ((bp->b_flags & B_ASYNC) == 0 ) 850 error = smbfs_doio(ap->a_vp, bp, cr, td); 851 return error; 852 } 853 854 int 855 smbfs_ioctl(ap) 856 struct vop_ioctl_args /* { 857 struct vnode *a_vp; 858 u_long a_command; 859 caddr_t a_data; 860 int fflag; 861 struct ucred *cred; 862 struct thread *td; 863 } */ *ap; 864 { 865 return ENOTTY; 866 } 867 868 static char smbfs_atl[] = "rhsvda"; 869 static int 870 smbfs_getextattr(struct vop_getextattr_args *ap) 871 /* { 872 IN struct vnode *a_vp; 873 IN char *a_name; 874 INOUT struct uio *a_uio; 875 IN struct ucred *a_cred; 876 IN struct thread *a_td; 877 }; 878 */ 879 { 880 struct vnode *vp = ap->a_vp; 881 struct thread *td = ap->a_td; 882 struct ucred *cred = ap->a_cred; 883 struct uio *uio = ap->a_uio; 884 const char *name = ap->a_name; 885 struct smbnode *np = VTOSMB(vp); 886 struct vattr vattr; 887 char buf[10]; 888 int i, attr, error; 889 890 error = VOP_ACCESS(vp, VREAD, cred, td); 891 if (error) 892 return error; 893 error = VOP_GETATTR(vp, &vattr, cred, td); 894 if (error) 895 return error; 896 if (strcmp(name, "dosattr") == 0) { 897 attr = np->n_dosattr; 898 for (i = 0; i < 6; i++, attr >>= 1) 899 buf[i] = (attr & 1) ? smbfs_atl[i] : '-'; 900 buf[i] = 0; 901 error = uiomove(buf, i, uio); 902 903 } else 904 error = EINVAL; 905 return error; 906 } 907 908 /* 909 * Since we expected to support F_GETLK (and SMB protocol has no such function), 910 * it is necessary to use lf_advlock(). It would be nice if this function had 911 * a callback mechanism because it will help to improve a level of consistency. 912 */ 913 int 914 smbfs_advlock(ap) 915 struct vop_advlock_args /* { 916 struct vnode *a_vp; 917 caddr_t a_id; 918 int a_op; 919 struct flock *a_fl; 920 int a_flags; 921 } */ *ap; 922 { 923 struct vnode *vp = ap->a_vp; 924 struct smbnode *np = VTOSMB(vp); 925 struct flock *fl = ap->a_fl; 926 caddr_t id = (caddr_t)1 /* ap->a_id */; 927 /* int flags = ap->a_flags;*/ 928 struct thread *td = curthread; 929 struct smb_cred scred; 930 u_quad_t size; 931 off_t start, end, oadd; 932 int error, lkop; 933 934 if (vp->v_type == VDIR) { 935 /* 936 * SMB protocol have no support for directory locking. 937 * Although locks can be processed on local machine, I don't 938 * think that this is a good idea, because some programs 939 * can work wrong assuming directory is locked. So, we just 940 * return 'operation not supported 941 */ 942 return EOPNOTSUPP; 943 } 944 size = np->n_size; 945 switch (fl->l_whence) { 946 947 case SEEK_SET: 948 case SEEK_CUR: 949 start = fl->l_start; 950 break; 951 952 case SEEK_END: 953 if (size > OFF_MAX || 954 (fl->l_start > 0 && size > OFF_MAX - fl->l_start)) 955 return EOVERFLOW; 956 start = size + fl->l_start; 957 break; 958 959 default: 960 return EINVAL; 961 } 962 if (start < 0) 963 return EINVAL; 964 if (fl->l_len < 0) { 965 if (start == 0) 966 return EINVAL; 967 end = start - 1; 968 start += fl->l_len; 969 if (start < 0) 970 return EINVAL; 971 } else if (fl->l_len == 0) 972 end = -1; 973 else { 974 oadd = fl->l_len - 1; 975 if (oadd > OFF_MAX - start) 976 return EOVERFLOW; 977 end = start + oadd; 978 } 979 smb_makescred(&scred, td, td->td_ucred); 980 switch (ap->a_op) { 981 case F_SETLK: 982 switch (fl->l_type) { 983 case F_WRLCK: 984 lkop = SMB_LOCK_EXCL; 985 break; 986 case F_RDLCK: 987 lkop = SMB_LOCK_SHARED; 988 break; 989 case F_UNLCK: 990 lkop = SMB_LOCK_RELEASE; 991 break; 992 default: 993 return EINVAL; 994 } 995 error = lf_advlock(ap, &np->n_lockf, size); 996 if (error) 997 break; 998 lkop = SMB_LOCK_EXCL; 999 error = smbfs_smb_lock(np, lkop, id, start, end, &scred); 1000 if (error) { 1001 ap->a_op = F_UNLCK; 1002 lf_advlock(ap, &np->n_lockf, size); 1003 } 1004 break; 1005 case F_UNLCK: 1006 lf_advlock(ap, &np->n_lockf, size); 1007 error = smbfs_smb_lock(np, SMB_LOCK_RELEASE, id, start, end, &scred); 1008 break; 1009 case F_GETLK: 1010 error = lf_advlock(ap, &np->n_lockf, size); 1011 break; 1012 default: 1013 return EINVAL; 1014 } 1015 return error; 1016 } 1017 1018 static int 1019 smbfs_pathcheck(struct smbmount *smp, const char *name, int nmlen, int nameiop) 1020 { 1021 static const char *badchars = "*/\\:<>;?"; 1022 static const char *badchars83 = " +|,[]="; 1023 const char *cp; 1024 int i, error; 1025 1026 if (nameiop == LOOKUP) 1027 return 0; 1028 error = ENOENT; 1029 if (SMB_DIALECT(SSTOVC(smp->sm_share)) < SMB_DIALECT_LANMAN2_0) { 1030 /* 1031 * Name should conform 8.3 format 1032 */ 1033 if (nmlen > 12) 1034 return ENAMETOOLONG; 1035 cp = index(name, '.'); 1036 if (cp == NULL) 1037 return error; 1038 if (cp == name || (cp - name) > 8) 1039 return error; 1040 cp = index(cp + 1, '.'); 1041 if (cp != NULL) 1042 return error; 1043 for (cp = name, i = 0; i < nmlen; i++, cp++) 1044 if (index(badchars83, *cp) != NULL) 1045 return error; 1046 } 1047 for (cp = name, i = 0; i < nmlen; i++, cp++) 1048 if (index(badchars, *cp) != NULL) 1049 return error; 1050 return 0; 1051 } 1052 1053 /* 1054 * Things go even weird without fixed inode numbers... 1055 */ 1056 int 1057 smbfs_lookup(ap) 1058 struct vop_lookup_args /* { 1059 struct vnodeop_desc *a_desc; 1060 struct vnode *a_dvp; 1061 struct vnode **a_vpp; 1062 struct componentname *a_cnp; 1063 } */ *ap; 1064 { 1065 struct componentname *cnp = ap->a_cnp; 1066 struct thread *td = cnp->cn_thread; 1067 struct vnode *dvp = ap->a_dvp; 1068 struct vnode **vpp = ap->a_vpp; 1069 struct vnode *vp; 1070 struct smbmount *smp; 1071 struct mount *mp = dvp->v_mount; 1072 struct smbnode *dnp; 1073 struct smbfattr fattr, *fap; 1074 struct smb_cred scred; 1075 char *name = cnp->cn_nameptr; 1076 int flags = cnp->cn_flags; 1077 int nameiop = cnp->cn_nameiop; 1078 int nmlen = cnp->cn_namelen; 1079 int error, islastcn, isdot; 1080 int killit; 1081 1082 SMBVDEBUG("\n"); 1083 if (dvp->v_type != VDIR) 1084 return ENOTDIR; 1085 if ((flags & ISDOTDOT) && (dvp->v_vflag & VV_ROOT)) { 1086 SMBFSERR("invalid '..'\n"); 1087 return EIO; 1088 } 1089 #ifdef SMB_VNODE_DEBUG 1090 { 1091 char *cp, c; 1092 1093 cp = name + nmlen; 1094 c = *cp; 1095 *cp = 0; 1096 SMBVDEBUG("%d '%s' in '%s' id=d\n", nameiop, name, 1097 VTOSMB(dvp)->n_name); 1098 *cp = c; 1099 } 1100 #endif 1101 islastcn = flags & ISLASTCN; 1102 if (islastcn && (mp->mnt_flag & MNT_RDONLY) && (nameiop != LOOKUP)) 1103 return EROFS; 1104 if ((error = VOP_ACCESS(dvp, VEXEC, cnp->cn_cred, td)) != 0) 1105 return error; 1106 smp = VFSTOSMBFS(mp); 1107 dnp = VTOSMB(dvp); 1108 isdot = (nmlen == 1 && name[0] == '.'); 1109 1110 error = smbfs_pathcheck(smp, cnp->cn_nameptr, cnp->cn_namelen, nameiop); 1111 1112 if (error) 1113 return ENOENT; 1114 1115 error = cache_lookup(dvp, vpp, cnp); 1116 SMBVDEBUG("cache_lookup returned %d\n", error); 1117 if (error > 0) 1118 return error; 1119 if (error) { /* name was found */ 1120 struct vattr vattr; 1121 1122 killit = 0; 1123 vp = *vpp; 1124 error = VOP_GETATTR(vp, &vattr, cnp->cn_cred, td); 1125 /* 1126 * If the file type on the server is inconsistent 1127 * with what it was when we created the vnode, 1128 * kill the bogus vnode now and fall through to 1129 * the code below to create a new one with the 1130 * right type. 1131 */ 1132 if (error == 0 && 1133 ((vp->v_type == VDIR && 1134 (VTOSMB(vp)->n_dosattr & SMB_FA_DIR) == 0) || 1135 (vp->v_type == VREG && 1136 (VTOSMB(vp)->n_dosattr & SMB_FA_DIR) != 0))) 1137 killit = 1; 1138 else if (error == 0 1139 /* && vattr.va_ctime.tv_sec == VTOSMB(vp)->n_ctime*/) { 1140 if (nameiop != LOOKUP && islastcn) 1141 cnp->cn_flags |= SAVENAME; 1142 SMBVDEBUG("use cached vnode\n"); 1143 return (0); 1144 } 1145 cache_purge(vp); 1146 /* 1147 * XXX This is not quite right, if '.' is 1148 * inconsistent, we really need to start the lookup 1149 * all over again. Hopefully there is some other 1150 * guarantee that prevents this case from happening. 1151 */ 1152 if (killit && vp != dvp) 1153 vgone(vp); 1154 if (vp != dvp) 1155 vput(vp); 1156 else 1157 vrele(vp); 1158 *vpp = NULLVP; 1159 } 1160 /* 1161 * entry is not in the cache or has been expired 1162 */ 1163 error = 0; 1164 *vpp = NULLVP; 1165 smb_makescred(&scred, td, cnp->cn_cred); 1166 fap = &fattr; 1167 if (flags & ISDOTDOT) { 1168 error = smbfs_smb_lookup(VTOSMB(dnp->n_parent), NULL, 0, fap, 1169 &scred); 1170 SMBVDEBUG("result of dotdot lookup: %d\n", error); 1171 } else { 1172 fap = &fattr; 1173 error = smbfs_smb_lookup(dnp, name, nmlen, fap, &scred); 1174 /* if (cnp->cn_namelen == 1 && cnp->cn_nameptr[0] == '.')*/ 1175 SMBVDEBUG("result of smbfs_smb_lookup: %d\n", error); 1176 } 1177 if (error && error != ENOENT) 1178 return error; 1179 if (error) { /* entry not found */ 1180 /* 1181 * Handle RENAME or CREATE case... 1182 */ 1183 if ((nameiop == CREATE || nameiop == RENAME) && islastcn) { 1184 error = VOP_ACCESS(dvp, VWRITE, cnp->cn_cred, td); 1185 if (error) 1186 return error; 1187 cnp->cn_flags |= SAVENAME; 1188 return (EJUSTRETURN); 1189 } 1190 return ENOENT; 1191 }/* else { 1192 SMBVDEBUG("Found entry %s with id=%d\n", fap->entryName, fap->dirEntNum); 1193 }*/ 1194 /* 1195 * handle DELETE case ... 1196 */ 1197 if (nameiop == DELETE && islastcn) { /* delete last component */ 1198 error = VOP_ACCESS(dvp, VWRITE, cnp->cn_cred, td); 1199 if (error) 1200 return error; 1201 if (isdot) { 1202 VREF(dvp); 1203 *vpp = dvp; 1204 return 0; 1205 } 1206 error = smbfs_nget(mp, dvp, name, nmlen, fap, &vp); 1207 if (error) 1208 return error; 1209 *vpp = vp; 1210 cnp->cn_flags |= SAVENAME; 1211 return 0; 1212 } 1213 if (nameiop == RENAME && islastcn) { 1214 error = VOP_ACCESS(dvp, VWRITE, cnp->cn_cred, td); 1215 if (error) 1216 return error; 1217 if (isdot) 1218 return EISDIR; 1219 error = smbfs_nget(mp, dvp, name, nmlen, fap, &vp); 1220 if (error) 1221 return error; 1222 *vpp = vp; 1223 cnp->cn_flags |= SAVENAME; 1224 return 0; 1225 } 1226 if (flags & ISDOTDOT) { 1227 VOP_UNLOCK(dvp, 0, td); 1228 error = smbfs_nget(mp, dvp, name, nmlen, NULL, &vp); 1229 vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY, td); 1230 if (error) 1231 return error; 1232 *vpp = vp; 1233 } else if (isdot) { 1234 vref(dvp); 1235 *vpp = dvp; 1236 } else { 1237 error = smbfs_nget(mp, dvp, name, nmlen, fap, &vp); 1238 if (error) 1239 return error; 1240 *vpp = vp; 1241 SMBVDEBUG("lookup: getnewvp!\n"); 1242 } 1243 if ((cnp->cn_flags & MAKEENTRY)/* && !islastcn*/) { 1244 /* VTOSMB(*vpp)->n_ctime = VTOSMB(*vpp)->n_vattr.va_ctime.tv_sec;*/ 1245 cache_enter(dvp, *vpp, cnp); 1246 } 1247 return 0; 1248 } 1249