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 /* 224 * XXX: VOP_CLOSE() usually called without lock held which is suck. Here we 225 * do some heruistic to determine if vnode should be locked. 226 */ 227 static int 228 smbfs_close(ap) 229 struct vop_close_args /* { 230 struct vnodeop_desc *a_desc; 231 struct vnode *a_vp; 232 int a_fflag; 233 struct ucred *a_cred; 234 struct thread *a_td; 235 } */ *ap; 236 { 237 struct vnode *vp = ap->a_vp; 238 struct thread *td = ap->a_td; 239 struct smbnode *np = VTOSMB(vp); 240 struct smb_cred scred; 241 int dolock; 242 243 VI_LOCK(vp); 244 dolock = (vp->v_iflag & VI_XLOCK) == 0; 245 if (dolock) 246 vn_lock(vp, LK_EXCLUSIVE | LK_RETRY | LK_INTERLOCK, td); 247 else 248 VI_UNLOCK(vp); 249 if (vp->v_type == VDIR && (np->n_flag & NOPEN) != 0 && 250 np->n_dirseq != NULL) { 251 smb_makescred(&scred, td, ap->a_cred); 252 smbfs_findclose(np->n_dirseq, &scred); 253 np->n_dirseq = NULL; 254 } 255 if (dolock) 256 VOP_UNLOCK(vp, 0, td); 257 return 0; 258 } 259 260 /* 261 * smbfs_getattr call from vfs. 262 */ 263 static int 264 smbfs_getattr(ap) 265 struct vop_getattr_args /* { 266 struct vnode *a_vp; 267 struct vattr *a_vap; 268 struct ucred *a_cred; 269 struct thread *a_td; 270 } */ *ap; 271 { 272 struct vnode *vp = ap->a_vp; 273 struct smbnode *np = VTOSMB(vp); 274 struct vattr *va=ap->a_vap; 275 struct smbfattr fattr; 276 struct smb_cred scred; 277 u_quad_t oldsize; 278 int error; 279 280 SMBVDEBUG("%lx: '%s' %d\n", (long)vp, np->n_name, (vp->v_vflag & VV_ROOT) != 0); 281 error = smbfs_attr_cachelookup(vp, va); 282 if (!error) 283 return 0; 284 SMBVDEBUG("not in the cache\n"); 285 smb_makescred(&scred, ap->a_td, ap->a_cred); 286 oldsize = np->n_size; 287 error = smbfs_smb_lookup(np, NULL, 0, &fattr, &scred); 288 if (error) { 289 SMBVDEBUG("error %d\n", error); 290 return error; 291 } 292 smbfs_attr_cacheenter(vp, &fattr); 293 smbfs_attr_cachelookup(vp, va); 294 if (np->n_flag & NOPEN) 295 np->n_size = oldsize; 296 return 0; 297 } 298 299 static int 300 smbfs_setattr(ap) 301 struct vop_setattr_args /* { 302 struct vnode *a_vp; 303 struct vattr *a_vap; 304 struct ucred *a_cred; 305 struct thread *a_td; 306 } */ *ap; 307 { 308 struct vnode *vp = ap->a_vp; 309 struct smbnode *np = VTOSMB(vp); 310 struct vattr *vap = ap->a_vap; 311 struct timespec *mtime, *atime; 312 struct smb_cred scred; 313 struct smb_share *ssp = np->n_mount->sm_share; 314 struct smb_vc *vcp = SSTOVC(ssp); 315 u_quad_t tsize = 0; 316 int isreadonly, doclose, error = 0; 317 318 SMBVDEBUG("\n"); 319 if (vap->va_flags != VNOVAL) 320 return EOPNOTSUPP; 321 isreadonly = (vp->v_mount->mnt_flag & MNT_RDONLY); 322 /* 323 * Disallow write attempts if the filesystem is mounted read-only. 324 */ 325 if ((vap->va_uid != (uid_t)VNOVAL || vap->va_gid != (gid_t)VNOVAL || 326 vap->va_atime.tv_sec != VNOVAL || vap->va_mtime.tv_sec != VNOVAL || 327 vap->va_mode != (mode_t)VNOVAL) && isreadonly) 328 return EROFS; 329 smb_makescred(&scred, ap->a_td, ap->a_cred); 330 if (vap->va_size != VNOVAL) { 331 switch (vp->v_type) { 332 case VDIR: 333 return EISDIR; 334 case VREG: 335 break; 336 default: 337 return EINVAL; 338 }; 339 if (isreadonly) 340 return EROFS; 341 doclose = 0; 342 vnode_pager_setsize(vp, (u_long)vap->va_size); 343 tsize = np->n_size; 344 np->n_size = vap->va_size; 345 if ((np->n_flag & NOPEN) == 0) { 346 error = smbfs_smb_open(np, 347 SMB_SM_DENYNONE|SMB_AM_OPENRW, 348 &scred); 349 if (error == 0) 350 doclose = 1; 351 } 352 if (error == 0) 353 error = smbfs_smb_setfsize(np, vap->va_size, &scred); 354 if (doclose) 355 smbfs_smb_close(ssp, np->n_fid, NULL, &scred); 356 if (error) { 357 np->n_size = tsize; 358 vnode_pager_setsize(vp, (u_long)tsize); 359 return error; 360 } 361 } 362 mtime = atime = NULL; 363 if (vap->va_mtime.tv_sec != VNOVAL) 364 mtime = &vap->va_mtime; 365 if (vap->va_atime.tv_sec != VNOVAL) 366 atime = &vap->va_atime; 367 if (mtime != atime) { 368 if (ap->a_cred->cr_uid != VTOSMBFS(vp)->sm_uid && 369 (error = suser_cred(ap->a_cred, SUSER_ALLOWJAIL)) && 370 ((vap->va_vaflags & VA_UTIMES_NULL) == 0 || 371 (error = VOP_ACCESS(vp, VWRITE, ap->a_cred, ap->a_td)))) 372 return (error); 373 #if 0 374 if (mtime == NULL) 375 mtime = &np->n_mtime; 376 if (atime == NULL) 377 atime = &np->n_atime; 378 #endif 379 /* 380 * If file is opened, then we can use handle based calls. 381 * If not, use path based ones. 382 */ 383 if ((np->n_flag & NOPEN) == 0) { 384 if (vcp->vc_flags & SMBV_WIN95) { 385 error = VOP_OPEN(vp, FWRITE, ap->a_cred, ap->a_td, -1); 386 if (!error) { 387 /* error = smbfs_smb_setfattrNT(np, 0, mtime, atime, &scred); 388 VOP_GETATTR(vp, &vattr, ap->a_cred, ap->a_td);*/ 389 if (mtime) 390 np->n_mtime = *mtime; 391 VOP_CLOSE(vp, FWRITE, ap->a_cred, ap->a_td); 392 } 393 } else if ((vcp->vc_sopt.sv_caps & SMB_CAP_NT_SMBS)) { 394 error = smbfs_smb_setptime2(np, mtime, atime, 0, &scred); 395 /* error = smbfs_smb_setpattrNT(np, 0, mtime, atime, &scred);*/ 396 } else if (SMB_DIALECT(vcp) >= SMB_DIALECT_LANMAN2_0) { 397 error = smbfs_smb_setptime2(np, mtime, atime, 0, &scred); 398 } else { 399 error = smbfs_smb_setpattr(np, 0, mtime, &scred); 400 } 401 } else { 402 if (vcp->vc_sopt.sv_caps & SMB_CAP_NT_SMBS) { 403 error = smbfs_smb_setfattrNT(np, 0, mtime, atime, &scred); 404 } else if (SMB_DIALECT(vcp) >= SMB_DIALECT_LANMAN1_0) { 405 error = smbfs_smb_setftime(np, mtime, atime, &scred); 406 } else { 407 /* 408 * I have no idea how to handle this for core 409 * level servers. The possible solution is to 410 * update mtime after file is closed. 411 */ 412 SMBERROR("can't update times on an opened file\n"); 413 } 414 } 415 } 416 /* 417 * Invalidate attribute cache in case if server doesn't set 418 * required attributes. 419 */ 420 smbfs_attr_cacheremove(vp); /* invalidate cache */ 421 VOP_GETATTR(vp, vap, ap->a_cred, ap->a_td); 422 np->n_mtime.tv_sec = vap->va_mtime.tv_sec; 423 return error; 424 } 425 /* 426 * smbfs_read call. 427 */ 428 static int 429 smbfs_read(ap) 430 struct vop_read_args /* { 431 struct vnode *a_vp; 432 struct uio *a_uio; 433 int a_ioflag; 434 struct ucred *a_cred; 435 } */ *ap; 436 { 437 struct vnode *vp = ap->a_vp; 438 struct uio *uio = ap->a_uio; 439 440 SMBVDEBUG("\n"); 441 if (vp->v_type != VREG && vp->v_type != VDIR) 442 return EPERM; 443 return smbfs_readvnode(vp, uio, ap->a_cred); 444 } 445 446 static int 447 smbfs_write(ap) 448 struct vop_write_args /* { 449 struct vnode *a_vp; 450 struct uio *a_uio; 451 int a_ioflag; 452 struct ucred *a_cred; 453 } */ *ap; 454 { 455 struct vnode *vp = ap->a_vp; 456 struct uio *uio = ap->a_uio; 457 458 SMBVDEBUG("%d,ofs=%d,sz=%d\n",vp->v_type, (int)uio->uio_offset, uio->uio_resid); 459 if (vp->v_type != VREG) 460 return (EPERM); 461 return smbfs_writevnode(vp, uio, ap->a_cred,ap->a_ioflag); 462 } 463 /* 464 * smbfs_create call 465 * Create a regular file. On entry the directory to contain the file being 466 * created is locked. We must release before we return. We must also free 467 * the pathname buffer pointed at by cnp->cn_pnbuf, always on error, or 468 * only if the SAVESTART bit in cn_flags is clear on success. 469 */ 470 static int 471 smbfs_create(ap) 472 struct vop_create_args /* { 473 struct vnode *a_dvp; 474 struct vnode **a_vpp; 475 struct componentname *a_cnp; 476 struct vattr *a_vap; 477 } */ *ap; 478 { 479 struct vnode *dvp = ap->a_dvp; 480 struct vattr *vap = ap->a_vap; 481 struct vnode **vpp=ap->a_vpp; 482 struct componentname *cnp = ap->a_cnp; 483 struct smbnode *dnp = VTOSMB(dvp); 484 struct vnode *vp; 485 struct vattr vattr; 486 struct smbfattr fattr; 487 struct smb_cred scred; 488 char *name = cnp->cn_nameptr; 489 int nmlen = cnp->cn_namelen; 490 int error; 491 492 493 SMBVDEBUG("\n"); 494 *vpp = NULL; 495 if (vap->va_type != VREG) 496 return EOPNOTSUPP; 497 if ((error = VOP_GETATTR(dvp, &vattr, cnp->cn_cred, cnp->cn_thread))) 498 return error; 499 smb_makescred(&scred, cnp->cn_thread, cnp->cn_cred); 500 501 error = smbfs_smb_create(dnp, name, nmlen, &scred); 502 if (error) 503 return error; 504 error = smbfs_smb_lookup(dnp, name, nmlen, &fattr, &scred); 505 if (error) 506 return error; 507 error = smbfs_nget(VTOVFS(dvp), dvp, name, nmlen, &fattr, &vp); 508 if (error) 509 return error; 510 *vpp = vp; 511 if (cnp->cn_flags & MAKEENTRY) 512 cache_enter(dvp, vp, cnp); 513 return error; 514 } 515 516 static int 517 smbfs_remove(ap) 518 struct vop_remove_args /* { 519 struct vnodeop_desc *a_desc; 520 struct vnode * a_dvp; 521 struct vnode * a_vp; 522 struct componentname * a_cnp; 523 } */ *ap; 524 { 525 struct vnode *vp = ap->a_vp; 526 /* struct vnode *dvp = ap->a_dvp;*/ 527 struct componentname *cnp = ap->a_cnp; 528 struct smbnode *np = VTOSMB(vp); 529 struct smb_cred scred; 530 int error; 531 532 if (vp->v_type == VDIR || (np->n_flag & NOPEN) != 0 || vrefcnt(vp) != 1) 533 return EPERM; 534 smb_makescred(&scred, cnp->cn_thread, cnp->cn_cred); 535 error = smbfs_smb_delete(np, &scred); 536 if (error == 0) 537 np->n_flag |= NGONE; 538 cache_purge(vp); 539 return error; 540 } 541 542 /* 543 * smbfs_file rename call 544 */ 545 static int 546 smbfs_rename(ap) 547 struct vop_rename_args /* { 548 struct vnode *a_fdvp; 549 struct vnode *a_fvp; 550 struct componentname *a_fcnp; 551 struct vnode *a_tdvp; 552 struct vnode *a_tvp; 553 struct componentname *a_tcnp; 554 } */ *ap; 555 { 556 struct vnode *fvp = ap->a_fvp; 557 struct vnode *tvp = ap->a_tvp; 558 struct vnode *fdvp = ap->a_fdvp; 559 struct vnode *tdvp = ap->a_tdvp; 560 struct componentname *tcnp = ap->a_tcnp; 561 /* struct componentname *fcnp = ap->a_fcnp;*/ 562 struct smb_cred scred; 563 u_int16_t flags = 6; 564 int error=0; 565 566 /* Check for cross-device rename */ 567 if ((fvp->v_mount != tdvp->v_mount) || 568 (tvp && (fvp->v_mount != tvp->v_mount))) { 569 error = EXDEV; 570 goto out; 571 } 572 573 if (tvp && vrefcnt(tvp) > 1) { 574 error = EBUSY; 575 goto out; 576 } 577 flags = 0x10; /* verify all writes */ 578 if (fvp->v_type == VDIR) { 579 flags |= 2; 580 } else if (fvp->v_type == VREG) { 581 flags |= 1; 582 } else { 583 error = EINVAL; 584 goto out; 585 } 586 smb_makescred(&scred, tcnp->cn_thread, tcnp->cn_cred); 587 /* 588 * It seems that Samba doesn't implement SMB_COM_MOVE call... 589 */ 590 #ifdef notnow 591 if (SMB_DIALECT(SSTOCN(smp->sm_share)) >= SMB_DIALECT_LANMAN1_0) { 592 error = smbfs_smb_move(VTOSMB(fvp), VTOSMB(tdvp), 593 tcnp->cn_nameptr, tcnp->cn_namelen, flags, &scred); 594 } else 595 #endif 596 { 597 /* 598 * We have to do the work atomicaly 599 */ 600 if (tvp && tvp != fvp) { 601 error = smbfs_smb_delete(VTOSMB(tvp), &scred); 602 if (error) 603 goto out_cacherem; 604 VTOSMB(fvp)->n_flag |= NGONE; 605 } 606 error = smbfs_smb_rename(VTOSMB(fvp), VTOSMB(tdvp), 607 tcnp->cn_nameptr, tcnp->cn_namelen, &scred); 608 } 609 610 if (fvp->v_type == VDIR) { 611 if (tvp != NULL && tvp->v_type == VDIR) 612 cache_purge(tdvp); 613 cache_purge(fdvp); 614 } 615 616 out_cacherem: 617 smbfs_attr_cacheremove(fdvp); 618 smbfs_attr_cacheremove(tdvp); 619 out: 620 if (tdvp == tvp) 621 vrele(tdvp); 622 else 623 vput(tdvp); 624 if (tvp) 625 vput(tvp); 626 vrele(fdvp); 627 vrele(fvp); 628 #ifdef possible_mistake 629 vgone(fvp); 630 if (tvp) 631 vgone(tvp); 632 #endif 633 return error; 634 } 635 636 /* 637 * somtime it will come true... 638 */ 639 static int 640 smbfs_link(ap) 641 struct vop_link_args /* { 642 struct vnode *a_tdvp; 643 struct vnode *a_vp; 644 struct componentname *a_cnp; 645 } */ *ap; 646 { 647 return EOPNOTSUPP; 648 } 649 650 /* 651 * smbfs_symlink link create call. 652 * Sometime it will be functional... 653 */ 654 static int 655 smbfs_symlink(ap) 656 struct vop_symlink_args /* { 657 struct vnode *a_dvp; 658 struct vnode **a_vpp; 659 struct componentname *a_cnp; 660 struct vattr *a_vap; 661 char *a_target; 662 } */ *ap; 663 { 664 return EOPNOTSUPP; 665 } 666 667 static int 668 smbfs_mknod(ap) 669 struct vop_mknod_args /* { 670 } */ *ap; 671 { 672 return EOPNOTSUPP; 673 } 674 675 static int 676 smbfs_mkdir(ap) 677 struct vop_mkdir_args /* { 678 struct vnode *a_dvp; 679 struct vnode **a_vpp; 680 struct componentname *a_cnp; 681 struct vattr *a_vap; 682 } */ *ap; 683 { 684 struct vnode *dvp = ap->a_dvp; 685 /* struct vattr *vap = ap->a_vap;*/ 686 struct vnode *vp; 687 struct componentname *cnp = ap->a_cnp; 688 struct smbnode *dnp = VTOSMB(dvp); 689 struct vattr vattr; 690 struct smb_cred scred; 691 struct smbfattr fattr; 692 char *name = cnp->cn_nameptr; 693 int len = cnp->cn_namelen; 694 int error; 695 696 if ((error = VOP_GETATTR(dvp, &vattr, cnp->cn_cred, cnp->cn_thread))) { 697 return error; 698 } 699 if ((name[0] == '.') && ((len == 1) || ((len == 2) && (name[1] == '.')))) 700 return EEXIST; 701 smb_makescred(&scred, cnp->cn_thread, cnp->cn_cred); 702 error = smbfs_smb_mkdir(dnp, name, len, &scred); 703 if (error) 704 return error; 705 error = smbfs_smb_lookup(dnp, name, len, &fattr, &scred); 706 if (error) 707 return error; 708 error = smbfs_nget(VTOVFS(dvp), dvp, name, len, &fattr, &vp); 709 if (error) 710 return error; 711 *ap->a_vpp = vp; 712 return 0; 713 } 714 715 /* 716 * smbfs_remove directory call 717 */ 718 static int 719 smbfs_rmdir(ap) 720 struct vop_rmdir_args /* { 721 struct vnode *a_dvp; 722 struct vnode *a_vp; 723 struct componentname *a_cnp; 724 } */ *ap; 725 { 726 struct vnode *vp = ap->a_vp; 727 struct vnode *dvp = ap->a_dvp; 728 struct componentname *cnp = ap->a_cnp; 729 /* struct smbmount *smp = VTOSMBFS(vp);*/ 730 struct smbnode *dnp = VTOSMB(dvp); 731 struct smbnode *np = VTOSMB(vp); 732 struct smb_cred scred; 733 int error; 734 735 if (dvp == vp) 736 return EINVAL; 737 738 smb_makescred(&scred, cnp->cn_thread, cnp->cn_cred); 739 error = smbfs_smb_rmdir(np, &scred); 740 if (error == 0) 741 np->n_flag |= NGONE; 742 dnp->n_flag |= NMODIFIED; 743 smbfs_attr_cacheremove(dvp); 744 /* cache_purge(dvp);*/ 745 cache_purge(vp); 746 return error; 747 } 748 749 /* 750 * smbfs_readdir call 751 */ 752 static int 753 smbfs_readdir(ap) 754 struct vop_readdir_args /* { 755 struct vnode *a_vp; 756 struct uio *a_uio; 757 struct ucred *a_cred; 758 int *a_eofflag; 759 u_long *a_cookies; 760 int a_ncookies; 761 } */ *ap; 762 { 763 struct vnode *vp = ap->a_vp; 764 struct uio *uio = ap->a_uio; 765 int error; 766 767 if (vp->v_type != VDIR) 768 return (EPERM); 769 #ifdef notnow 770 if (ap->a_ncookies) { 771 printf("smbfs_readdir: no support for cookies now..."); 772 return (EOPNOTSUPP); 773 } 774 #endif 775 error = smbfs_readvnode(vp, uio, ap->a_cred); 776 return error; 777 } 778 779 /* ARGSUSED */ 780 static int 781 smbfs_fsync(ap) 782 struct vop_fsync_args /* { 783 struct vnodeop_desc *a_desc; 784 struct vnode * a_vp; 785 struct ucred * a_cred; 786 int a_waitfor; 787 struct thread * a_td; 788 } */ *ap; 789 { 790 /* return (smb_flush(ap->a_vp, ap->a_cred, ap->a_waitfor, ap->a_td, 1));*/ 791 return (0); 792 } 793 794 static 795 int smbfs_print (ap) 796 struct vop_print_args /* { 797 struct vnode *a_vp; 798 } */ *ap; 799 { 800 struct vnode *vp = ap->a_vp; 801 struct smbnode *np = VTOSMB(vp); 802 803 if (np == NULL) { 804 printf("no smbnode data\n"); 805 return (0); 806 } 807 printf("\tname = %s, parent = %p, open = %d\n", np->n_name, 808 np->n_parent ? np->n_parent : NULL, (np->n_flag & NOPEN) != 0); 809 return (0); 810 } 811 812 static int 813 smbfs_pathconf (ap) 814 struct vop_pathconf_args /* { 815 struct vnode *vp; 816 int name; 817 register_t *retval; 818 } */ *ap; 819 { 820 struct smbmount *smp = VFSTOSMBFS(VTOVFS(ap->a_vp)); 821 struct smb_vc *vcp = SSTOVC(smp->sm_share); 822 register_t *retval = ap->a_retval; 823 int error = 0; 824 825 switch (ap->a_name) { 826 case _PC_LINK_MAX: 827 *retval = 0; 828 break; 829 case _PC_NAME_MAX: 830 *retval = (vcp->vc_hflags2 & SMB_FLAGS2_KNOWS_LONG_NAMES) ? 255 : 12; 831 break; 832 case _PC_PATH_MAX: 833 *retval = 800; /* XXX: a correct one ? */ 834 break; 835 default: 836 error = EINVAL; 837 } 838 return error; 839 } 840 841 static int 842 smbfs_strategy (ap) 843 struct vop_strategy_args /* { 844 struct buf *a_bp 845 } */ *ap; 846 { 847 struct buf *bp=ap->a_bp; 848 struct ucred *cr; 849 struct thread *td; 850 int error = 0; 851 852 SMBVDEBUG("\n"); 853 if (bp->b_flags & B_ASYNC) 854 td = (struct thread *)0; 855 else 856 td = curthread; /* XXX */ 857 if (bp->b_iocmd == BIO_READ) 858 cr = bp->b_rcred; 859 else 860 cr = bp->b_wcred; 861 862 if ((bp->b_flags & B_ASYNC) == 0 ) 863 error = smbfs_doio(ap->a_vp, bp, cr, td); 864 return error; 865 } 866 867 int 868 smbfs_ioctl(ap) 869 struct vop_ioctl_args /* { 870 struct vnode *a_vp; 871 u_long a_command; 872 caddr_t a_data; 873 int fflag; 874 struct ucred *cred; 875 struct thread *td; 876 } */ *ap; 877 { 878 return ENOTTY; 879 } 880 881 static char smbfs_atl[] = "rhsvda"; 882 static int 883 smbfs_getextattr(struct vop_getextattr_args *ap) 884 /* { 885 IN struct vnode *a_vp; 886 IN char *a_name; 887 INOUT struct uio *a_uio; 888 IN struct ucred *a_cred; 889 IN struct thread *a_td; 890 }; 891 */ 892 { 893 struct vnode *vp = ap->a_vp; 894 struct thread *td = ap->a_td; 895 struct ucred *cred = ap->a_cred; 896 struct uio *uio = ap->a_uio; 897 const char *name = ap->a_name; 898 struct smbnode *np = VTOSMB(vp); 899 struct vattr vattr; 900 char buf[10]; 901 int i, attr, error; 902 903 error = VOP_ACCESS(vp, VREAD, cred, td); 904 if (error) 905 return error; 906 error = VOP_GETATTR(vp, &vattr, cred, td); 907 if (error) 908 return error; 909 if (strcmp(name, "dosattr") == 0) { 910 attr = np->n_dosattr; 911 for (i = 0; i < 6; i++, attr >>= 1) 912 buf[i] = (attr & 1) ? smbfs_atl[i] : '-'; 913 buf[i] = 0; 914 error = uiomove(buf, i, uio); 915 916 } else 917 error = EINVAL; 918 return error; 919 } 920 921 /* 922 * Since we expected to support F_GETLK (and SMB protocol has no such function), 923 * it is necessary to use lf_advlock(). It would be nice if this function had 924 * a callback mechanism because it will help to improve a level of consistency. 925 */ 926 int 927 smbfs_advlock(ap) 928 struct vop_advlock_args /* { 929 struct vnode *a_vp; 930 caddr_t a_id; 931 int a_op; 932 struct flock *a_fl; 933 int a_flags; 934 } */ *ap; 935 { 936 struct vnode *vp = ap->a_vp; 937 struct smbnode *np = VTOSMB(vp); 938 struct flock *fl = ap->a_fl; 939 caddr_t id = (caddr_t)1 /* ap->a_id */; 940 /* int flags = ap->a_flags;*/ 941 struct thread *td = curthread; 942 struct smb_cred scred; 943 u_quad_t size; 944 off_t start, end, oadd; 945 int error, lkop; 946 947 if (vp->v_type == VDIR) { 948 /* 949 * SMB protocol have no support for directory locking. 950 * Although locks can be processed on local machine, I don't 951 * think that this is a good idea, because some programs 952 * can work wrong assuming directory is locked. So, we just 953 * return 'operation not supported 954 */ 955 return EOPNOTSUPP; 956 } 957 size = np->n_size; 958 switch (fl->l_whence) { 959 960 case SEEK_SET: 961 case SEEK_CUR: 962 start = fl->l_start; 963 break; 964 965 case SEEK_END: 966 if (size > OFF_MAX || 967 (fl->l_start > 0 && size > OFF_MAX - fl->l_start)) 968 return EOVERFLOW; 969 start = size + fl->l_start; 970 break; 971 972 default: 973 return EINVAL; 974 } 975 if (start < 0) 976 return EINVAL; 977 if (fl->l_len < 0) { 978 if (start == 0) 979 return EINVAL; 980 end = start - 1; 981 start += fl->l_len; 982 if (start < 0) 983 return EINVAL; 984 } else if (fl->l_len == 0) 985 end = -1; 986 else { 987 oadd = fl->l_len - 1; 988 if (oadd > OFF_MAX - start) 989 return EOVERFLOW; 990 end = start + oadd; 991 } 992 smb_makescred(&scred, td, td->td_ucred); 993 switch (ap->a_op) { 994 case F_SETLK: 995 switch (fl->l_type) { 996 case F_WRLCK: 997 lkop = SMB_LOCK_EXCL; 998 break; 999 case F_RDLCK: 1000 lkop = SMB_LOCK_SHARED; 1001 break; 1002 case F_UNLCK: 1003 lkop = SMB_LOCK_RELEASE; 1004 break; 1005 default: 1006 return EINVAL; 1007 } 1008 error = lf_advlock(ap, &np->n_lockf, size); 1009 if (error) 1010 break; 1011 lkop = SMB_LOCK_EXCL; 1012 error = smbfs_smb_lock(np, lkop, id, start, end, &scred); 1013 if (error) { 1014 ap->a_op = F_UNLCK; 1015 lf_advlock(ap, &np->n_lockf, size); 1016 } 1017 break; 1018 case F_UNLCK: 1019 lf_advlock(ap, &np->n_lockf, size); 1020 error = smbfs_smb_lock(np, SMB_LOCK_RELEASE, id, start, end, &scred); 1021 break; 1022 case F_GETLK: 1023 error = lf_advlock(ap, &np->n_lockf, size); 1024 break; 1025 default: 1026 return EINVAL; 1027 } 1028 return error; 1029 } 1030 1031 static int 1032 smbfs_pathcheck(struct smbmount *smp, const char *name, int nmlen, int nameiop) 1033 { 1034 static const char *badchars = "*/\\:<>;?"; 1035 static const char *badchars83 = " +|,[]="; 1036 const char *cp; 1037 int i, error; 1038 1039 if (nameiop == LOOKUP) 1040 return 0; 1041 error = ENOENT; 1042 if (SMB_DIALECT(SSTOVC(smp->sm_share)) < SMB_DIALECT_LANMAN2_0) { 1043 /* 1044 * Name should conform 8.3 format 1045 */ 1046 if (nmlen > 12) 1047 return ENAMETOOLONG; 1048 cp = index(name, '.'); 1049 if (cp == NULL) 1050 return error; 1051 if (cp == name || (cp - name) > 8) 1052 return error; 1053 cp = index(cp + 1, '.'); 1054 if (cp != NULL) 1055 return error; 1056 for (cp = name, i = 0; i < nmlen; i++, cp++) 1057 if (index(badchars83, *cp) != NULL) 1058 return error; 1059 } 1060 for (cp = name, i = 0; i < nmlen; i++, cp++) 1061 if (index(badchars, *cp) != NULL) 1062 return error; 1063 return 0; 1064 } 1065 1066 #ifndef PDIRUNLOCK 1067 #define PDIRUNLOCK 0 1068 #endif 1069 1070 /* 1071 * Things go even weird without fixed inode numbers... 1072 */ 1073 int 1074 smbfs_lookup(ap) 1075 struct vop_lookup_args /* { 1076 struct vnodeop_desc *a_desc; 1077 struct vnode *a_dvp; 1078 struct vnode **a_vpp; 1079 struct componentname *a_cnp; 1080 } */ *ap; 1081 { 1082 struct componentname *cnp = ap->a_cnp; 1083 struct thread *td = cnp->cn_thread; 1084 struct vnode *dvp = ap->a_dvp; 1085 struct vnode **vpp = ap->a_vpp; 1086 struct vnode *vp; 1087 struct smbmount *smp; 1088 struct mount *mp = dvp->v_mount; 1089 struct smbnode *dnp; 1090 struct smbfattr fattr, *fap; 1091 struct smb_cred scred; 1092 char *name = cnp->cn_nameptr; 1093 int flags = cnp->cn_flags; 1094 int nameiop = cnp->cn_nameiop; 1095 int nmlen = cnp->cn_namelen; 1096 int lockparent, wantparent, error, islastcn, isdot; 1097 int killit; 1098 1099 SMBVDEBUG("\n"); 1100 cnp->cn_flags &= ~PDIRUNLOCK; 1101 if (dvp->v_type != VDIR) 1102 return ENOTDIR; 1103 if ((flags & ISDOTDOT) && (dvp->v_vflag & VV_ROOT)) { 1104 SMBFSERR("invalid '..'\n"); 1105 return EIO; 1106 } 1107 #ifdef SMB_VNODE_DEBUG 1108 { 1109 char *cp, c; 1110 1111 cp = name + nmlen; 1112 c = *cp; 1113 *cp = 0; 1114 SMBVDEBUG("%d '%s' in '%s' id=d\n", nameiop, name, 1115 VTOSMB(dvp)->n_name); 1116 *cp = c; 1117 } 1118 #endif 1119 islastcn = flags & ISLASTCN; 1120 if (islastcn && (mp->mnt_flag & MNT_RDONLY) && (nameiop != LOOKUP)) 1121 return EROFS; 1122 if ((error = VOP_ACCESS(dvp, VEXEC, cnp->cn_cred, td)) != 0) 1123 return error; 1124 lockparent = flags & LOCKPARENT; 1125 wantparent = flags & (LOCKPARENT|WANTPARENT); 1126 smp = VFSTOSMBFS(mp); 1127 dnp = VTOSMB(dvp); 1128 isdot = (nmlen == 1 && name[0] == '.'); 1129 1130 error = smbfs_pathcheck(smp, cnp->cn_nameptr, cnp->cn_namelen, nameiop); 1131 1132 if (error) 1133 return ENOENT; 1134 1135 error = cache_lookup(dvp, vpp, cnp); 1136 SMBVDEBUG("cache_lookup returned %d\n", error); 1137 if (error > 0) 1138 return error; 1139 if (error) { /* name was found */ 1140 struct vattr vattr; 1141 int vpid; 1142 1143 vp = *vpp; 1144 mp_fixme("Unlocked v_id access."); 1145 vpid = vp->v_id; 1146 if (dvp == vp) { /* lookup on current */ 1147 vref(vp); 1148 error = 0; 1149 SMBVDEBUG("cached '.'\n"); 1150 } else if (flags & ISDOTDOT) { 1151 VOP_UNLOCK(dvp, 0, td); /* unlock parent */ 1152 cnp->cn_flags |= PDIRUNLOCK; 1153 error = vget(vp, LK_EXCLUSIVE, td); 1154 if (!error && lockparent && islastcn) { 1155 error = vn_lock(dvp, LK_EXCLUSIVE, td); 1156 if (error == 0) 1157 cnp->cn_flags &= ~PDIRUNLOCK; 1158 } 1159 } else { 1160 error = vget(vp, LK_EXCLUSIVE, td); 1161 if (!lockparent || error || !islastcn) { 1162 VOP_UNLOCK(dvp, 0, td); 1163 cnp->cn_flags |= PDIRUNLOCK; 1164 } 1165 } 1166 if (!error) { 1167 killit = 0; 1168 if (vpid == vp->v_id) { 1169 error = VOP_GETATTR(vp, &vattr, cnp->cn_cred, td); 1170 /* 1171 * If the file type on the server is inconsistent 1172 * with what it was when we created the vnode, 1173 * kill the bogus vnode now and fall through to 1174 * the code below to create a new one with the 1175 * right type. 1176 */ 1177 if (error == 0 && 1178 ((vp->v_type == VDIR && 1179 (VTOSMB(vp)->n_dosattr & SMB_FA_DIR) == 0) || 1180 (vp->v_type == VREG && 1181 (VTOSMB(vp)->n_dosattr & SMB_FA_DIR) != 0))) 1182 killit = 1; 1183 else if (error == 0 1184 /* && vattr.va_ctime.tv_sec == VTOSMB(vp)->n_ctime*/) { 1185 if (nameiop != LOOKUP && islastcn) 1186 cnp->cn_flags |= SAVENAME; 1187 SMBVDEBUG("use cached vnode\n"); 1188 return (0); 1189 } 1190 cache_purge(vp); 1191 } 1192 vput(vp); 1193 if (killit) 1194 vgone(vp); 1195 if (lockparent && dvp != vp && islastcn) 1196 VOP_UNLOCK(dvp, 0, td); 1197 } 1198 error = vn_lock(dvp, LK_EXCLUSIVE, td); 1199 *vpp = NULLVP; 1200 if (error) { 1201 cnp->cn_flags |= PDIRUNLOCK; 1202 return (error); 1203 } 1204 cnp->cn_flags &= ~PDIRUNLOCK; 1205 } 1206 /* 1207 * entry is not in the cache or has been expired 1208 */ 1209 error = 0; 1210 *vpp = NULLVP; 1211 smb_makescred(&scred, td, cnp->cn_cred); 1212 fap = &fattr; 1213 if (flags & ISDOTDOT) { 1214 error = smbfs_smb_lookup(VTOSMB(dnp->n_parent), NULL, 0, fap, 1215 &scred); 1216 SMBVDEBUG("result of dotdot lookup: %d\n", error); 1217 } else { 1218 fap = &fattr; 1219 error = smbfs_smb_lookup(dnp, name, nmlen, fap, &scred); 1220 /* if (cnp->cn_namelen == 1 && cnp->cn_nameptr[0] == '.')*/ 1221 SMBVDEBUG("result of smbfs_smb_lookup: %d\n", error); 1222 } 1223 if (error && error != ENOENT) 1224 return error; 1225 if (error) { /* entry not found */ 1226 /* 1227 * Handle RENAME or CREATE case... 1228 */ 1229 if ((nameiop == CREATE || nameiop == RENAME) && wantparent && islastcn) { 1230 error = VOP_ACCESS(dvp, VWRITE, cnp->cn_cred, td); 1231 if (error) 1232 return error; 1233 cnp->cn_flags |= SAVENAME; 1234 if (!lockparent) { 1235 VOP_UNLOCK(dvp, 0, td); 1236 cnp->cn_flags |= PDIRUNLOCK; 1237 } 1238 return (EJUSTRETURN); 1239 } 1240 return ENOENT; 1241 }/* else { 1242 SMBVDEBUG("Found entry %s with id=%d\n", fap->entryName, fap->dirEntNum); 1243 }*/ 1244 /* 1245 * handle DELETE case ... 1246 */ 1247 if (nameiop == DELETE && islastcn) { /* delete last component */ 1248 error = VOP_ACCESS(dvp, VWRITE, cnp->cn_cred, td); 1249 if (error) 1250 return error; 1251 if (isdot) { 1252 VREF(dvp); 1253 *vpp = dvp; 1254 return 0; 1255 } 1256 error = smbfs_nget(mp, dvp, name, nmlen, fap, &vp); 1257 if (error) 1258 return error; 1259 *vpp = vp; 1260 cnp->cn_flags |= SAVENAME; 1261 if (!lockparent) { 1262 VOP_UNLOCK(dvp, 0, td); 1263 cnp->cn_flags |= PDIRUNLOCK; 1264 } 1265 return 0; 1266 } 1267 if (nameiop == RENAME && islastcn && wantparent) { 1268 error = VOP_ACCESS(dvp, VWRITE, cnp->cn_cred, td); 1269 if (error) 1270 return error; 1271 if (isdot) 1272 return EISDIR; 1273 error = smbfs_nget(mp, dvp, name, nmlen, fap, &vp); 1274 if (error) 1275 return error; 1276 *vpp = vp; 1277 cnp->cn_flags |= SAVENAME; 1278 if (!lockparent) { 1279 VOP_UNLOCK(dvp, 0, td); 1280 cnp->cn_flags |= PDIRUNLOCK; 1281 } 1282 return 0; 1283 } 1284 if (flags & ISDOTDOT) { 1285 VOP_UNLOCK(dvp, 0, td); 1286 error = smbfs_nget(mp, dvp, name, nmlen, NULL, &vp); 1287 if (error) { 1288 vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY, td); 1289 return error; 1290 } 1291 if (lockparent && islastcn) { 1292 error = vn_lock(dvp, LK_EXCLUSIVE, td); 1293 if (error) { 1294 cnp->cn_flags |= PDIRUNLOCK; 1295 vput(vp); 1296 return error; 1297 } 1298 } 1299 *vpp = vp; 1300 } else if (isdot) { 1301 vref(dvp); 1302 *vpp = dvp; 1303 } else { 1304 error = smbfs_nget(mp, dvp, name, nmlen, fap, &vp); 1305 if (error) 1306 return error; 1307 *vpp = vp; 1308 SMBVDEBUG("lookup: getnewvp!\n"); 1309 if (!lockparent || !islastcn) { 1310 VOP_UNLOCK(dvp, 0, td); 1311 cnp->cn_flags |= PDIRUNLOCK; 1312 } 1313 } 1314 if ((cnp->cn_flags & MAKEENTRY)/* && !islastcn*/) { 1315 /* VTOSMB(*vpp)->n_ctime = VTOSMB(*vpp)->n_vattr.va_ctime.tv_sec;*/ 1316 cache_enter(dvp, *vpp, cnp); 1317 } 1318 return 0; 1319 } 1320