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