smbfs_vnops.c revision 242384
1193323Sed/*-
2193323Sed * Copyright (c) 2000-2001 Boris Popov
3193323Sed * All rights reserved.
4193323Sed *
5193323Sed * Redistribution and use in source and binary forms, with or without
6193323Sed * modification, are permitted provided that the following conditions
7193323Sed * are met:
8193323Sed * 1. Redistributions of source code must retain the above copyright
9193323Sed *    notice, this list of conditions and the following disclaimer.
10193323Sed * 2. Redistributions in binary form must reproduce the above copyright
11193323Sed *    notice, this list of conditions and the following disclaimer in the
12193323Sed *    documentation and/or other materials provided with the distribution.
13193323Sed *
14193323Sed * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15193323Sed * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16193323Sed * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17198090Srdivacky * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18193323Sed * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19193323Sed * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20193323Sed * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21193323Sed * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22193323Sed * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23193323Sed * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24193323Sed * SUCH DAMAGE.
25193323Sed *
26193323Sed * $FreeBSD: head/sys/fs/smbfs/smbfs_vnops.c 242384 2012-10-31 02:54:44Z davide $
27193323Sed */
28193323Sed#include <sys/param.h>
29193323Sed#include <sys/systm.h>
30193323Sed#include <sys/namei.h>
31193323Sed#include <sys/kernel.h>
32193323Sed#include <sys/proc.h>
33193323Sed#include <sys/bio.h>
34193323Sed#include <sys/buf.h>
35193323Sed#include <sys/fcntl.h>
36193323Sed#include <sys/mount.h>
37193323Sed#include <sys/unistd.h>
38193323Sed#include <sys/vnode.h>
39193323Sed#include <sys/limits.h>
40193323Sed#include <sys/lockf.h>
41193323Sed#include <sys/stat.h>
42193323Sed
43193323Sed#include <vm/vm.h>
44193323Sed#include <vm/vm_extern.h>
45193323Sed
46193323Sed
47193323Sed#include <netsmb/smb.h>
48193323Sed#include <netsmb/smb_conn.h>
49193323Sed#include <netsmb/smb_subr.h>
50193323Sed
51193323Sed#include <fs/smbfs/smbfs.h>
52193323Sed#include <fs/smbfs/smbfs_node.h>
53193323Sed#include <fs/smbfs/smbfs_subr.h>
54193323Sed
55234353Sdim/*
56234353Sdim * Prototypes for SMBFS vnode operations
57234353Sdim */
58234353Sdimstatic vop_create_t	smbfs_create;
59193323Sedstatic vop_mknod_t	smbfs_mknod;
60193323Sedstatic vop_open_t	smbfs_open;
61193323Sedstatic vop_close_t	smbfs_close;
62193323Sedstatic vop_access_t	smbfs_access;
63193323Sedstatic vop_getattr_t	smbfs_getattr;
64193323Sedstatic vop_setattr_t	smbfs_setattr;
65193323Sedstatic vop_read_t	smbfs_read;
66193323Sedstatic vop_write_t	smbfs_write;
67193323Sedstatic vop_fsync_t	smbfs_fsync;
68193323Sedstatic vop_remove_t	smbfs_remove;
69193323Sedstatic vop_link_t	smbfs_link;
70193323Sedstatic vop_lookup_t	smbfs_lookup;
71193323Sedstatic vop_rename_t	smbfs_rename;
72193323Sedstatic vop_mkdir_t	smbfs_mkdir;
73193323Sedstatic vop_rmdir_t	smbfs_rmdir;
74193323Sedstatic vop_symlink_t	smbfs_symlink;
75193323Sedstatic vop_readdir_t	smbfs_readdir;
76193323Sedstatic vop_strategy_t	smbfs_strategy;
77193323Sedstatic vop_print_t	smbfs_print;
78193323Sedstatic vop_pathconf_t	smbfs_pathconf;
79199481Srdivackystatic vop_advlock_t	smbfs_advlock;
80193323Sedstatic vop_getextattr_t	smbfs_getextattr;
81193323Sed
82193323Sedstruct vop_vector smbfs_vnodeops = {
83193323Sed	.vop_default =		&default_vnodeops,
84199481Srdivacky
85193323Sed	.vop_access =		smbfs_access,
86193323Sed	.vop_advlock =		smbfs_advlock,
87193323Sed	.vop_close =		smbfs_close,
88193323Sed	.vop_create =		smbfs_create,
89193323Sed	.vop_fsync =		smbfs_fsync,
90193323Sed	.vop_getattr =		smbfs_getattr,
91193323Sed	.vop_getextattr = 	smbfs_getextattr,
92199481Srdivacky	.vop_getpages =		smbfs_getpages,
93193323Sed	.vop_inactive =		smbfs_inactive,
94193323Sed	.vop_ioctl =		smbfs_ioctl,
95193323Sed	.vop_link =		smbfs_link,
96193323Sed	.vop_lookup =		smbfs_lookup,
97193323Sed	.vop_mkdir =		smbfs_mkdir,
98193323Sed	.vop_mknod =		smbfs_mknod,
99193323Sed	.vop_open =		smbfs_open,
100193323Sed	.vop_pathconf =		smbfs_pathconf,
101193323Sed	.vop_print =		smbfs_print,
102193323Sed	.vop_putpages =		smbfs_putpages,
103193323Sed	.vop_read =		smbfs_read,
104193323Sed	.vop_readdir =		smbfs_readdir,
105193323Sed	.vop_reclaim =		smbfs_reclaim,
106193323Sed	.vop_remove =		smbfs_remove,
107193323Sed	.vop_rename =		smbfs_rename,
108193323Sed	.vop_rmdir =		smbfs_rmdir,
109193323Sed	.vop_setattr =		smbfs_setattr,
110193323Sed/*	.vop_setextattr =	smbfs_setextattr,*/
111193323Sed	.vop_strategy =		smbfs_strategy,
112193323Sed	.vop_symlink =		smbfs_symlink,
113193323Sed	.vop_write =		smbfs_write,
114193323Sed};
115193323Sed
116193323Sedstatic int
117193323Sedsmbfs_access(ap)
118193323Sed	struct vop_access_args /* {
119193323Sed		struct vnode *a_vp;
120218893Sdim		accmode_t a_accmode;
121218893Sdim		struct ucred *a_cred;
122198090Srdivacky		struct thread *a_td;
123198090Srdivacky	} */ *ap;
124193323Sed{
125193323Sed	struct vnode *vp = ap->a_vp;
126193323Sed	accmode_t accmode = ap->a_accmode;
127193323Sed	mode_t mpmode;
128193323Sed	struct smbmount *smp = VTOSMBFS(vp);
129193323Sed
130193323Sed	SMBVDEBUG("\n");
131193323Sed	if ((accmode & VWRITE) && (vp->v_mount->mnt_flag & MNT_RDONLY)) {
132193323Sed		switch (vp->v_type) {
133193323Sed		    case VREG: case VDIR: case VLNK:
134224145Sdim			return EROFS;
135193323Sed		    default:
136193323Sed			break;
137193323Sed		}
138193323Sed	}
139193323Sed	mpmode = vp->v_type == VREG ? smp->sm_file_mode : smp->sm_dir_mode;
140193323Sed	return (vaccess(vp->v_type, mpmode, smp->sm_uid,
141193323Sed	    smp->sm_gid, ap->a_accmode, ap->a_cred, NULL));
142193323Sed}
143193323Sed
144193323Sed/* ARGSUSED */
145193323Sedstatic int
146193323Sedsmbfs_open(ap)
147193323Sed	struct vop_open_args /* {
148193323Sed		struct vnode *a_vp;
149193323Sed		int  a_mode;
150218893Sdim		struct ucred *a_cred;
151193323Sed		struct thread *a_td;
152193323Sed	} */ *ap;
153193323Sed{
154193323Sed	struct vnode *vp = ap->a_vp;
155193323Sed	struct smbnode *np = VTOSMB(vp);
156193323Sed	struct smb_cred scred;
157193323Sed	struct vattr vattr;
158193323Sed	int mode = ap->a_mode;
159193323Sed	int error, accmode;
160193323Sed
161193323Sed	SMBVDEBUG("%s,%d\n", np->n_name, (np->n_flag & NOPEN) != 0);
162193323Sed	if (vp->v_type != VREG && vp->v_type != VDIR) {
163193323Sed		SMBFSERR("open eacces vtype=%d\n", vp->v_type);
164193323Sed		return EACCES;
165193323Sed	}
166193323Sed	if (vp->v_type == VDIR) {
167193323Sed		np->n_flag |= NOPEN;
168193323Sed		return 0;
169193323Sed	}
170193323Sed	if (np->n_flag & NMODIFIED) {
171193323Sed		if ((error = smbfs_vinvalbuf(vp, ap->a_td)) == EINTR)
172193323Sed			return error;
173193323Sed		smbfs_attr_cacheremove(vp);
174193323Sed		error = VOP_GETATTR(vp, &vattr, ap->a_cred);
175193323Sed		if (error)
176193323Sed			return error;
177193323Sed		np->n_mtime.tv_sec = vattr.va_mtime.tv_sec;
178193323Sed	} else {
179193323Sed		error = VOP_GETATTR(vp, &vattr, ap->a_cred);
180193323Sed		if (error)
181193323Sed			return error;
182193323Sed		if (np->n_mtime.tv_sec != vattr.va_mtime.tv_sec) {
183193323Sed			error = smbfs_vinvalbuf(vp, ap->a_td);
184193323Sed			if (error == EINTR)
185193323Sed				return error;
186193323Sed			np->n_mtime.tv_sec = vattr.va_mtime.tv_sec;
187193323Sed		}
188193323Sed	}
189193323Sed	if ((np->n_flag & NOPEN) != 0)
190193323Sed		return 0;
191193323Sed	/*
192193323Sed	 * Use DENYNONE to give unixy semantics of permitting
193193323Sed	 * everything not forbidden by permissions.  Ie denial
194193323Sed	 * is up to server with clients/openers needing to use
195193323Sed	 * advisory locks for further control.
196193323Sed	 */
197193323Sed	accmode = SMB_SM_DENYNONE|SMB_AM_OPENREAD;
198193323Sed	if ((vp->v_mount->mnt_flag & MNT_RDONLY) == 0)
199218893Sdim		accmode = SMB_SM_DENYNONE|SMB_AM_OPENRW;
200206083Srdivacky	smb_makescred(&scred, ap->a_td, ap->a_cred);
201206083Srdivacky	error = smbfs_smb_open(np, accmode, &scred);
202206083Srdivacky	if (error) {
203206083Srdivacky		if (mode & FWRITE)
204206083Srdivacky			return EACCES;
205206083Srdivacky		else if ((vp->v_mount->mnt_flag & MNT_RDONLY) == 0) {
206193323Sed			accmode = SMB_SM_DENYNONE|SMB_AM_OPENREAD;
207218893Sdim			error = smbfs_smb_open(np, accmode, &scred);
208193323Sed		}
209193323Sed	}
210193323Sed	if (error == 0) {
211193323Sed		np->n_flag |= NOPEN;
212193323Sed		vnode_create_vobject(ap->a_vp, vattr.va_size, ap->a_td);
213193323Sed	}
214193323Sed	smbfs_attr_cacheremove(vp);
215193323Sed	return error;
216193323Sed}
217193323Sed
218193323Sedstatic int
219193323Sedsmbfs_close(ap)
220193323Sed	struct vop_close_args /* {
221193323Sed		struct vnodeop_desc *a_desc;
222193323Sed		struct vnode *a_vp;
223193323Sed		int  a_fflag;
224193323Sed		struct ucred *a_cred;
225193323Sed		struct thread *a_td;
226193323Sed	} */ *ap;
227193323Sed{
228193323Sed	struct vnode *vp = ap->a_vp;
229193323Sed	struct thread *td = ap->a_td;
230193323Sed	struct smbnode *np = VTOSMB(vp);
231193323Sed	struct smb_cred scred;
232234353Sdim
233193323Sed	if (vp->v_type == VDIR && (np->n_flag & NOPEN) != 0 &&
234234353Sdim	    np->n_dirseq != NULL) {
235193323Sed		smb_makescred(&scred, td, ap->a_cred);
236193323Sed		smbfs_findclose(np->n_dirseq, &scred);
237193323Sed		np->n_dirseq = NULL;
238218893Sdim	}
239212904Sdim	return 0;
240212904Sdim}
241212904Sdim
242234982Sdim/*
243193323Sed * smbfs_getattr call from vfs.
244193323Sed */
245193323Sedstatic int
246218893Sdimsmbfs_getattr(ap)
247193323Sed	struct vop_getattr_args /* {
248193323Sed		struct vnode *a_vp;
249193323Sed		struct vattr *a_vap;
250193323Sed		struct ucred *a_cred;
251218893Sdim	} */ *ap;
252193323Sed{
253193323Sed	struct vnode *vp = ap->a_vp;
254193323Sed	struct smbnode *np = VTOSMB(vp);
255218893Sdim	struct vattr *va=ap->a_vap;
256218893Sdim	struct smbfattr fattr;
257218893Sdim	struct smb_cred scred;
258218893Sdim	u_quad_t oldsize;
259193323Sed	int error;
260193323Sed
261193323Sed	SMBVDEBUG("%lx: '%s' %d\n", (long)vp, np->n_name, (vp->v_vflag & VV_ROOT) != 0);
262193323Sed	error = smbfs_attr_cachelookup(vp, va);
263193323Sed	if (!error)
264193323Sed		return 0;
265193323Sed	SMBVDEBUG("not in the cache\n");
266193323Sed	smb_makescred(&scred, curthread, ap->a_cred);
267193323Sed	oldsize = np->n_size;
268193323Sed	error = smbfs_smb_lookup(np, NULL, 0, &fattr, &scred);
269193323Sed	if (error) {
270193323Sed		SMBVDEBUG("error %d\n", error);
271193323Sed		return error;
272193323Sed	}
273193323Sed	smbfs_attr_cacheenter(vp, &fattr);
274193323Sed	smbfs_attr_cachelookup(vp, va);
275193323Sed	if (np->n_flag & NOPEN)
276193323Sed		np->n_size = oldsize;
277193323Sed	return 0;
278193323Sed}
279193323Sed
280193323Sedstatic int
281199481Srdivackysmbfs_setattr(ap)
282198090Srdivacky	struct vop_setattr_args /* {
283193323Sed		struct vnode *a_vp;
284234353Sdim		struct vattr *a_vap;
285193323Sed		struct ucred *a_cred;
286193323Sed	} */ *ap;
287199481Srdivacky{
288198090Srdivacky	struct vnode *vp = ap->a_vp;
289193323Sed	struct smbnode *np = VTOSMB(vp);
290234353Sdim	struct vattr *vap = ap->a_vap;
291193323Sed	struct timespec *mtime, *atime;
292193323Sed	struct smb_cred scred;
293193323Sed	struct smb_share *ssp = np->n_mount->sm_share;
294193323Sed	struct smb_vc *vcp = SSTOVC(ssp);
295199481Srdivacky	struct thread *td = curthread;
296193323Sed	u_quad_t tsize = 0;
297193323Sed	int isreadonly, doclose, error = 0;
298193323Sed	int old_n_dosattr;
299193323Sed
300193323Sed	SMBVDEBUG("\n");
301193323Sed	if (vap->va_flags != VNOVAL)
302224145Sdim		return EOPNOTSUPP;
303198090Srdivacky	isreadonly = (vp->v_mount->mnt_flag & MNT_RDONLY);
304193323Sed	/*
305193323Sed	 * Disallow write attempts if the filesystem is mounted read-only.
306199481Srdivacky	 */
307198090Srdivacky  	if ((vap->va_uid != (uid_t)VNOVAL || vap->va_gid != (gid_t)VNOVAL ||
308193323Sed	     vap->va_atime.tv_sec != VNOVAL || vap->va_mtime.tv_sec != VNOVAL ||
309193323Sed	     vap->va_mode != (mode_t)VNOVAL) && isreadonly)
310193323Sed		return EROFS;
311193323Sed	smb_makescred(&scred, td, ap->a_cred);
312193323Sed	if (vap->va_size != VNOVAL) {
313193323Sed 		switch (vp->v_type) {
314198090Srdivacky 		    case VDIR:
315234353Sdim 			return EISDIR;
316234353Sdim 		    case VREG:
317193323Sed			break;
318193323Sed 		    default:
319234353Sdim			return EINVAL;
320193323Sed  		};
321234353Sdim		if (isreadonly)
322193323Sed			return EROFS;
323221345Sdim		doclose = 0;
324193323Sed		vnode_pager_setsize(vp, (u_long)vap->va_size);
325221345Sdim 		tsize = np->n_size;
326193323Sed 		np->n_size = vap->va_size;
327193323Sed		if ((np->n_flag & NOPEN) == 0) {
328193323Sed			error = smbfs_smb_open(np,
329193323Sed					       SMB_SM_DENYNONE|SMB_AM_OPENRW,
330193323Sed					       &scred);
331193323Sed			if (error == 0)
332193323Sed				doclose = 1;
333193323Sed		}
334193323Sed		if (error == 0)
335234353Sdim			error = smbfs_smb_setfsize(np, vap->va_size, &scred);
336234353Sdim		if (doclose)
337234353Sdim			smbfs_smb_close(ssp, np->n_fid, NULL, &scred);
338234353Sdim		if (error) {
339234353Sdim			np->n_size = tsize;
340193323Sed			vnode_pager_setsize(vp, (u_long)tsize);
341193323Sed			return error;
342193323Sed		}
343193323Sed  	}
344221345Sdim	if (vap->va_mode != (mode_t)VNOVAL) {
345193323Sed		old_n_dosattr = np->n_dosattr;
346193323Sed		if (vap->va_mode & S_IWUSR)
347193323Sed			np->n_dosattr &= ~SMB_FA_RDONLY;
348193323Sed		else
349193323Sed			np->n_dosattr |= SMB_FA_RDONLY;
350193323Sed		if (np->n_dosattr != old_n_dosattr) {
351224145Sdim			error = smbfs_smb_setpattr(np, np->n_dosattr, NULL, &scred);
352198090Srdivacky			if (error)
353234353Sdim				return error;
354234353Sdim		}
355234353Sdim	}
356193323Sed	mtime = atime = NULL;
357198090Srdivacky	if (vap->va_mtime.tv_sec != VNOVAL)
358198090Srdivacky		mtime = &vap->va_mtime;
359193323Sed	if (vap->va_atime.tv_sec != VNOVAL)
360234353Sdim		atime = &vap->va_atime;
361193323Sed	if (mtime != atime) {
362193323Sed		if (vap->va_vaflags & VA_UTIMES_NULL) {
363221345Sdim			error = VOP_ACCESS(vp, VADMIN, ap->a_cred, td);
364193323Sed			if (error)
365193323Sed				error = VOP_ACCESS(vp, VWRITE, ap->a_cred, td);
366193323Sed		} else
367234353Sdim			error = VOP_ACCESS(vp, VADMIN, ap->a_cred, td);
368193323Sed#if 0
369221345Sdim		if (mtime == NULL)
370193323Sed			mtime = &np->n_mtime;
371193323Sed		if (atime == NULL)
372193323Sed			atime = &np->n_atime;
373224145Sdim#endif
374198090Srdivacky		/*
375198090Srdivacky		 * If file is opened, then we can use handle based calls.
376198090Srdivacky		 * If not, use path based ones.
377193323Sed		 */
378193323Sed		if ((np->n_flag & NOPEN) == 0) {
379193323Sed			if (vcp->vc_flags & SMBV_WIN95) {
380193323Sed				error = VOP_OPEN(vp, FWRITE, ap->a_cred, td,
381193323Sed				    NULL);
382193323Sed				if (!error) {
383193323Sed/*					error = smbfs_smb_setfattrNT(np, 0,
384193323Sed					    mtime, atime, &scred);
385193323Sed					VOP_GETATTR(vp, &vattr, ap->a_cred); */
386193323Sed					if (mtime)
387193323Sed						np->n_mtime = *mtime;
388193323Sed					VOP_CLOSE(vp, FWRITE, ap->a_cred, td);
389199481Srdivacky				}
390193323Sed			} else if ((vcp->vc_sopt.sv_caps & SMB_CAP_NT_SMBS)) {
391193323Sed				error = smbfs_smb_setptime2(np, mtime, atime, 0, &scred);
392193323Sed/*				error = smbfs_smb_setpattrNT(np, 0, mtime, atime, &scred);*/
393193323Sed			} else if (SMB_DIALECT(vcp) >= SMB_DIALECT_LANMAN2_0) {
394193323Sed				error = smbfs_smb_setptime2(np, mtime, atime, 0, &scred);
395193323Sed			} else {
396193323Sed				error = smbfs_smb_setpattr(np, 0, mtime, &scred);
397193323Sed			}
398193323Sed		} else {
399193323Sed			if (vcp->vc_sopt.sv_caps & SMB_CAP_NT_SMBS) {
400193323Sed				error = smbfs_smb_setfattrNT(np, 0, mtime, atime, &scred);
401193323Sed			} else if (SMB_DIALECT(vcp) >= SMB_DIALECT_LANMAN1_0) {
402193323Sed				error = smbfs_smb_setftime(np, mtime, atime, &scred);
403193323Sed			} else {
404193323Sed				/*
405193323Sed				 * I have no idea how to handle this for core
406234353Sdim				 * level servers. The possible solution is to
407193323Sed				 * update mtime after file is closed.
408193323Sed				 */
409193323Sed				 SMBERROR("can't update times on an opened file\n");
410234353Sdim			}
411193323Sed		}
412193323Sed	}
413193323Sed	/*
414193323Sed	 * Invalidate attribute cache in case if server doesn't set
415193323Sed	 * required attributes.
416193323Sed	 */
417234353Sdim	smbfs_attr_cacheremove(vp);	/* invalidate cache */
418193323Sed	VOP_GETATTR(vp, vap, ap->a_cred);
419193323Sed	np->n_mtime.tv_sec = vap->va_mtime.tv_sec;
420234353Sdim	return error;
421193323Sed}
422193323Sed/*
423193323Sed * smbfs_read call.
424193323Sed */
425193323Sedstatic int
426193323Sedsmbfs_read(ap)
427193323Sed	struct vop_read_args /* {
428193323Sed		struct vnode *a_vp;
429193323Sed		struct uio *a_uio;
430193323Sed		int  a_ioflag;
431193323Sed		struct ucred *a_cred;
432193323Sed	} */ *ap;
433193323Sed{
434193323Sed	struct vnode *vp = ap->a_vp;
435193323Sed	struct uio *uio = ap->a_uio;
436193323Sed
437193323Sed	SMBVDEBUG("\n");
438193323Sed	if (vp->v_type != VREG && vp->v_type != VDIR)
439193323Sed		return EPERM;
440193323Sed	return smbfs_readvnode(vp, uio, ap->a_cred);
441234353Sdim}
442193323Sed
443193323Sedstatic int
444193323Sedsmbfs_write(ap)
445193323Sed	struct vop_write_args /* {
446193323Sed		struct vnode *a_vp;
447193323Sed		struct uio *a_uio;
448193323Sed		int  a_ioflag;
449234353Sdim		struct ucred *a_cred;
450193323Sed	} */ *ap;
451193323Sed{
452193323Sed	struct vnode *vp = ap->a_vp;
453193323Sed	struct uio *uio = ap->a_uio;
454234353Sdim
455193323Sed	SMBVDEBUG("%d,ofs=%jd,sz=%zd\n",vp->v_type, (intmax_t)uio->uio_offset,
456193323Sed	    uio->uio_resid);
457234353Sdim	if (vp->v_type != VREG)
458193323Sed		return (EPERM);
459193323Sed	return smbfs_writevnode(vp, uio, ap->a_cred,ap->a_ioflag);
460193323Sed}
461193323Sed/*
462193323Sed * smbfs_create call
463193323Sed * Create a regular file. On entry the directory to contain the file being
464 * created is locked.  We must release before we return. We must also free
465 * the pathname buffer pointed at by cnp->cn_pnbuf, always on error, or
466 * only if the SAVESTART bit in cn_flags is clear on success.
467 */
468static int
469smbfs_create(ap)
470	struct vop_create_args /* {
471		struct vnode *a_dvp;
472		struct vnode **a_vpp;
473		struct componentname *a_cnp;
474		struct vattr *a_vap;
475	} */ *ap;
476{
477	struct vnode *dvp = ap->a_dvp;
478	struct vattr *vap = ap->a_vap;
479	struct vnode **vpp=ap->a_vpp;
480	struct componentname *cnp = ap->a_cnp;
481	struct smbnode *dnp = VTOSMB(dvp);
482	struct vnode *vp;
483	struct vattr vattr;
484	struct smbfattr fattr;
485	struct smb_cred scred;
486	char *name = cnp->cn_nameptr;
487	int nmlen = cnp->cn_namelen;
488	int error;
489
490
491	SMBVDEBUG("\n");
492	*vpp = NULL;
493	if (vap->va_type != VREG)
494		return EOPNOTSUPP;
495	if ((error = VOP_GETATTR(dvp, &vattr, cnp->cn_cred)))
496		return error;
497	smb_makescred(&scred, cnp->cn_thread, cnp->cn_cred);
498
499	error = smbfs_smb_create(dnp, name, nmlen, &scred);
500	if (error)
501		return error;
502	error = smbfs_smb_lookup(dnp, name, nmlen, &fattr, &scred);
503	if (error)
504		return error;
505	error = smbfs_nget(VTOVFS(dvp), dvp, name, nmlen, &fattr, &vp);
506	if (error)
507		return error;
508	*vpp = vp;
509	if (cnp->cn_flags & MAKEENTRY)
510		cache_enter(dvp, vp, cnp);
511	return error;
512}
513
514static int
515smbfs_remove(ap)
516	struct vop_remove_args /* {
517		struct vnodeop_desc *a_desc;
518		struct vnode * a_dvp;
519		struct vnode * a_vp;
520		struct componentname * a_cnp;
521	} */ *ap;
522{
523	struct vnode *vp = ap->a_vp;
524/*	struct vnode *dvp = ap->a_dvp;*/
525	struct componentname *cnp = ap->a_cnp;
526	struct smbnode *np = VTOSMB(vp);
527	struct smb_cred scred;
528	int error;
529
530	if (vp->v_type == VDIR || (np->n_flag & NOPEN) != 0 || vrefcnt(vp) != 1)
531		return EPERM;
532	smb_makescred(&scred, cnp->cn_thread, cnp->cn_cred);
533	error = smbfs_smb_delete(np, &scred);
534	if (error == 0)
535		np->n_flag |= NGONE;
536	cache_purge(vp);
537	return error;
538}
539
540/*
541 * smbfs_file rename call
542 */
543static int
544smbfs_rename(ap)
545	struct vop_rename_args  /* {
546		struct vnode *a_fdvp;
547		struct vnode *a_fvp;
548		struct componentname *a_fcnp;
549		struct vnode *a_tdvp;
550		struct vnode *a_tvp;
551		struct componentname *a_tcnp;
552	} */ *ap;
553{
554	struct vnode *fvp = ap->a_fvp;
555	struct vnode *tvp = ap->a_tvp;
556	struct vnode *fdvp = ap->a_fdvp;
557	struct vnode *tdvp = ap->a_tdvp;
558	struct componentname *tcnp = ap->a_tcnp;
559/*	struct componentname *fcnp = ap->a_fcnp;*/
560	struct smb_cred scred;
561	u_int16_t flags = 6;
562	int error=0;
563
564	/* Check for cross-device rename */
565	if ((fvp->v_mount != tdvp->v_mount) ||
566	    (tvp && (fvp->v_mount != tvp->v_mount))) {
567		error = EXDEV;
568		goto out;
569	}
570
571	if (tvp && vrefcnt(tvp) > 1) {
572		error = EBUSY;
573		goto out;
574	}
575	flags = 0x10;			/* verify all writes */
576	if (fvp->v_type == VDIR) {
577		flags |= 2;
578	} else if (fvp->v_type == VREG) {
579		flags |= 1;
580	} else {
581		error = EINVAL;
582		goto out;
583	}
584	smb_makescred(&scred, tcnp->cn_thread, tcnp->cn_cred);
585	/*
586	 * It seems that Samba doesn't implement SMB_COM_MOVE call...
587	 */
588#ifdef notnow
589	if (SMB_DIALECT(SSTOCN(smp->sm_share)) >= SMB_DIALECT_LANMAN1_0) {
590		error = smbfs_smb_move(VTOSMB(fvp), VTOSMB(tdvp),
591		    tcnp->cn_nameptr, tcnp->cn_namelen, flags, &scred);
592	} else
593#endif
594	{
595		/*
596		 * We have to do the work atomicaly
597		 */
598		if (tvp && tvp != fvp) {
599			error = smbfs_smb_delete(VTOSMB(tvp), &scred);
600			if (error)
601				goto out_cacherem;
602			VTOSMB(fvp)->n_flag |= NGONE;
603		}
604		error = smbfs_smb_rename(VTOSMB(fvp), VTOSMB(tdvp),
605		    tcnp->cn_nameptr, tcnp->cn_namelen, &scred);
606	}
607
608	if (fvp->v_type == VDIR) {
609		if (tvp != NULL && tvp->v_type == VDIR)
610			cache_purge(tdvp);
611		cache_purge(fdvp);
612	}
613
614out_cacherem:
615	smbfs_attr_cacheremove(fdvp);
616	smbfs_attr_cacheremove(tdvp);
617out:
618	if (tdvp == tvp)
619		vrele(tdvp);
620	else
621		vput(tdvp);
622	if (tvp)
623		vput(tvp);
624	vrele(fdvp);
625	vrele(fvp);
626#ifdef possible_mistake
627	vgone(fvp);
628	if (tvp)
629		vgone(tvp);
630#endif
631	return error;
632}
633
634/*
635 * somtime it will come true...
636 */
637static int
638smbfs_link(ap)
639	struct vop_link_args /* {
640		struct vnode *a_tdvp;
641		struct vnode *a_vp;
642		struct componentname *a_cnp;
643	} */ *ap;
644{
645	return EOPNOTSUPP;
646}
647
648/*
649 * smbfs_symlink link create call.
650 * Sometime it will be functional...
651 */
652static int
653smbfs_symlink(ap)
654	struct vop_symlink_args /* {
655		struct vnode *a_dvp;
656		struct vnode **a_vpp;
657		struct componentname *a_cnp;
658		struct vattr *a_vap;
659		char *a_target;
660	} */ *ap;
661{
662	return EOPNOTSUPP;
663}
664
665static int
666smbfs_mknod(ap)
667	struct vop_mknod_args /* {
668	} */ *ap;
669{
670	return EOPNOTSUPP;
671}
672
673static int
674smbfs_mkdir(ap)
675	struct vop_mkdir_args /* {
676		struct vnode *a_dvp;
677		struct vnode **a_vpp;
678		struct componentname *a_cnp;
679		struct vattr *a_vap;
680	} */ *ap;
681{
682	struct vnode *dvp = ap->a_dvp;
683/*	struct vattr *vap = ap->a_vap;*/
684	struct vnode *vp;
685	struct componentname *cnp = ap->a_cnp;
686	struct smbnode *dnp = VTOSMB(dvp);
687	struct vattr vattr;
688	struct smb_cred scred;
689	struct smbfattr fattr;
690	char *name = cnp->cn_nameptr;
691	int len = cnp->cn_namelen;
692	int error;
693
694	if ((error = VOP_GETATTR(dvp, &vattr, cnp->cn_cred))) {
695		return error;
696	}
697	if ((name[0] == '.') && ((len == 1) || ((len == 2) && (name[1] == '.'))))
698		return EEXIST;
699	smb_makescred(&scred, cnp->cn_thread, cnp->cn_cred);
700	error = smbfs_smb_mkdir(dnp, name, len, &scred);
701	if (error)
702		return error;
703	error = smbfs_smb_lookup(dnp, name, len, &fattr, &scred);
704	if (error)
705		return error;
706	error = smbfs_nget(VTOVFS(dvp), dvp, name, len, &fattr, &vp);
707	if (error)
708		return error;
709	*ap->a_vpp = vp;
710	return 0;
711}
712
713/*
714 * smbfs_remove directory call
715 */
716static int
717smbfs_rmdir(ap)
718	struct vop_rmdir_args /* {
719		struct vnode *a_dvp;
720		struct vnode *a_vp;
721		struct componentname *a_cnp;
722	} */ *ap;
723{
724	struct vnode *vp = ap->a_vp;
725	struct vnode *dvp = ap->a_dvp;
726	struct componentname *cnp = ap->a_cnp;
727/*	struct smbmount *smp = VTOSMBFS(vp);*/
728	struct smbnode *dnp = VTOSMB(dvp);
729	struct smbnode *np = VTOSMB(vp);
730	struct smb_cred scred;
731	int error;
732
733	if (dvp == vp)
734		return EINVAL;
735
736	smb_makescred(&scred, cnp->cn_thread, cnp->cn_cred);
737	error = smbfs_smb_rmdir(np, &scred);
738	if (error == 0)
739		np->n_flag |= NGONE;
740	dnp->n_flag |= NMODIFIED;
741	smbfs_attr_cacheremove(dvp);
742/*	cache_purge(dvp);*/
743	cache_purge(vp);
744	return error;
745}
746
747/*
748 * smbfs_readdir call
749 */
750static int
751smbfs_readdir(ap)
752	struct vop_readdir_args /* {
753		struct vnode *a_vp;
754		struct uio *a_uio;
755		struct ucred *a_cred;
756		int *a_eofflag;
757		u_long *a_cookies;
758		int a_ncookies;
759	} */ *ap;
760{
761	struct vnode *vp = ap->a_vp;
762	struct uio *uio = ap->a_uio;
763	int error;
764
765	if (vp->v_type != VDIR)
766		return (EPERM);
767#ifdef notnow
768	if (ap->a_ncookies) {
769		printf("smbfs_readdir: no support for cookies now...");
770		return (EOPNOTSUPP);
771	}
772#endif
773	error = smbfs_readvnode(vp, uio, ap->a_cred);
774	return error;
775}
776
777/* ARGSUSED */
778static int
779smbfs_fsync(ap)
780	struct vop_fsync_args /* {
781		struct vnodeop_desc *a_desc;
782		struct vnode * a_vp;
783		struct ucred * a_cred;
784		int  a_waitfor;
785		struct thread * a_td;
786	} */ *ap;
787{
788/*	return (smb_flush(ap->a_vp, ap->a_cred, ap->a_waitfor, ap->a_td, 1));*/
789    return (0);
790}
791
792static
793int smbfs_print (ap)
794	struct vop_print_args /* {
795	struct vnode *a_vp;
796	} */ *ap;
797{
798	struct vnode *vp = ap->a_vp;
799	struct smbnode *np = VTOSMB(vp);
800
801	if (np == NULL) {
802		printf("no smbnode data\n");
803		return (0);
804	}
805	printf("\tname = %s, parent = %p, open = %d\n", np->n_name,
806	    np->n_parent ? np->n_parent : NULL, (np->n_flag & NOPEN) != 0);
807	return (0);
808}
809
810static int
811smbfs_pathconf (ap)
812	struct vop_pathconf_args  /* {
813	struct vnode *vp;
814	int name;
815	register_t *retval;
816	} */ *ap;
817{
818	struct smbmount *smp = VFSTOSMBFS(VTOVFS(ap->a_vp));
819	struct smb_vc *vcp = SSTOVC(smp->sm_share);
820	register_t *retval = ap->a_retval;
821	int error = 0;
822
823	switch (ap->a_name) {
824	    case _PC_LINK_MAX:
825		*retval = 0;
826		break;
827	    case _PC_NAME_MAX:
828		*retval = (vcp->vc_hflags2 & SMB_FLAGS2_KNOWS_LONG_NAMES) ? 255 : 12;
829		break;
830	    case _PC_PATH_MAX:
831		*retval = 800;	/* XXX: a correct one ? */
832		break;
833	    default:
834		error = EINVAL;
835	}
836	return error;
837}
838
839static int
840smbfs_strategy (ap)
841	struct vop_strategy_args /* {
842	struct buf *a_bp
843	} */ *ap;
844{
845	struct buf *bp=ap->a_bp;
846	struct ucred *cr;
847	struct thread *td;
848
849	SMBVDEBUG("\n");
850	if (bp->b_flags & B_ASYNC)
851		td = (struct thread *)0;
852	else
853		td = curthread;	/* XXX */
854	if (bp->b_iocmd == BIO_READ)
855		cr = bp->b_rcred;
856	else
857		cr = bp->b_wcred;
858
859	if ((bp->b_flags & B_ASYNC) == 0 )
860		(void)smbfs_doio(ap->a_vp, bp, cr, td);
861	return (0);
862}
863
864int
865smbfs_ioctl(ap)
866	struct vop_ioctl_args /* {
867		struct vnode *a_vp;
868		u_long a_command;
869		caddr_t a_data;
870		int fflag;
871		struct ucred *cred;
872		struct thread *td;
873	} */ *ap;
874{
875	return ENOTTY;
876}
877
878static char smbfs_atl[] = "rhsvda";
879static int
880smbfs_getextattr(struct vop_getextattr_args *ap)
881/* {
882        IN struct vnode *a_vp;
883        IN char *a_name;
884        INOUT struct uio *a_uio;
885        IN struct ucred *a_cred;
886        IN struct thread *a_td;
887};
888*/
889{
890	struct vnode *vp = ap->a_vp;
891	struct thread *td = ap->a_td;
892	struct ucred *cred = ap->a_cred;
893	struct uio *uio = ap->a_uio;
894	const char *name = ap->a_name;
895	struct smbnode *np = VTOSMB(vp);
896	struct vattr vattr;
897	char buf[10];
898	int i, attr, error;
899
900	error = VOP_ACCESS(vp, VREAD, cred, td);
901	if (error)
902		return error;
903	error = VOP_GETATTR(vp, &vattr, cred);
904	if (error)
905		return error;
906	if (strcmp(name, "dosattr") == 0) {
907		attr = np->n_dosattr;
908		for (i = 0; i < 6; i++, attr >>= 1)
909			buf[i] = (attr & 1) ? smbfs_atl[i] : '-';
910		buf[i] = 0;
911		error = uiomove(buf, i, uio);
912
913	} else
914		error = EINVAL;
915	return error;
916}
917
918/*
919 * Since we expected to support F_GETLK (and SMB protocol has no such function),
920 * it is necessary to use lf_advlock(). It would be nice if this function had
921 * a callback mechanism because it will help to improve a level of consistency.
922 */
923int
924smbfs_advlock(ap)
925	struct vop_advlock_args /* {
926		struct vnode *a_vp;
927		caddr_t  a_id;
928		int  a_op;
929		struct flock *a_fl;
930		int  a_flags;
931	} */ *ap;
932{
933	struct vnode *vp = ap->a_vp;
934	struct smbnode *np = VTOSMB(vp);
935	struct flock *fl = ap->a_fl;
936	caddr_t id = (caddr_t)1 /* ap->a_id */;
937/*	int flags = ap->a_flags;*/
938	struct thread *td = curthread;
939	struct smb_cred scred;
940	u_quad_t size;
941	off_t start, end, oadd;
942	int error, lkop;
943
944	if (vp->v_type == VDIR) {
945		/*
946		 * SMB protocol have no support for directory locking.
947		 * Although locks can be processed on local machine, I don't
948		 * think that this is a good idea, because some programs
949		 * can work wrong assuming directory is locked. So, we just
950		 * return 'operation not supported
951		 */
952		 return EOPNOTSUPP;
953	}
954	size = np->n_size;
955	switch (fl->l_whence) {
956
957	case SEEK_SET:
958	case SEEK_CUR:
959		start = fl->l_start;
960		break;
961
962	case SEEK_END:
963		if (size > OFF_MAX ||
964		    (fl->l_start > 0 && size > OFF_MAX - fl->l_start))
965			return EOVERFLOW;
966		start = size + fl->l_start;
967		break;
968
969	default:
970		return EINVAL;
971	}
972	if (start < 0)
973		return EINVAL;
974	if (fl->l_len < 0) {
975		if (start == 0)
976			return EINVAL;
977		end = start - 1;
978		start += fl->l_len;
979		if (start < 0)
980			return EINVAL;
981	} else if (fl->l_len == 0)
982		end = -1;
983	else {
984		oadd = fl->l_len - 1;
985		if (oadd > OFF_MAX - start)
986			return EOVERFLOW;
987		end = start + oadd;
988	}
989	smb_makescred(&scred, td, td->td_ucred);
990	switch (ap->a_op) {
991	    case F_SETLK:
992		switch (fl->l_type) {
993		    case F_WRLCK:
994			lkop = SMB_LOCK_EXCL;
995			break;
996		    case F_RDLCK:
997			lkop = SMB_LOCK_SHARED;
998			break;
999		    case F_UNLCK:
1000			lkop = SMB_LOCK_RELEASE;
1001			break;
1002		    default:
1003			return EINVAL;
1004		}
1005		error = lf_advlock(ap, &vp->v_lockf, size);
1006		if (error)
1007			break;
1008		lkop = SMB_LOCK_EXCL;
1009		error = smbfs_smb_lock(np, lkop, id, start, end, &scred);
1010		if (error) {
1011			int oldtype = fl->l_type;
1012			fl->l_type = F_UNLCK;
1013			ap->a_op = F_UNLCK;
1014			lf_advlock(ap, &vp->v_lockf, size);
1015			fl->l_type = oldtype;
1016		}
1017		break;
1018	    case F_UNLCK:
1019		lf_advlock(ap, &vp->v_lockf, size);
1020		error = smbfs_smb_lock(np, SMB_LOCK_RELEASE, id, start, end, &scred);
1021		break;
1022	    case F_GETLK:
1023		error = lf_advlock(ap, &vp->v_lockf, size);
1024		break;
1025	    default:
1026		return EINVAL;
1027	}
1028	return error;
1029}
1030
1031static int
1032smbfs_pathcheck(struct smbmount *smp, const char *name, int nmlen, int nameiop)
1033{
1034	static const char *badchars = "*/:<>;?";
1035	static const char *badchars83 = " +|,[]=";
1036	const char *cp;
1037	int i, error;
1038
1039	/*
1040	 * Backslash characters, being a path delimiter, are prohibited
1041	 * within a path component even for LOOKUP operations.
1042	 */
1043	if (strchr(name, '\\') != NULL)
1044		return ENOENT;
1045
1046	if (nameiop == LOOKUP)
1047		return 0;
1048	error = ENOENT;
1049	if (SMB_DIALECT(SSTOVC(smp->sm_share)) < SMB_DIALECT_LANMAN2_0) {
1050		/*
1051		 * Name should conform 8.3 format
1052		 */
1053		if (nmlen > 12)
1054			return ENAMETOOLONG;
1055		cp = strchr(name, '.');
1056		if (cp == NULL)
1057			return error;
1058		if (cp == name || (cp - name) > 8)
1059			return error;
1060		cp = strchr(cp + 1, '.');
1061		if (cp != NULL)
1062			return error;
1063		for (cp = name, i = 0; i < nmlen; i++, cp++)
1064			if (strchr(badchars83, *cp) != NULL)
1065				return error;
1066	}
1067	for (cp = name, i = 0; i < nmlen; i++, cp++)
1068		if (strchr(badchars, *cp) != NULL)
1069			return error;
1070	return 0;
1071}
1072
1073/*
1074 * Things go even weird without fixed inode numbers...
1075 */
1076int
1077smbfs_lookup(ap)
1078	struct vop_lookup_args /* {
1079		struct vnodeop_desc *a_desc;
1080		struct vnode *a_dvp;
1081		struct vnode **a_vpp;
1082		struct componentname *a_cnp;
1083	} */ *ap;
1084{
1085	struct componentname *cnp = ap->a_cnp;
1086	struct thread *td = cnp->cn_thread;
1087	struct vnode *dvp = ap->a_dvp;
1088	struct vnode **vpp = ap->a_vpp;
1089	struct vnode *vp;
1090	struct smbmount *smp;
1091	struct mount *mp = dvp->v_mount;
1092	struct smbnode *dnp;
1093	struct smbfattr fattr, *fap;
1094	struct smb_cred scred;
1095	char *name = cnp->cn_nameptr;
1096	int flags = cnp->cn_flags;
1097	int nameiop = cnp->cn_nameiop;
1098	int nmlen = cnp->cn_namelen;
1099	int error, islastcn, isdot;
1100	int killit;
1101
1102	SMBVDEBUG("\n");
1103	if (dvp->v_type != VDIR)
1104		return ENOTDIR;
1105	if ((flags & ISDOTDOT) && (dvp->v_vflag & VV_ROOT)) {
1106		SMBFSERR("invalid '..'\n");
1107		return EIO;
1108	}
1109#ifdef SMB_VNODE_DEBUG
1110	{
1111		char *cp, c;
1112
1113		cp = name + nmlen;
1114		c = *cp;
1115		*cp = 0;
1116		SMBVDEBUG("%d '%s' in '%s' id=d\n", nameiop, name,
1117			VTOSMB(dvp)->n_name);
1118		*cp = c;
1119	}
1120#endif
1121	islastcn = flags & ISLASTCN;
1122	if (islastcn && (mp->mnt_flag & MNT_RDONLY) && (nameiop != LOOKUP))
1123		return EROFS;
1124	if ((error = VOP_ACCESS(dvp, VEXEC, cnp->cn_cred, td)) != 0)
1125		return error;
1126	smp = VFSTOSMBFS(mp);
1127	dnp = VTOSMB(dvp);
1128	isdot = (nmlen == 1 && name[0] == '.');
1129
1130	error = smbfs_pathcheck(smp, cnp->cn_nameptr, cnp->cn_namelen, nameiop);
1131
1132	if (error)
1133		return ENOENT;
1134
1135	error = cache_lookup(dvp, vpp, cnp, NULL, NULL);
1136	SMBVDEBUG("cache_lookup returned %d\n", error);
1137	if (error > 0)
1138		return error;
1139	if (error) {		/* name was found */
1140		struct vattr vattr;
1141
1142		killit = 0;
1143		vp = *vpp;
1144		error = VOP_GETATTR(vp, &vattr, cnp->cn_cred);
1145		/*
1146		 * If the file type on the server is inconsistent
1147		 * with what it was when we created the vnode,
1148		 * kill the bogus vnode now and fall through to
1149		 * the code below to create a new one with the
1150		 * right type.
1151		 */
1152		if (error == 0 &&
1153		   ((vp->v_type == VDIR &&
1154		   (VTOSMB(vp)->n_dosattr & SMB_FA_DIR) == 0) ||
1155		   (vp->v_type == VREG &&
1156		   (VTOSMB(vp)->n_dosattr & SMB_FA_DIR) != 0)))
1157		   killit = 1;
1158		else if (error == 0
1159	     /*    && vattr.va_ctime.tv_sec == VTOSMB(vp)->n_ctime*/) {
1160		     if (nameiop != LOOKUP && islastcn)
1161			     cnp->cn_flags |= SAVENAME;
1162		     SMBVDEBUG("use cached vnode\n");
1163		     return (0);
1164		}
1165		cache_purge(vp);
1166		/*
1167		 * XXX This is not quite right, if '.' is
1168		 * inconsistent, we really need to start the lookup
1169		 * all over again.  Hopefully there is some other
1170		 * guarantee that prevents this case from happening.
1171		 */
1172		if (killit && vp != dvp)
1173			vgone(vp);
1174		if (vp != dvp)
1175			vput(vp);
1176		else
1177			vrele(vp);
1178		*vpp = NULLVP;
1179	}
1180	/*
1181	 * entry is not in the cache or has been expired
1182	 */
1183	error = 0;
1184	*vpp = NULLVP;
1185	smb_makescred(&scred, td, cnp->cn_cred);
1186	fap = &fattr;
1187	if (flags & ISDOTDOT) {
1188		error = smbfs_smb_lookup(VTOSMB(dnp->n_parent), NULL, 0, fap,
1189		    &scred);
1190		SMBVDEBUG("result of dotdot lookup: %d\n", error);
1191	} else {
1192		fap = &fattr;
1193		error = smbfs_smb_lookup(dnp, name, nmlen, fap, &scred);
1194/*		if (cnp->cn_namelen == 1 && cnp->cn_nameptr[0] == '.')*/
1195		SMBVDEBUG("result of smbfs_smb_lookup: %d\n", error);
1196	}
1197	if (error && error != ENOENT)
1198		return error;
1199	if (error) {			/* entry not found */
1200		/*
1201		 * Handle RENAME or CREATE case...
1202		 */
1203		if ((nameiop == CREATE || nameiop == RENAME) && islastcn) {
1204			error = VOP_ACCESS(dvp, VWRITE, cnp->cn_cred, td);
1205			if (error)
1206				return error;
1207			cnp->cn_flags |= SAVENAME;
1208			return (EJUSTRETURN);
1209		}
1210		return ENOENT;
1211	}/* else {
1212		SMBVDEBUG("Found entry %s with id=%d\n", fap->entryName, fap->dirEntNum);
1213	}*/
1214	/*
1215	 * handle DELETE case ...
1216	 */
1217	if (nameiop == DELETE && islastcn) { 	/* delete last component */
1218		error = VOP_ACCESS(dvp, VWRITE, cnp->cn_cred, td);
1219		if (error)
1220			return error;
1221		if (isdot) {
1222			VREF(dvp);
1223			*vpp = dvp;
1224			return 0;
1225		}
1226		error = smbfs_nget(mp, dvp, name, nmlen, fap, &vp);
1227		if (error)
1228			return error;
1229		*vpp = vp;
1230		cnp->cn_flags |= SAVENAME;
1231		return 0;
1232	}
1233	if (nameiop == RENAME && islastcn) {
1234		error = VOP_ACCESS(dvp, VWRITE, cnp->cn_cred, td);
1235		if (error)
1236			return error;
1237		if (isdot)
1238			return EISDIR;
1239		error = smbfs_nget(mp, dvp, name, nmlen, fap, &vp);
1240		if (error)
1241			return error;
1242		*vpp = vp;
1243		cnp->cn_flags |= SAVENAME;
1244		return 0;
1245	}
1246	if (flags & ISDOTDOT) {
1247		VOP_UNLOCK(dvp, 0);
1248		error = smbfs_nget(mp, dvp, name, nmlen, NULL, &vp);
1249		vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY);
1250		if (error)
1251			return error;
1252		*vpp = vp;
1253	} else if (isdot) {
1254		vref(dvp);
1255		*vpp = dvp;
1256	} else {
1257		error = smbfs_nget(mp, dvp, name, nmlen, fap, &vp);
1258		if (error)
1259			return error;
1260		*vpp = vp;
1261		SMBVDEBUG("lookup: getnewvp!\n");
1262	}
1263	if ((cnp->cn_flags & MAKEENTRY)/* && !islastcn*/) {
1264/*		VTOSMB(*vpp)->n_ctime = VTOSMB(*vpp)->n_vattr.va_ctime.tv_sec;*/
1265		cache_enter(dvp, *vpp, cnp);
1266	}
1267	return 0;
1268}
1269