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