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