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