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