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