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