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