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 int a_mode; 127 struct ucred *a_cred; 128 struct thread *a_td; 129 } */ *ap; 130 { 131 struct vnode *vp = ap->a_vp; 132 mode_t mode = ap->a_mode; 133 mode_t mpmode; 134 struct smbmount *smp = VTOSMBFS(vp); 135 136 SMBVDEBUG("\n"); 137 if ((mode & 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_mode, 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, ap->a_td); 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, ap->a_td); 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 struct thread *a_td; 258 } */ *ap; 259 { 260 struct vnode *vp = ap->a_vp; 261 struct smbnode *np = VTOSMB(vp); 262 struct vattr *va=ap->a_vap; 263 struct smbfattr fattr; 264 struct smb_cred scred; 265 u_quad_t oldsize; 266 int error; 267 268 SMBVDEBUG("%lx: '%s' %d\n", (long)vp, np->n_name, (vp->v_vflag & VV_ROOT) != 0); 269 error = smbfs_attr_cachelookup(vp, va); 270 if (!error) 271 return 0; 272 SMBVDEBUG("not in the cache\n"); 273 smb_makescred(&scred, ap->a_td, ap->a_cred); 274 oldsize = np->n_size; 275 error = smbfs_smb_lookup(np, NULL, 0, &fattr, &scred); 276 if (error) { 277 SMBVDEBUG("error %d\n", error); 278 return error; 279 } 280 smbfs_attr_cacheenter(vp, &fattr); 281 smbfs_attr_cachelookup(vp, va); 282 if (np->n_flag & NOPEN) 283 np->n_size = oldsize; 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 struct thread *a_td; 294 } */ *ap; 295 { 296 struct vnode *vp = ap->a_vp; 297 struct smbnode *np = VTOSMB(vp); 298 struct vattr *vap = ap->a_vap; 299 struct timespec *mtime, *atime; 300 struct smb_cred scred; 301 struct smb_share *ssp = np->n_mount->sm_share; 302 struct smb_vc *vcp = SSTOVC(ssp); 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 smb_makescred(&scred, ap->a_td, ap->a_cred); 319 if (vap->va_size != VNOVAL) { 320 switch (vp->v_type) { 321 case VDIR: 322 return EISDIR; 323 case VREG: 324 break; 325 default: 326 return EINVAL; 327 }; 328 if (isreadonly) 329 return EROFS; 330 doclose = 0; 331 vnode_pager_setsize(vp, (u_long)vap->va_size); 332 tsize = np->n_size; 333 np->n_size = vap->va_size; 334 if ((np->n_flag & NOPEN) == 0) { 335 error = smbfs_smb_open(np, 336 SMB_SM_DENYNONE|SMB_AM_OPENRW, 337 &scred); 338 if (error == 0) 339 doclose = 1; 340 } 341 if (error == 0) 342 error = smbfs_smb_setfsize(np, vap->va_size, &scred); 343 if (doclose) 344 smbfs_smb_close(ssp, np->n_fid, NULL, &scred); 345 if (error) { 346 np->n_size = tsize; 347 vnode_pager_setsize(vp, (u_long)tsize); 348 return error; 349 } 350 } 351 if (vap->va_mode != (mode_t)VNOVAL) { 352 old_n_dosattr = np->n_dosattr; 353 if (vap->va_mode & S_IWUSR) 354 np->n_dosattr &= ~SMB_FA_RDONLY; 355 else 356 np->n_dosattr |= SMB_FA_RDONLY; 357 if (np->n_dosattr != old_n_dosattr) { 358 error = smbfs_smb_setpattr(np, np->n_dosattr, NULL, &scred); 359 if (error) 360 return error; 361 } 362 } 363 mtime = atime = NULL; 364 if (vap->va_mtime.tv_sec != VNOVAL) 365 mtime = &vap->va_mtime; 366 if (vap->va_atime.tv_sec != VNOVAL) 367 atime = &vap->va_atime; 368 if (mtime != atime) { 369 if (vap->va_vaflags & VA_UTIMES_NULL) { 370 error = VOP_ACCESS(vp, VADMIN, ap->a_cred, ap->a_td); 371 if (error) 372 error = VOP_ACCESS(vp, VWRITE, ap->a_cred, 373 ap->a_td); 374 } else 375 error = VOP_ACCESS(vp, VADMIN, ap->a_cred, ap->a_td); 376 #if 0 377 if (mtime == NULL) 378 mtime = &np->n_mtime; 379 if (atime == NULL) 380 atime = &np->n_atime; 381 #endif 382 /* 383 * If file is opened, then we can use handle based calls. 384 * If not, use path based ones. 385 */ 386 if ((np->n_flag & NOPEN) == 0) { 387 if (vcp->vc_flags & SMBV_WIN95) { 388 error = VOP_OPEN(vp, FWRITE, ap->a_cred, ap->a_td, NULL); 389 if (!error) { 390 /* error = smbfs_smb_setfattrNT(np, 0, mtime, atime, &scred); 391 VOP_GETATTR(vp, &vattr, ap->a_cred, ap->a_td);*/ 392 if (mtime) 393 np->n_mtime = *mtime; 394 VOP_CLOSE(vp, FWRITE, ap->a_cred, ap->a_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, ap->a_td); 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, cnp->cn_thread))) 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, cnp->cn_thread))) { 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 int error = 0; 854 855 SMBVDEBUG("\n"); 856 if (bp->b_flags & B_ASYNC) 857 td = (struct thread *)0; 858 else 859 td = curthread; /* XXX */ 860 if (bp->b_iocmd == BIO_READ) 861 cr = bp->b_rcred; 862 else 863 cr = bp->b_wcred; 864 865 if ((bp->b_flags & B_ASYNC) == 0 ) 866 error = smbfs_doio(ap->a_vp, bp, cr, td); 867 return error; 868 } 869 870 int 871 smbfs_ioctl(ap) 872 struct vop_ioctl_args /* { 873 struct vnode *a_vp; 874 u_long a_command; 875 caddr_t a_data; 876 int fflag; 877 struct ucred *cred; 878 struct thread *td; 879 } */ *ap; 880 { 881 return ENOTTY; 882 } 883 884 static char smbfs_atl[] = "rhsvda"; 885 static int 886 smbfs_getextattr(struct vop_getextattr_args *ap) 887 /* { 888 IN struct vnode *a_vp; 889 IN char *a_name; 890 INOUT struct uio *a_uio; 891 IN struct ucred *a_cred; 892 IN struct thread *a_td; 893 }; 894 */ 895 { 896 struct vnode *vp = ap->a_vp; 897 struct thread *td = ap->a_td; 898 struct ucred *cred = ap->a_cred; 899 struct uio *uio = ap->a_uio; 900 const char *name = ap->a_name; 901 struct smbnode *np = VTOSMB(vp); 902 struct vattr vattr; 903 char buf[10]; 904 int i, attr, error; 905 906 error = VOP_ACCESS(vp, VREAD, cred, td); 907 if (error) 908 return error; 909 error = VOP_GETATTR(vp, &vattr, cred, td); 910 if (error) 911 return error; 912 if (strcmp(name, "dosattr") == 0) { 913 attr = np->n_dosattr; 914 for (i = 0; i < 6; i++, attr >>= 1) 915 buf[i] = (attr & 1) ? smbfs_atl[i] : '-'; 916 buf[i] = 0; 917 error = uiomove(buf, i, uio); 918 919 } else 920 error = EINVAL; 921 return error; 922 } 923 924 /* 925 * Since we expected to support F_GETLK (and SMB protocol has no such function), 926 * it is necessary to use lf_advlock(). It would be nice if this function had 927 * a callback mechanism because it will help to improve a level of consistency. 928 */ 929 int 930 smbfs_advlock(ap) 931 struct vop_advlock_args /* { 932 struct vnode *a_vp; 933 caddr_t a_id; 934 int a_op; 935 struct flock *a_fl; 936 int a_flags; 937 } */ *ap; 938 { 939 struct vnode *vp = ap->a_vp; 940 struct smbnode *np = VTOSMB(vp); 941 struct flock *fl = ap->a_fl; 942 caddr_t id = (caddr_t)1 /* ap->a_id */; 943 /* int flags = ap->a_flags;*/ 944 struct thread *td = curthread; 945 struct smb_cred scred; 946 u_quad_t size; 947 off_t start, end, oadd; 948 int error, lkop; 949 950 if (vp->v_type == VDIR) { 951 /* 952 * SMB protocol have no support for directory locking. 953 * Although locks can be processed on local machine, I don't 954 * think that this is a good idea, because some programs 955 * can work wrong assuming directory is locked. So, we just 956 * return 'operation not supported 957 */ 958 return EOPNOTSUPP; 959 } 960 size = np->n_size; 961 switch (fl->l_whence) { 962 963 case SEEK_SET: 964 case SEEK_CUR: 965 start = fl->l_start; 966 break; 967 968 case SEEK_END: 969 if (size > OFF_MAX || 970 (fl->l_start > 0 && size > OFF_MAX - fl->l_start)) 971 return EOVERFLOW; 972 start = size + fl->l_start; 973 break; 974 975 default: 976 return EINVAL; 977 } 978 if (start < 0) 979 return EINVAL; 980 if (fl->l_len < 0) { 981 if (start == 0) 982 return EINVAL; 983 end = start - 1; 984 start += fl->l_len; 985 if (start < 0) 986 return EINVAL; 987 } else if (fl->l_len == 0) 988 end = -1; 989 else { 990 oadd = fl->l_len - 1; 991 if (oadd > OFF_MAX - start) 992 return EOVERFLOW; 993 end = start + oadd; 994 } 995 smb_makescred(&scred, td, td->td_ucred); 996 switch (ap->a_op) { 997 case F_SETLK: 998 switch (fl->l_type) { 999 case F_WRLCK: 1000 lkop = SMB_LOCK_EXCL; 1001 break; 1002 case F_RDLCK: 1003 lkop = SMB_LOCK_SHARED; 1004 break; 1005 case F_UNLCK: 1006 lkop = SMB_LOCK_RELEASE; 1007 break; 1008 default: 1009 return EINVAL; 1010 } 1011 error = lf_advlock(ap, &np->n_lockf, size); 1012 if (error) 1013 break; 1014 lkop = SMB_LOCK_EXCL; 1015 error = smbfs_smb_lock(np, lkop, id, start, end, &scred); 1016 if (error) { 1017 ap->a_op = F_UNLCK; 1018 lf_advlock(ap, &np->n_lockf, size); 1019 } 1020 break; 1021 case F_UNLCK: 1022 lf_advlock(ap, &np->n_lockf, size); 1023 error = smbfs_smb_lock(np, SMB_LOCK_RELEASE, id, start, end, &scred); 1024 break; 1025 case F_GETLK: 1026 error = lf_advlock(ap, &np->n_lockf, size); 1027 break; 1028 default: 1029 return EINVAL; 1030 } 1031 return error; 1032 } 1033 1034 static int 1035 smbfs_pathcheck(struct smbmount *smp, const char *name, int nmlen, int nameiop) 1036 { 1037 static const char *badchars = "*/:<>;?"; 1038 static const char *badchars83 = " +|,[]="; 1039 const char *cp; 1040 int i, error; 1041 1042 /* 1043 * Backslash characters, being a path delimiter, are prohibited 1044 * within a path component even for LOOKUP operations. 1045 */ 1046 if (index(name, '\\') != NULL) 1047 return ENOENT; 1048 1049 if (nameiop == LOOKUP) 1050 return 0; 1051 error = ENOENT; 1052 if (SMB_DIALECT(SSTOVC(smp->sm_share)) < SMB_DIALECT_LANMAN2_0) { 1053 /* 1054 * Name should conform 8.3 format 1055 */ 1056 if (nmlen > 12) 1057 return ENAMETOOLONG; 1058 cp = index(name, '.'); 1059 if (cp == NULL) 1060 return error; 1061 if (cp == name || (cp - name) > 8) 1062 return error; 1063 cp = index(cp + 1, '.'); 1064 if (cp != NULL) 1065 return error; 1066 for (cp = name, i = 0; i < nmlen; i++, cp++) 1067 if (index(badchars83, *cp) != NULL) 1068 return error; 1069 } 1070 for (cp = name, i = 0; i < nmlen; i++, cp++) 1071 if (index(badchars, *cp) != NULL) 1072 return error; 1073 return 0; 1074 } 1075 1076 /* 1077 * Things go even weird without fixed inode numbers... 1078 */ 1079 int 1080 smbfs_lookup(ap) 1081 struct vop_lookup_args /* { 1082 struct vnodeop_desc *a_desc; 1083 struct vnode *a_dvp; 1084 struct vnode **a_vpp; 1085 struct componentname *a_cnp; 1086 } */ *ap; 1087 { 1088 struct componentname *cnp = ap->a_cnp; 1089 struct thread *td = cnp->cn_thread; 1090 struct vnode *dvp = ap->a_dvp; 1091 struct vnode **vpp = ap->a_vpp; 1092 struct vnode *vp; 1093 struct smbmount *smp; 1094 struct mount *mp = dvp->v_mount; 1095 struct smbnode *dnp; 1096 struct smbfattr fattr, *fap; 1097 struct smb_cred scred; 1098 char *name = cnp->cn_nameptr; 1099 int flags = cnp->cn_flags; 1100 int nameiop = cnp->cn_nameiop; 1101 int nmlen = cnp->cn_namelen; 1102 int error, islastcn, isdot; 1103 int killit; 1104 1105 SMBVDEBUG("\n"); 1106 if (dvp->v_type != VDIR) 1107 return ENOTDIR; 1108 if ((flags & ISDOTDOT) && (dvp->v_vflag & VV_ROOT)) { 1109 SMBFSERR("invalid '..'\n"); 1110 return EIO; 1111 } 1112 #ifdef SMB_VNODE_DEBUG 1113 { 1114 char *cp, c; 1115 1116 cp = name + nmlen; 1117 c = *cp; 1118 *cp = 0; 1119 SMBVDEBUG("%d '%s' in '%s' id=d\n", nameiop, name, 1120 VTOSMB(dvp)->n_name); 1121 *cp = c; 1122 } 1123 #endif 1124 islastcn = flags & ISLASTCN; 1125 if (islastcn && (mp->mnt_flag & MNT_RDONLY) && (nameiop != LOOKUP)) 1126 return EROFS; 1127 if ((error = VOP_ACCESS(dvp, VEXEC, cnp->cn_cred, td)) != 0) 1128 return error; 1129 smp = VFSTOSMBFS(mp); 1130 dnp = VTOSMB(dvp); 1131 isdot = (nmlen == 1 && name[0] == '.'); 1132 1133 error = smbfs_pathcheck(smp, cnp->cn_nameptr, cnp->cn_namelen, nameiop); 1134 1135 if (error) 1136 return ENOENT; 1137 1138 error = cache_lookup(dvp, vpp, cnp); 1139 SMBVDEBUG("cache_lookup returned %d\n", error); 1140 if (error > 0) 1141 return error; 1142 if (error) { /* name was found */ 1143 struct vattr vattr; 1144 1145 killit = 0; 1146 vp = *vpp; 1147 error = VOP_GETATTR(vp, &vattr, cnp->cn_cred, td); 1148 /* 1149 * If the file type on the server is inconsistent 1150 * with what it was when we created the vnode, 1151 * kill the bogus vnode now and fall through to 1152 * the code below to create a new one with the 1153 * right type. 1154 */ 1155 if (error == 0 && 1156 ((vp->v_type == VDIR && 1157 (VTOSMB(vp)->n_dosattr & SMB_FA_DIR) == 0) || 1158 (vp->v_type == VREG && 1159 (VTOSMB(vp)->n_dosattr & SMB_FA_DIR) != 0))) 1160 killit = 1; 1161 else if (error == 0 1162 /* && vattr.va_ctime.tv_sec == VTOSMB(vp)->n_ctime*/) { 1163 if (nameiop != LOOKUP && islastcn) 1164 cnp->cn_flags |= SAVENAME; 1165 SMBVDEBUG("use cached vnode\n"); 1166 return (0); 1167 } 1168 cache_purge(vp); 1169 /* 1170 * XXX This is not quite right, if '.' is 1171 * inconsistent, we really need to start the lookup 1172 * all over again. Hopefully there is some other 1173 * guarantee that prevents this case from happening. 1174 */ 1175 if (killit && vp != dvp) 1176 vgone(vp); 1177 if (vp != dvp) 1178 vput(vp); 1179 else 1180 vrele(vp); 1181 *vpp = NULLVP; 1182 } 1183 /* 1184 * entry is not in the cache or has been expired 1185 */ 1186 error = 0; 1187 *vpp = NULLVP; 1188 smb_makescred(&scred, td, cnp->cn_cred); 1189 fap = &fattr; 1190 if (flags & ISDOTDOT) { 1191 error = smbfs_smb_lookup(VTOSMB(dnp->n_parent), NULL, 0, fap, 1192 &scred); 1193 SMBVDEBUG("result of dotdot lookup: %d\n", error); 1194 } else { 1195 fap = &fattr; 1196 error = smbfs_smb_lookup(dnp, name, nmlen, fap, &scred); 1197 /* if (cnp->cn_namelen == 1 && cnp->cn_nameptr[0] == '.')*/ 1198 SMBVDEBUG("result of smbfs_smb_lookup: %d\n", error); 1199 } 1200 if (error && error != ENOENT) 1201 return error; 1202 if (error) { /* entry not found */ 1203 /* 1204 * Handle RENAME or CREATE case... 1205 */ 1206 if ((nameiop == CREATE || nameiop == RENAME) && islastcn) { 1207 error = VOP_ACCESS(dvp, VWRITE, cnp->cn_cred, td); 1208 if (error) 1209 return error; 1210 cnp->cn_flags |= SAVENAME; 1211 return (EJUSTRETURN); 1212 } 1213 return ENOENT; 1214 }/* else { 1215 SMBVDEBUG("Found entry %s with id=%d\n", fap->entryName, fap->dirEntNum); 1216 }*/ 1217 /* 1218 * handle DELETE case ... 1219 */ 1220 if (nameiop == DELETE && islastcn) { /* delete last component */ 1221 error = VOP_ACCESS(dvp, VWRITE, cnp->cn_cred, td); 1222 if (error) 1223 return error; 1224 if (isdot) { 1225 VREF(dvp); 1226 *vpp = dvp; 1227 return 0; 1228 } 1229 error = smbfs_nget(mp, dvp, name, nmlen, fap, &vp); 1230 if (error) 1231 return error; 1232 *vpp = vp; 1233 cnp->cn_flags |= SAVENAME; 1234 return 0; 1235 } 1236 if (nameiop == RENAME && islastcn) { 1237 error = VOP_ACCESS(dvp, VWRITE, cnp->cn_cred, td); 1238 if (error) 1239 return error; 1240 if (isdot) 1241 return EISDIR; 1242 error = smbfs_nget(mp, dvp, name, nmlen, fap, &vp); 1243 if (error) 1244 return error; 1245 *vpp = vp; 1246 cnp->cn_flags |= SAVENAME; 1247 return 0; 1248 } 1249 if (flags & ISDOTDOT) { 1250 VOP_UNLOCK(dvp, 0); 1251 error = smbfs_nget(mp, dvp, name, nmlen, NULL, &vp); 1252 vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY); 1253 if (error) 1254 return error; 1255 *vpp = vp; 1256 } else if (isdot) { 1257 vref(dvp); 1258 *vpp = dvp; 1259 } else { 1260 error = smbfs_nget(mp, dvp, name, nmlen, fap, &vp); 1261 if (error) 1262 return error; 1263 *vpp = vp; 1264 SMBVDEBUG("lookup: getnewvp!\n"); 1265 } 1266 if ((cnp->cn_flags & MAKEENTRY)/* && !islastcn*/) { 1267 /* VTOSMB(*vpp)->n_ctime = VTOSMB(*vpp)->n_vattr.va_ctime.tv_sec;*/ 1268 cache_enter(dvp, *vpp, cnp); 1269 } 1270 return 0; 1271 } 1272