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