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