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