xref: /dragonfly/sys/vfs/smbfs/smbfs_vnops.c (revision 9c600e7d)
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: src/sys/fs/smbfs/smbfs_vnops.c,v 1.2.2.8 2003/04/04 08:57:23 tjr Exp $
33  * $DragonFly: src/sys/vfs/smbfs/smbfs_vnops.c,v 1.5 2003/06/26 05:55:12 dillon Exp $
34  */
35 #include <sys/param.h>
36 #include <sys/systm.h>
37 #include <sys/kernel.h>
38 #include <sys/proc.h>
39 #include <sys/namei.h>
40 #include <sys/fcntl.h>
41 #include <sys/mount.h>
42 #include <sys/unistd.h>
43 #include <sys/vnode.h>
44 #include <sys/lockf.h>
45 
46 #include <vm/vm.h>
47 #include <vm/vm_extern.h>
48 #include <vm/vm_zone.h>
49 
50 
51 #include <netsmb/smb.h>
52 #include <netsmb/smb_conn.h>
53 #include <netsmb/smb_subr.h>
54 
55 #include <fs/smbfs/smbfs.h>
56 #include <fs/smbfs/smbfs_node.h>
57 #include <fs/smbfs/smbfs_subr.h>
58 
59 #include <sys/buf.h>
60 
61 /*
62  * Prototypes for SMBFS vnode operations
63  */
64 static int smbfs_create(struct vop_create_args *);
65 static int smbfs_mknod(struct vop_mknod_args *);
66 static int smbfs_open(struct vop_open_args *);
67 static int smbfs_close(struct vop_close_args *);
68 static int smbfs_access(struct vop_access_args *);
69 static int smbfs_getattr(struct vop_getattr_args *);
70 static int smbfs_setattr(struct vop_setattr_args *);
71 static int smbfs_read(struct vop_read_args *);
72 static int smbfs_write(struct vop_write_args *);
73 static int smbfs_fsync(struct vop_fsync_args *);
74 static int smbfs_remove(struct vop_remove_args *);
75 static int smbfs_link(struct vop_link_args *);
76 static int smbfs_lookup(struct vop_lookup_args *);
77 static int smbfs_rename(struct vop_rename_args *);
78 static int smbfs_mkdir(struct vop_mkdir_args *);
79 static int smbfs_rmdir(struct vop_rmdir_args *);
80 static int smbfs_symlink(struct vop_symlink_args *);
81 static int smbfs_readdir(struct vop_readdir_args *);
82 static int smbfs_bmap(struct vop_bmap_args *);
83 static int smbfs_strategy(struct vop_strategy_args *);
84 static int smbfs_print(struct vop_print_args *);
85 static int smbfs_pathconf(struct vop_pathconf_args *ap);
86 static int smbfs_advlock(struct vop_advlock_args *);
87 static int smbfs_getextattr(struct vop_getextattr_args *ap);
88 
89 vop_t **smbfs_vnodeop_p;
90 static struct vnodeopv_entry_desc smbfs_vnodeop_entries[] = {
91 	{ &vop_default_desc,		(vop_t *) vop_defaultop },
92 	{ &vop_access_desc,		(vop_t *) smbfs_access },
93 	{ &vop_advlock_desc,		(vop_t *) smbfs_advlock },
94 	{ &vop_bmap_desc,		(vop_t *) smbfs_bmap },
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 	{ &vop_getextattr_desc, 	(vop_t *) smbfs_getextattr },
124 /*	{ &vop_setextattr_desc,		(vop_t *) smbfs_setextattr },*/
125 	{ NULL, NULL }
126 };
127 
128 static struct vnodeopv_desc smbfs_vnodeop_opv_desc =
129 	{ &smbfs_vnodeop_p, smbfs_vnodeop_entries };
130 
131 VNODEOP_SET(smbfs_vnodeop_opv_desc);
132 
133 static int
134 smbfs_access(ap)
135 	struct vop_access_args /* {
136 		struct vnode *a_vp;
137 		int  a_mode;
138 		struct ucred *a_cred;
139 		struct thread *a_td;
140 	} */ *ap;
141 {
142 	struct vnode *vp = ap->a_vp;
143 	struct ucred *cred = ap->a_cred;
144 	u_int mode = ap->a_mode;
145 	struct smbmount *smp = VTOSMBFS(vp);
146 	int error = 0;
147 
148 	SMBVDEBUG("\n");
149 	if ((mode & VWRITE) && (vp->v_mount->mnt_flag & MNT_RDONLY)) {
150 		switch (vp->v_type) {
151 		    case VREG: case VDIR: case VLNK:
152 			return EROFS;
153 		    default:
154 			break;
155 		}
156 	}
157 	if (cred->cr_uid == 0)
158 		return 0;
159 	if (cred->cr_uid != smp->sm_args.uid) {
160 		mode >>= 3;
161 		if (!groupmember(smp->sm_args.gid, cred))
162 			mode >>= 3;
163 	}
164 	error = (((vp->v_type == VREG) ? smp->sm_args.file_mode : smp->sm_args.dir_mode) & mode) == mode ? 0 : EACCES;
165 	return error;
166 }
167 
168 /* ARGSUSED */
169 static int
170 smbfs_open(ap)
171 	struct vop_open_args /* {
172 		struct vnode *a_vp;
173 		int  a_mode;
174 		struct ucred *a_cred;
175 		struct thread *a_td;
176 	} */ *ap;
177 {
178 	struct vnode *vp = ap->a_vp;
179 	struct smbnode *np = VTOSMB(vp);
180 	struct smb_cred scred;
181 	struct vattr vattr;
182 	int mode = ap->a_mode;
183 	int error, accmode;
184 
185 	SMBVDEBUG("%s,%d\n", np->n_name, np->n_opencount);
186 	if (vp->v_type != VREG && vp->v_type != VDIR) {
187 		SMBFSERR("open eacces vtype=%d\n", vp->v_type);
188 		return EACCES;
189 	}
190 	if (vp->v_type == VDIR) {
191 		np->n_opencount++;
192 		return 0;
193 	}
194 	if (np->n_flag & NMODIFIED) {
195 		if ((error = smbfs_vinvalbuf(vp, V_SAVE, ap->a_td, 1)) == EINTR)
196 			return error;
197 		smbfs_attr_cacheremove(vp);
198 		error = VOP_GETATTR(vp, &vattr, ap->a_td);
199 		if (error)
200 			return error;
201 		np->n_mtime.tv_sec = vattr.va_mtime.tv_sec;
202 	} else {
203 		error = VOP_GETATTR(vp, &vattr, ap->a_td);
204 		if (error)
205 			return error;
206 		if (np->n_mtime.tv_sec != vattr.va_mtime.tv_sec) {
207 			error = smbfs_vinvalbuf(vp, V_SAVE, ap->a_td, 1);
208 			if (error == EINTR)
209 				return error;
210 			np->n_mtime.tv_sec = vattr.va_mtime.tv_sec;
211 		}
212 	}
213 	if (np->n_opencount) {
214 		np->n_opencount++;
215 		return 0;
216 	}
217 	accmode = SMB_AM_OPENREAD;
218 	if ((vp->v_mount->mnt_flag & MNT_RDONLY) == 0)
219 		accmode = SMB_AM_OPENRW;
220 	smb_makescred(&scred, ap->a_td, ap->a_cred);
221 	error = smbfs_smb_open(np, accmode, &scred);
222 	if (error) {
223 		if (mode & FWRITE)
224 			return EACCES;
225 		accmode = SMB_AM_OPENREAD;
226 		error = smbfs_smb_open(np, accmode, &scred);
227 	}
228 	if (!error) {
229 		np->n_opencount++;
230 	}
231 	smbfs_attr_cacheremove(vp);
232 	return error;
233 }
234 
235 static int
236 smbfs_closel(struct vop_close_args *ap)
237 {
238 	struct vnode *vp = ap->a_vp;
239 	struct smbnode *np = VTOSMB(vp);
240 	struct thread *td = ap->a_td;
241 	struct smb_cred scred;
242 	struct vattr vattr;
243 	int error;
244 
245 	SMBVDEBUG("name=%s, pid=%d, c=%d\n",np->n_name, p->p_pid, np->n_opencount);
246 
247 	smb_makescred(&scred, td, proc0.p_ucred);
248 
249 	if (np->n_opencount == 0) {
250 		if (vp->v_type != VDIR)
251 			SMBERROR("Negative opencount\n");
252 		return 0;
253 	}
254 	np->n_opencount--;
255 	if (vp->v_type == VDIR) {
256 		if (np->n_opencount)
257 			return 0;
258 		if (np->n_dirseq) {
259 			smbfs_findclose(np->n_dirseq, &scred);
260 			np->n_dirseq = NULL;
261 		}
262 		error = 0;
263 	} else {
264 		error = smbfs_vinvalbuf(vp, V_SAVE, td, 1);
265 		if (np->n_opencount)
266 			return error;
267 		VOP_GETATTR(vp, &vattr, td);
268 		error = smbfs_smb_close(np->n_mount->sm_share, np->n_fid,
269 			   &np->n_mtime, &scred);
270 	}
271 	smbfs_attr_cacheremove(vp);
272 	return error;
273 }
274 
275 /*
276  * XXX: VOP_CLOSE() usually called without lock held which is suck. Here we
277  * do some heruistic to determine if vnode should be locked.
278  */
279 static int
280 smbfs_close(ap)
281 	struct vop_close_args /* {
282 		struct vnodeop_desc *a_desc;
283 		struct vnode *a_vp;
284 		int  a_fflag;
285 		struct ucred *a_cred;
286 		struct thread *a_td;
287 	} */ *ap;
288 {
289 	struct vnode *vp = ap->a_vp;
290 	struct thread *td = ap->a_td;
291 	int error, dolock;
292 
293 	VI_LOCK(vp);
294 	dolock = (vp->v_flag & VXLOCK) == 0;
295 	if (dolock)
296 		vn_lock(vp, LK_EXCLUSIVE | LK_RETRY | LK_INTERLOCK, td);
297 	else
298 		VI_UNLOCK(vp);
299 	error = smbfs_closel(ap);
300 	if (dolock)
301 		VOP_UNLOCK(vp, 0, td);
302 	return error;
303 }
304 
305 /*
306  * smbfs_getattr call from vfs.
307  */
308 static int
309 smbfs_getattr(ap)
310 	struct vop_getattr_args /* {
311 		struct vnode *a_vp;
312 		struct vattr *a_vap;
313 		struct thread *a_td;
314 	} */ *ap;
315 {
316 	struct vnode *vp = ap->a_vp;
317 	struct smbnode *np = VTOSMB(vp);
318 	struct vattr *va=ap->a_vap;
319 	struct smbfattr fattr;
320 	struct smb_cred scred;
321 	u_int32_t oldsize;
322 	int error;
323 
324 	SMBVDEBUG("%lx: '%s' %d\n", (long)vp, np->n_name, (vp->v_flag & VROOT) != 0);
325 	error = smbfs_attr_cachelookup(vp, va);
326 	if (!error)
327 		return 0;
328 	SMBVDEBUG("not in the cache\n");
329 	smb_makescred(&scred, ap->a_td, proc0.p_ucred);
330 	oldsize = np->n_size;
331 	error = smbfs_smb_lookup(np, NULL, 0, &fattr, &scred);
332 	if (error) {
333 		SMBVDEBUG("error %d\n", error);
334 		return error;
335 	}
336 	smbfs_attr_cacheenter(vp, &fattr);
337 	smbfs_attr_cachelookup(vp, va);
338 	if (np->n_opencount)
339 		np->n_size = oldsize;
340 	return 0;
341 }
342 
343 static int
344 smbfs_setattr(ap)
345 	struct vop_setattr_args /* {
346 		struct vnode *a_vp;
347 		struct vattr *a_vap;
348 		struct ucred *a_cred;
349 		struct thread *a_td;
350 	} */ *ap;
351 {
352 	struct vnode *vp = ap->a_vp;
353 	struct smbnode *np = VTOSMB(vp);
354 	struct vattr *vap = ap->a_vap;
355 	struct timespec *mtime, *atime;
356 	struct smb_cred scred;
357 	struct smb_share *ssp = np->n_mount->sm_share;
358 	struct smb_vc *vcp = SSTOVC(ssp);
359 	u_quad_t tsize = 0;
360 	int isreadonly, doclose, error = 0;
361 
362 	SMBVDEBUG("\n");
363 	if (vap->va_flags != VNOVAL)
364 		return EOPNOTSUPP;
365 	isreadonly = (vp->v_mount->mnt_flag & MNT_RDONLY);
366 	/*
367 	 * Disallow write attempts if the filesystem is mounted read-only.
368 	 */
369   	if ((vap->va_uid != (uid_t)VNOVAL || vap->va_gid != (gid_t)VNOVAL ||
370 	     vap->va_atime.tv_sec != VNOVAL || vap->va_mtime.tv_sec != VNOVAL ||
371 	     vap->va_mode != (mode_t)VNOVAL) && isreadonly)
372 		return EROFS;
373 	smb_makescred(&scred, ap->a_td, ap->a_cred);
374 	if (vap->va_size != VNOVAL) {
375  		switch (vp->v_type) {
376  		    case VDIR:
377  			return EISDIR;
378  		    case VREG:
379 			break;
380  		    default:
381 			return EINVAL;
382   		};
383 		if (isreadonly)
384 			return EROFS;
385 		doclose = 0;
386 		vnode_pager_setsize(vp, (u_long)vap->va_size);
387  		tsize = np->n_size;
388  		np->n_size = vap->va_size;
389 		if (np->n_opencount == 0) {
390 			error = smbfs_smb_open(np, SMB_AM_OPENRW, &scred);
391 			if (error == 0)
392 				doclose = 1;
393 		}
394 		if (error == 0)
395 			error = smbfs_smb_setfsize(np, vap->va_size, &scred);
396 		if (doclose)
397 			smbfs_smb_close(ssp, np->n_fid, NULL, &scred);
398 		if (error) {
399 			np->n_size = tsize;
400 			vnode_pager_setsize(vp, (u_long)tsize);
401 			return error;
402 		}
403   	}
404 	mtime = atime = NULL;
405 	if (vap->va_mtime.tv_sec != VNOVAL)
406 		mtime = &vap->va_mtime;
407 	if (vap->va_atime.tv_sec != VNOVAL)
408 		atime = &vap->va_atime;
409 	if (mtime != atime) {
410 		if (ap->a_cred->cr_uid != VTOSMBFS(vp)->sm_args.uid &&
411 		    (error = suser_cred(ap->a_cred, PRISON_ROOT)) &&
412 		    ((vap->va_vaflags & VA_UTIMES_NULL) == 0 ||
413 		    (error = VOP_ACCESS(vp, VWRITE, ap->a_cred, ap->a_td))))
414 			return (error);
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_td);*/
431 				if (mtime)
432 					np->n_mtime = *mtime;
433 				VOP_CLOSE(vp, FWRITE, 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_td);
464 	np->n_mtime.tv_sec = vap->va_mtime.tv_sec;
465 	return error;
466 }
467 /*
468  * smbfs_read call.
469  */
470 static int
471 smbfs_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 
488 static int
489 smbfs_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  */
512 static int
513 smbfs_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_td)))
540 		return error;
541 	smb_makescred(&scred, cnp->cn_td, 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 
558 static int
559 smbfs_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 || vp->v_usecount != 1)
575 		return EPERM;
576 	smb_makescred(&scred, cnp->cn_td, 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  */
585 static int
586 smbfs_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 && tvp->v_usecount > 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 		error = EINVAL;
624 		goto out;
625 	}
626 	smb_makescred(&scred, tcnp->cn_td, tcnp->cn_cred);
627 	/*
628 	 * It seems that Samba doesn't implement SMB_COM_MOVE call...
629 	 */
630 #ifdef notnow
631 	if (SMB_DIALECT(SSTOCN(smp->sm_share)) >= SMB_DIALECT_LANMAN1_0) {
632 		error = smbfs_smb_move(VTOSMB(fvp), VTOSMB(tdvp),
633 		    tcnp->cn_nameptr, tcnp->cn_namelen, flags, &scred);
634 	} else
635 #endif
636 	{
637 		/*
638 		 * We have to do the work atomicaly
639 		 */
640 		if (tvp && tvp != fvp) {
641 			error = smbfs_smb_delete(VTOSMB(tvp), &scred);
642 			if (error)
643 				goto out_cacherem;
644 		}
645 		error = smbfs_smb_rename(VTOSMB(fvp), VTOSMB(tdvp),
646 		    tcnp->cn_nameptr, tcnp->cn_namelen, &scred);
647 	}
648 
649 	if (fvp->v_type == VDIR) {
650 		if (tvp != NULL && tvp->v_type == VDIR)
651 			cache_purge(tdvp);
652 		cache_purge(fdvp);
653 	}
654 
655 out_cacherem:
656 	smbfs_attr_cacheremove(fdvp);
657 	smbfs_attr_cacheremove(tdvp);
658 out:
659 	if (tdvp == tvp)
660 		vrele(tdvp);
661 	else
662 		vput(tdvp);
663 	if (tvp)
664 		vput(tvp);
665 	vrele(fdvp);
666 	vrele(fvp);
667 #ifdef possible_mistake
668 	vgone(fvp);
669 	if (tvp)
670 		vgone(tvp);
671 #endif
672 	return error;
673 }
674 
675 /*
676  * somtime it will come true...
677  */
678 static int
679 smbfs_link(ap)
680 	struct vop_link_args /* {
681 		struct vnode *a_tdvp;
682 		struct vnode *a_vp;
683 		struct componentname *a_cnp;
684 	} */ *ap;
685 {
686 	return EOPNOTSUPP;
687 }
688 
689 /*
690  * smbfs_symlink link create call.
691  * Sometime it will be functional...
692  */
693 static int
694 smbfs_symlink(ap)
695 	struct vop_symlink_args /* {
696 		struct vnode *a_dvp;
697 		struct vnode **a_vpp;
698 		struct componentname *a_cnp;
699 		struct vattr *a_vap;
700 		char *a_target;
701 	} */ *ap;
702 {
703 	return EOPNOTSUPP;
704 }
705 
706 static int
707 smbfs_mknod(ap)
708 	struct vop_mknod_args /* {
709 	} */ *ap;
710 {
711 	return EOPNOTSUPP;
712 }
713 
714 static int
715 smbfs_mkdir(ap)
716 	struct vop_mkdir_args /* {
717 		struct vnode *a_dvp;
718 		struct vnode **a_vpp;
719 		struct componentname *a_cnp;
720 		struct vattr *a_vap;
721 	} */ *ap;
722 {
723 	struct vnode *dvp = ap->a_dvp;
724 /*	struct vattr *vap = ap->a_vap;*/
725 	struct vnode *vp;
726 	struct componentname *cnp = ap->a_cnp;
727 	struct smbnode *dnp = VTOSMB(dvp);
728 	struct vattr vattr;
729 	struct smb_cred scred;
730 	struct smbfattr fattr;
731 	char *name = cnp->cn_nameptr;
732 	int len = cnp->cn_namelen;
733 	int error;
734 
735 	if ((error = VOP_GETATTR(dvp, &vattr, cnp->cn_td))) {
736 		return error;
737 	}
738 	if ((name[0] == '.') && ((len == 1) || ((len == 2) && (name[1] == '.'))))
739 		return EEXIST;
740 	smb_makescred(&scred, cnp->cn_td, cnp->cn_cred);
741 	error = smbfs_smb_mkdir(dnp, name, len, &scred);
742 	if (error)
743 		return error;
744 	error = smbfs_smb_lookup(dnp, name, len, &fattr, &scred);
745 	if (error)
746 		return error;
747 	error = smbfs_nget(VTOVFS(dvp), dvp, name, len, &fattr, &vp);
748 	if (error)
749 		return error;
750 	*ap->a_vpp = vp;
751 	return 0;
752 }
753 
754 /*
755  * smbfs_remove directory call
756  */
757 static int
758 smbfs_rmdir(ap)
759 	struct vop_rmdir_args /* {
760 		struct vnode *a_dvp;
761 		struct vnode *a_vp;
762 		struct componentname *a_cnp;
763 	} */ *ap;
764 {
765 	struct vnode *vp = ap->a_vp;
766 	struct vnode *dvp = ap->a_dvp;
767 	struct componentname *cnp = ap->a_cnp;
768 /*	struct smbmount *smp = VTOSMBFS(vp);*/
769 	struct smbnode *dnp = VTOSMB(dvp);
770 	struct smbnode *np = VTOSMB(vp);
771 	struct smb_cred scred;
772 	int error;
773 
774 	if (dvp == vp)
775 		return EINVAL;
776 
777 	smb_makescred(&scred, cnp->cn_td, cnp->cn_cred);
778 	error = smbfs_smb_rmdir(np, &scred);
779 	dnp->n_flag |= NMODIFIED;
780 	smbfs_attr_cacheremove(dvp);
781 /*	cache_purge(dvp);*/
782 	cache_purge(vp);
783 	return error;
784 }
785 
786 /*
787  * smbfs_readdir call
788  */
789 static int
790 smbfs_readdir(ap)
791 	struct vop_readdir_args /* {
792 		struct vnode *a_vp;
793 		struct uio *a_uio;
794 		struct ucred *a_cred;
795 		int *a_eofflag;
796 		u_long *a_cookies;
797 		int a_ncookies;
798 	} */ *ap;
799 {
800 	struct vnode *vp = ap->a_vp;
801 	struct uio *uio = ap->a_uio;
802 	int error;
803 
804 	if (vp->v_type != VDIR)
805 		return (EPERM);
806 #ifdef notnow
807 	if (ap->a_ncookies) {
808 		printf("smbfs_readdir: no support for cookies now...");
809 		return (EOPNOTSUPP);
810 	}
811 #endif
812 	error = smbfs_readvnode(vp, uio, ap->a_cred);
813 	return error;
814 }
815 
816 /* ARGSUSED */
817 static int
818 smbfs_fsync(ap)
819 	struct vop_fsync_args /* {
820 		struct vnodeop_desc *a_desc;
821 		struct vnode *a_vp;
822 		struct ucred *a_cred;
823 		int  a_waitfor;
824 		struct thread *a_td;
825 	} */ *ap;
826 {
827 /*	return (smb_flush(ap->a_vp, ap->a_cred, ap->a_waitfor, ap->a_td, 1));*/
828     return (0);
829 }
830 
831 static
832 int smbfs_print (ap)
833 	struct vop_print_args /* {
834 	struct vnode *a_vp;
835 	} */ *ap;
836 {
837 	struct vnode *vp = ap->a_vp;
838 	struct smbnode *np = VTOSMB(vp);
839 
840 	if (np == NULL) {
841 		printf("no smbnode data\n");
842 		return (0);
843 	}
844 	printf("tag VT_SMBFS, name = %s, parent = %p, opencount = %d",
845 	    np->n_name, np->n_parent ? np->n_parent : NULL,
846 	    np->n_opencount);
847 	lockmgr_printinfo(&np->n_lock);
848 	printf("\n");
849 	return (0);
850 }
851 
852 static int
853 smbfs_pathconf (ap)
854 	struct vop_pathconf_args  /* {
855 	struct vnode *vp;
856 	int name;
857 	register_t *retval;
858 	} */ *ap;
859 {
860 	struct smbmount *smp = VFSTOSMBFS(VTOVFS(ap->a_vp));
861 	struct smb_vc *vcp = SSTOVC(smp->sm_share);
862 	register_t *retval = ap->a_retval;
863 	int error = 0;
864 
865 	switch (ap->a_name) {
866 	    case _PC_LINK_MAX:
867 		*retval = 0;
868 		break;
869 	    case _PC_NAME_MAX:
870 		*retval = (vcp->vc_hflags2 & SMB_FLAGS2_KNOWS_LONG_NAMES) ? 255 : 12;
871 		break;
872 	    case _PC_PATH_MAX:
873 		*retval = 800;	/* XXX: a correct one ? */
874 		break;
875 	    default:
876 		error = EINVAL;
877 	}
878 	return error;
879 }
880 
881 static int
882 smbfs_strategy (ap)
883 	struct vop_strategy_args /* {
884 	struct buf *a_bp
885 	} */ *ap;
886 {
887 	struct buf *bp=ap->a_bp;
888 	struct thread *td = NULL;
889 	int error = 0;
890 
891 	SMBVDEBUG("\n");
892 	if (bp->b_flags & B_PHYS)
893 		panic("smbfs physio");
894 	if ((bp->b_flags & B_ASYNC) == 0)
895 		td = curthread;		/* XXX */
896 
897 	if ((bp->b_flags & B_ASYNC) == 0 )
898 		error = smbfs_doio(bp, proc0.p_ucred, td);
899 	return error;
900 }
901 
902 static int
903 smbfs_bmap(ap)
904 	struct vop_bmap_args /* {
905 		struct vnode *a_vp;
906 		daddr_t  a_bn;
907 		struct vnode **a_vpp;
908 		daddr_t *a_bnp;
909 		int *a_runp;
910 		int *a_runb;
911 	} */ *ap;
912 {
913 	struct vnode *vp = ap->a_vp;
914 
915 	if (ap->a_vpp != NULL)
916 		*ap->a_vpp = vp;
917 	if (ap->a_bnp != NULL)
918 		*ap->a_bnp = ap->a_bn * btodb(vp->v_mount->mnt_stat.f_iosize);
919 	if (ap->a_runp != NULL)
920 		*ap->a_runp = 0;
921 	if (ap->a_runb != NULL)
922 		*ap->a_runb = 0;
923 	return (0);
924 }
925 
926 int
927 smbfs_ioctl(ap)
928 	struct vop_ioctl_args /* {
929 		struct vnode *a_vp;
930 		u_long a_command;
931 		caddr_t a_data;
932 		int fflag;
933 		struct ucred *cred;
934 		struct proc *p;
935 	} */ *ap;
936 {
937 	return EINVAL;
938 }
939 
940 static char smbfs_atl[] = "rhsvda";
941 static int
942 smbfs_getextattr(struct vop_getextattr_args *ap)
943 /* {
944         IN struct vnode *a_vp;
945         IN char *a_name;
946         INOUT struct uio *a_uio;
947         IN struct ucred *a_cred;
948         IN struct thread *a_td;
949 };
950 */
951 {
952 	struct vnode *vp = ap->a_vp;
953 	struct thread *td = ap->a_td;
954 	struct ucred *cred = ap->a_cred;
955 	struct uio *uio = ap->a_uio;
956 	const char *name = ap->a_name;
957 	struct smbnode *np = VTOSMB(vp);
958 	struct vattr vattr;
959 	char buf[10];
960 	int i, attr, error;
961 
962 	error = VOP_ACCESS(vp, VREAD, cred, td);
963 	if (error)
964 		return error;
965 	error = VOP_GETATTR(vp, &vattr, td);
966 	if (error)
967 		return error;
968 	if (strcmp(name, "dosattr") == 0) {
969 		attr = np->n_dosattr;
970 		for (i = 0; i < 6; i++, attr >>= 1)
971 			buf[i] = (attr & 1) ? smbfs_atl[i] : '-';
972 		buf[i] = 0;
973 		error = uiomove(buf, i, uio);
974 
975 	} else
976 		error = EINVAL;
977 	return error;
978 }
979 
980 /*
981  * Since we expected to support F_GETLK (and SMB protocol has no such function),
982  * it is necessary to use lf_advlock(). It would be nice if this function had
983  * a callback mechanism because it will help to improve a level of consistency.
984  */
985 int
986 smbfs_advlock(ap)
987 	struct vop_advlock_args /* {
988 		struct vnode *a_vp;
989 		caddr_t  a_id;
990 		int  a_op;
991 		struct flock *a_fl;
992 		int  a_flags;
993 	} */ *ap;
994 {
995 	struct vnode *vp = ap->a_vp;
996 	struct smbnode *np = VTOSMB(vp);
997 	struct flock *fl = ap->a_fl;
998 	caddr_t id = (caddr_t)1 /* ap->a_id */;
999 /*	int flags = ap->a_flags;*/
1000 	struct thread *td = curthread;		/* XXX */
1001 	struct smb_cred scred;
1002 	off_t start, end, size;
1003 	int error, lkop;
1004 
1005 	if (vp->v_type == VDIR) {
1006 		/*
1007 		 * SMB protocol have no support for directory locking.
1008 		 * Although locks can be processed on local machine, I don't
1009 		 * think that this is a good idea, because some programs
1010 		 * can work wrong assuming directory is locked. So, we just
1011 		 * return 'operation not supported
1012 		 */
1013 		 return EOPNOTSUPP;
1014 	}
1015 	size = np->n_size;
1016 	switch (fl->l_whence) {
1017 	    case SEEK_SET:
1018 	    case SEEK_CUR:
1019 		start = fl->l_start;
1020 		break;
1021 	    case SEEK_END:
1022 		start = fl->l_start + size;
1023 	    default:
1024 		return EINVAL;
1025 	}
1026 	if (start < 0)
1027 		return EINVAL;
1028 	if (fl->l_len == 0)
1029 		end = -1;
1030 	else {
1031 		end = start + fl->l_len - 1;
1032 		if (end < start)
1033 			return EINVAL;
1034 	}
1035 	smb_makescred(&scred, td, td->td_proc ? td->td_proc->p_ucred : NULL);
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 
1074 static int
1075 smbfs_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 /*
1110  * Things go even weird without fixed inode numbers...
1111  */
1112 int
1113 smbfs_lookup(ap)
1114 	struct vop_lookup_args /* {
1115 		struct vnodeop_desc *a_desc;
1116 		struct vnode *a_dvp;
1117 		struct vnode **a_vpp;
1118 		struct componentname *a_cnp;
1119 	} */ *ap;
1120 {
1121 	struct componentname *cnp = ap->a_cnp;
1122 	struct thread *td = cnp->cn_td;
1123 	struct vnode *dvp = ap->a_dvp;
1124 	struct vnode **vpp = ap->a_vpp;
1125 	struct vnode *vp;
1126 	struct smbmount *smp;
1127 	struct mount *mp = dvp->v_mount;
1128 	struct smbnode *dnp;
1129 	struct smbfattr fattr, *fap;
1130 	struct smb_cred scred;
1131 	char *name = cnp->cn_nameptr;
1132 	int flags = cnp->cn_flags;
1133 	int nameiop = cnp->cn_nameiop;
1134 	int nmlen = cnp->cn_namelen;
1135 	int lockparent, wantparent, error, islastcn, isdot;
1136 
1137 	SMBVDEBUG("\n");
1138 	cnp->cn_flags &= ~PDIRUNLOCK;
1139 	if (dvp->v_type != VDIR)
1140 		return ENOTDIR;
1141 	if ((flags & ISDOTDOT) && (dvp->v_flag & VROOT)) {
1142 		SMBFSERR("invalid '..'\n");
1143 		return EIO;
1144 	}
1145 #ifdef SMB_VNODE_DEBUG
1146 	{
1147 		char *cp, c;
1148 
1149 		cp = name + nmlen;
1150 		c = *cp;
1151 		*cp = 0;
1152 		SMBVDEBUG("%d '%s' in '%s' id=d\n", nameiop, name,
1153 			VTOSMB(dvp)->n_name);
1154 		*cp = c;
1155 	}
1156 #endif
1157 	islastcn = flags & ISLASTCN;
1158 	if (islastcn && (mp->mnt_flag & MNT_RDONLY) && (nameiop != LOOKUP))
1159 		return EROFS;
1160 	if ((error = VOP_ACCESS(dvp, VEXEC, cnp->cn_cred, td)) != 0)
1161 		return error;
1162 	lockparent = flags & LOCKPARENT;
1163 	wantparent = flags & (LOCKPARENT|WANTPARENT);
1164 	smp = VFSTOSMBFS(mp);
1165 	dnp = VTOSMB(dvp);
1166 	isdot = (nmlen == 1 && name[0] == '.');
1167 
1168 	error = smbfs_pathcheck(smp, cnp->cn_nameptr, cnp->cn_namelen, nameiop);
1169 
1170 	if (error)
1171 		return ENOENT;
1172 
1173 	error = cache_lookup(dvp, vpp, cnp);
1174 	SMBVDEBUG("cache_lookup returned %d\n", error);
1175 	if (error > 0)
1176 		return error;
1177 	if (error) {		/* name was found */
1178 		struct vattr vattr;
1179 		int vpid;
1180 
1181 		vp = *vpp;
1182 		vpid = vp->v_id;
1183 		if (dvp == vp) {	/* lookup on current */
1184 			vref(vp);
1185 			error = 0;
1186 			SMBVDEBUG("cached '.'\n");
1187 		} else if (flags & ISDOTDOT) {
1188 			VOP_UNLOCK(dvp, 0, td);	/* unlock parent */
1189 			cnp->cn_flags |= PDIRUNLOCK;
1190 			error = vget(vp, LK_EXCLUSIVE, td);
1191 			if (!error && lockparent && islastcn) {
1192 				error = vn_lock(dvp, LK_EXCLUSIVE, td);
1193 				if (error == 0)
1194 					cnp->cn_flags &= ~PDIRUNLOCK;
1195 			}
1196 		} else {
1197 			error = vget(vp, LK_EXCLUSIVE, td);
1198 			if (!lockparent || error || !islastcn) {
1199 				VOP_UNLOCK(dvp, 0, td);
1200 				cnp->cn_flags |= PDIRUNLOCK;
1201 			}
1202 		}
1203 		if (!error) {
1204 			if (vpid == vp->v_id) {
1205 			   if (!VOP_GETATTR(vp, &vattr, td)
1206 			/*    && vattr.va_ctime.tv_sec == VTOSMB(vp)->n_ctime*/) {
1207 				if (nameiop != LOOKUP && islastcn)
1208 					cnp->cn_flags |= SAVENAME;
1209 				SMBVDEBUG("use cached vnode\n");
1210 				return (0);
1211 			   }
1212 			   cache_purge(vp);
1213 			}
1214 			vput(vp);
1215 			if (lockparent && dvp != vp && islastcn)
1216 				VOP_UNLOCK(dvp, 0, td);
1217 		}
1218 		error = vn_lock(dvp, LK_EXCLUSIVE, td);
1219 		*vpp = NULLVP;
1220 		if (error) {
1221 			cnp->cn_flags |= PDIRUNLOCK;
1222 			return (error);
1223 		}
1224 		cnp->cn_flags &= ~PDIRUNLOCK;
1225 	}
1226 	/*
1227 	 * entry is not in the cache or has been expired
1228 	 */
1229 	error = 0;
1230 	*vpp = NULLVP;
1231 	smb_makescred(&scred, td, cnp->cn_cred);
1232 	fap = &fattr;
1233 	if (flags & ISDOTDOT) {
1234 		error = smbfs_smb_lookup(VTOSMB(dnp->n_parent), NULL, 0, fap,
1235 		    &scred);
1236 		SMBVDEBUG("result of dotdot lookup: %d\n", error);
1237 	} else {
1238 		fap = &fattr;
1239 		error = smbfs_smb_lookup(dnp, name, nmlen, fap, &scred);
1240 /*		if (cnp->cn_namelen == 1 && cnp->cn_nameptr[0] == '.')*/
1241 		SMBVDEBUG("result of smbfs_smb_lookup: %d\n", error);
1242 	}
1243 	if (error && error != ENOENT)
1244 		return error;
1245 	if (error) {			/* entry not found */
1246 		/*
1247 		 * Handle RENAME or CREATE case...
1248 		 */
1249 		if ((nameiop == CREATE || nameiop == RENAME) && wantparent && islastcn) {
1250 			error = VOP_ACCESS(dvp, VWRITE, cnp->cn_cred, td);
1251 			if (error)
1252 				return error;
1253 			cnp->cn_flags |= SAVENAME;
1254 			if (!lockparent) {
1255 				VOP_UNLOCK(dvp, 0, td);
1256 				cnp->cn_flags |= PDIRUNLOCK;
1257 			}
1258 			return (EJUSTRETURN);
1259 		}
1260 		return ENOENT;
1261 	}/* else {
1262 		SMBVDEBUG("Found entry %s with id=%d\n", fap->entryName, fap->dirEntNum);
1263 	}*/
1264 	/*
1265 	 * handle DELETE case ...
1266 	 */
1267 	if (nameiop == DELETE && islastcn) { 	/* delete last component */
1268 		error = VOP_ACCESS(dvp, VWRITE, cnp->cn_cred, td);
1269 		if (error)
1270 			return error;
1271 		if (isdot) {
1272 			VREF(dvp);
1273 			*vpp = dvp;
1274 			return 0;
1275 		}
1276 		error = smbfs_nget(mp, dvp, name, nmlen, fap, &vp);
1277 		if (error)
1278 			return error;
1279 		*vpp = vp;
1280 		cnp->cn_flags |= SAVENAME;
1281 		if (!lockparent) {
1282 			VOP_UNLOCK(dvp, 0, td);
1283 			cnp->cn_flags |= PDIRUNLOCK;
1284 		}
1285 		return 0;
1286 	}
1287 	if (nameiop == RENAME && islastcn && wantparent) {
1288 		error = VOP_ACCESS(dvp, VWRITE, cnp->cn_cred, td);
1289 		if (error)
1290 			return error;
1291 		if (isdot)
1292 			return EISDIR;
1293 		error = smbfs_nget(mp, dvp, name, nmlen, fap, &vp);
1294 		if (error)
1295 			return error;
1296 		*vpp = vp;
1297 		cnp->cn_flags |= SAVENAME;
1298 		if (!lockparent) {
1299 			VOP_UNLOCK(dvp, 0, td);
1300 			cnp->cn_flags |= PDIRUNLOCK;
1301 		}
1302 		return 0;
1303 	}
1304 	if (flags & ISDOTDOT) {
1305 		VOP_UNLOCK(dvp, 0, td);
1306 		error = smbfs_nget(mp, dvp, name, nmlen, NULL, &vp);
1307 		if (error) {
1308 			vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY, td);
1309 			return error;
1310 		}
1311 		if (lockparent && islastcn) {
1312 			error = vn_lock(dvp, LK_EXCLUSIVE, td);
1313 			if (error) {
1314 				cnp->cn_flags |= PDIRUNLOCK;
1315 				vput(vp);
1316 				return error;
1317 			}
1318 		}
1319 		*vpp = vp;
1320 	} else if (isdot) {
1321 		vref(dvp);
1322 		*vpp = dvp;
1323 	} else {
1324 		error = smbfs_nget(mp, dvp, name, nmlen, fap, &vp);
1325 		if (error)
1326 			return error;
1327 		*vpp = vp;
1328 		SMBVDEBUG("lookup: getnewvp!\n");
1329 		if (!lockparent || !islastcn) {
1330 			VOP_UNLOCK(dvp, 0, td);
1331 			cnp->cn_flags |= PDIRUNLOCK;
1332 		}
1333 	}
1334 	if ((cnp->cn_flags & MAKEENTRY)/* && !islastcn*/) {
1335 /*		VTOSMB(*vpp)->n_ctime = VTOSMB(*vpp)->n_vattr.va_ctime.tv_sec;*/
1336 		cache_enter(dvp, *vpp, cnp);
1337 	}
1338 	return 0;
1339 }
1340