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