smbfs_vnops.c revision 140223
1/*-
2 * Copyright (c) 2000-2001 Boris Popov
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 *    must display the following acknowledgement:
15 *    This product includes software developed by Boris Popov.
16 * 4. Neither the name of the author nor the names of any co-contributors
17 *    may be used to endorse or promote products derived from this software
18 *    without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 *
32 * $FreeBSD: head/sys/fs/smbfs/smbfs_vnops.c 140223 2005-01-14 08:52:55Z phk $
33 */
34#include <sys/param.h>
35#include <sys/systm.h>
36#include <sys/namei.h>
37#include <sys/kernel.h>
38#include <sys/proc.h>
39#include <sys/bio.h>
40#include <sys/buf.h>
41#include <sys/fcntl.h>
42#include <sys/mount.h>
43#include <sys/unistd.h>
44#include <sys/vnode.h>
45#include <sys/limits.h>
46#include <sys/lockf.h>
47
48#include <vm/vm.h>
49#include <vm/vm_extern.h>
50
51
52#include <netsmb/smb.h>
53#include <netsmb/smb_conn.h>
54#include <netsmb/smb_subr.h>
55
56#include <fs/smbfs/smbfs.h>
57#include <fs/smbfs/smbfs_node.h>
58#include <fs/smbfs/smbfs_subr.h>
59
60/*
61 * Prototypes for SMBFS vnode operations
62 */
63static vop_create_t	smbfs_create;
64static vop_mknod_t	smbfs_mknod;
65static vop_open_t	smbfs_open;
66static vop_close_t	smbfs_close;
67static vop_access_t	smbfs_access;
68static vop_getattr_t	smbfs_getattr;
69static vop_setattr_t	smbfs_setattr;
70static vop_read_t	smbfs_read;
71static vop_write_t	smbfs_write;
72static vop_fsync_t	smbfs_fsync;
73static vop_remove_t	smbfs_remove;
74static vop_link_t	smbfs_link;
75static vop_lookup_t	smbfs_lookup;
76static vop_rename_t	smbfs_rename;
77static vop_mkdir_t	smbfs_mkdir;
78static vop_rmdir_t	smbfs_rmdir;
79static vop_symlink_t	smbfs_symlink;
80static vop_readdir_t	smbfs_readdir;
81static vop_strategy_t	smbfs_strategy;
82static vop_print_t	smbfs_print;
83static vop_pathconf_t	smbfs_pathconf;
84static vop_advlock_t	smbfs_advlock;
85static vop_getextattr_t	smbfs_getextattr;
86
87struct vop_vector smbfs_vnodeops = {
88	.vop_default =		&default_vnodeops,
89
90	.vop_access =		smbfs_access,
91	.vop_advlock =		smbfs_advlock,
92	.vop_close =		smbfs_close,
93	.vop_create =		smbfs_create,
94	.vop_fsync =		smbfs_fsync,
95	.vop_getattr =		smbfs_getattr,
96	.vop_getextattr = 	smbfs_getextattr,
97	.vop_getpages =		smbfs_getpages,
98	.vop_inactive =		smbfs_inactive,
99	.vop_ioctl =		smbfs_ioctl,
100	.vop_link =		smbfs_link,
101	.vop_lookup =		smbfs_lookup,
102	.vop_mkdir =		smbfs_mkdir,
103	.vop_mknod =		smbfs_mknod,
104	.vop_open =		smbfs_open,
105	.vop_pathconf =		smbfs_pathconf,
106	.vop_print =		smbfs_print,
107	.vop_putpages =		smbfs_putpages,
108	.vop_read =		smbfs_read,
109	.vop_readdir =		smbfs_readdir,
110	.vop_reclaim =		smbfs_reclaim,
111	.vop_remove =		smbfs_remove,
112	.vop_rename =		smbfs_rename,
113	.vop_rmdir =		smbfs_rmdir,
114	.vop_setattr =		smbfs_setattr,
115/*	.vop_setextattr =	smbfs_setextattr,*/
116	.vop_strategy =		smbfs_strategy,
117	.vop_symlink =		smbfs_symlink,
118	.vop_write =		smbfs_write,
119};
120
121static int
122smbfs_access(ap)
123	struct vop_access_args /* {
124		struct vnode *a_vp;
125		int  a_mode;
126		struct ucred *a_cred;
127		struct thread *a_td;
128	} */ *ap;
129{
130	struct vnode *vp = ap->a_vp;
131	mode_t mode = ap->a_mode;
132	mode_t mpmode;
133	struct smbmount *smp = VTOSMBFS(vp);
134
135	SMBVDEBUG("\n");
136	if ((mode & VWRITE) && (vp->v_mount->mnt_flag & MNT_RDONLY)) {
137		switch (vp->v_type) {
138		    case VREG: case VDIR: case VLNK:
139			return EROFS;
140		    default:
141			break;
142		}
143	}
144	mpmode = vp->v_type == VREG ? smp->sm_file_mode : smp->sm_dir_mode;
145	return (vaccess(vp->v_type, mpmode, smp->sm_uid,
146	    smp->sm_gid, ap->a_mode, ap->a_cred, NULL));
147}
148
149/* ARGSUSED */
150static int
151smbfs_open(ap)
152	struct vop_open_args /* {
153		struct vnode *a_vp;
154		int  a_mode;
155		struct ucred *a_cred;
156		struct thread *a_td;
157	} */ *ap;
158{
159	struct vnode *vp = ap->a_vp;
160	struct smbnode *np = VTOSMB(vp);
161	struct smb_cred scred;
162	struct vattr vattr;
163	int mode = ap->a_mode;
164	int error, accmode;
165
166	SMBVDEBUG("%s,%d\n", np->n_name, (np->n_flag & NOPEN) != 0);
167	if (vp->v_type != VREG && vp->v_type != VDIR) {
168		SMBFSERR("open eacces vtype=%d\n", vp->v_type);
169		return EACCES;
170	}
171	if (vp->v_type == VDIR) {
172		np->n_flag |= NOPEN;
173		return 0;
174	}
175	if (np->n_flag & NMODIFIED) {
176		if ((error = smbfs_vinvalbuf(vp, ap->a_td)) == EINTR)
177			return error;
178		smbfs_attr_cacheremove(vp);
179		error = VOP_GETATTR(vp, &vattr, ap->a_cred, ap->a_td);
180		if (error)
181			return error;
182		np->n_mtime.tv_sec = vattr.va_mtime.tv_sec;
183	} else {
184		error = VOP_GETATTR(vp, &vattr, ap->a_cred, ap->a_td);
185		if (error)
186			return error;
187		if (np->n_mtime.tv_sec != vattr.va_mtime.tv_sec) {
188			error = smbfs_vinvalbuf(vp, ap->a_td);
189			if (error == EINTR)
190				return error;
191			np->n_mtime.tv_sec = vattr.va_mtime.tv_sec;
192		}
193	}
194	if ((np->n_flag & NOPEN) != 0)
195		return 0;
196	/*
197	 * Use DENYNONE to give unixy semantics of permitting
198	 * everything not forbidden by permissions.  Ie denial
199	 * is up to server with clients/openers needing to use
200	 * advisory locks for further control.
201	 */
202	accmode = SMB_SM_DENYNONE|SMB_AM_OPENREAD;
203	if ((vp->v_mount->mnt_flag & MNT_RDONLY) == 0)
204		accmode = SMB_SM_DENYNONE|SMB_AM_OPENRW;
205	smb_makescred(&scred, ap->a_td, ap->a_cred);
206	error = smbfs_smb_open(np, accmode, &scred);
207	if (error) {
208		if (mode & FWRITE)
209			return EACCES;
210		else if ((vp->v_mount->mnt_flag & MNT_RDONLY) == 0) {
211			accmode = SMB_SM_DENYNONE|SMB_AM_OPENREAD;
212			error = smbfs_smb_open(np, accmode, &scred);
213		}
214	}
215	if (error == 0)
216		np->n_flag |= NOPEN;
217	smbfs_attr_cacheremove(vp);
218	return error;
219}
220
221/*
222 * XXX: VOP_CLOSE() usually called without lock held which is suck. Here we
223 * do some heruistic to determine if vnode should be locked.
224 */
225static int
226smbfs_close(ap)
227	struct vop_close_args /* {
228		struct vnodeop_desc *a_desc;
229		struct vnode *a_vp;
230		int  a_fflag;
231		struct ucred *a_cred;
232		struct thread *a_td;
233	} */ *ap;
234{
235	struct vnode *vp = ap->a_vp;
236	struct thread *td = ap->a_td;
237	struct smbnode *np = VTOSMB(vp);
238	struct smb_cred scred;
239	int dolock;
240
241	VI_LOCK(vp);
242	dolock = (vp->v_iflag & VI_XLOCK) == 0;
243	if (dolock)
244		vn_lock(vp, LK_EXCLUSIVE | LK_RETRY | LK_INTERLOCK, td);
245	else
246		VI_UNLOCK(vp);
247	if (vp->v_type == VDIR && (np->n_flag & NOPEN) != 0 &&
248	    np->n_dirseq != NULL) {
249		smb_makescred(&scred, td, ap->a_cred);
250		smbfs_findclose(np->n_dirseq, &scred);
251		np->n_dirseq = NULL;
252	}
253	if (dolock)
254		VOP_UNLOCK(vp, 0, td);
255	return 0;
256}
257
258/*
259 * smbfs_getattr call from vfs.
260 */
261static int
262smbfs_getattr(ap)
263	struct vop_getattr_args /* {
264		struct vnode *a_vp;
265		struct vattr *a_vap;
266		struct ucred *a_cred;
267		struct thread *a_td;
268	} */ *ap;
269{
270	struct vnode *vp = ap->a_vp;
271	struct smbnode *np = VTOSMB(vp);
272	struct vattr *va=ap->a_vap;
273	struct smbfattr fattr;
274	struct smb_cred scred;
275	u_quad_t oldsize;
276	int error;
277
278	SMBVDEBUG("%lx: '%s' %d\n", (long)vp, np->n_name, (vp->v_vflag & VV_ROOT) != 0);
279	error = smbfs_attr_cachelookup(vp, va);
280	if (!error)
281		return 0;
282	SMBVDEBUG("not in the cache\n");
283	smb_makescred(&scred, ap->a_td, ap->a_cred);
284	oldsize = np->n_size;
285	error = smbfs_smb_lookup(np, NULL, 0, &fattr, &scred);
286	if (error) {
287		SMBVDEBUG("error %d\n", error);
288		return error;
289	}
290	smbfs_attr_cacheenter(vp, &fattr);
291	smbfs_attr_cachelookup(vp, va);
292	if (np->n_flag & NOPEN)
293		np->n_size = oldsize;
294	return 0;
295}
296
297static int
298smbfs_setattr(ap)
299	struct vop_setattr_args /* {
300		struct vnode *a_vp;
301		struct vattr *a_vap;
302		struct ucred *a_cred;
303		struct thread *a_td;
304	} */ *ap;
305{
306	struct vnode *vp = ap->a_vp;
307	struct smbnode *np = VTOSMB(vp);
308	struct vattr *vap = ap->a_vap;
309	struct timespec *mtime, *atime;
310	struct smb_cred scred;
311	struct smb_share *ssp = np->n_mount->sm_share;
312	struct smb_vc *vcp = SSTOVC(ssp);
313	u_quad_t tsize = 0;
314	int isreadonly, doclose, error = 0;
315
316	SMBVDEBUG("\n");
317	if (vap->va_flags != VNOVAL)
318		return EOPNOTSUPP;
319	isreadonly = (vp->v_mount->mnt_flag & MNT_RDONLY);
320	/*
321	 * Disallow write attempts if the filesystem is mounted read-only.
322	 */
323  	if ((vap->va_uid != (uid_t)VNOVAL || vap->va_gid != (gid_t)VNOVAL ||
324	     vap->va_atime.tv_sec != VNOVAL || vap->va_mtime.tv_sec != VNOVAL ||
325	     vap->va_mode != (mode_t)VNOVAL) && isreadonly)
326		return EROFS;
327	smb_makescred(&scred, ap->a_td, ap->a_cred);
328	if (vap->va_size != VNOVAL) {
329 		switch (vp->v_type) {
330 		    case VDIR:
331 			return EISDIR;
332 		    case VREG:
333			break;
334 		    default:
335			return EINVAL;
336  		};
337		if (isreadonly)
338			return EROFS;
339		doclose = 0;
340		vnode_pager_setsize(vp, (u_long)vap->va_size);
341 		tsize = np->n_size;
342 		np->n_size = vap->va_size;
343		if ((np->n_flag & NOPEN) == 0) {
344			error = smbfs_smb_open(np,
345					       SMB_SM_DENYNONE|SMB_AM_OPENRW,
346					       &scred);
347			if (error == 0)
348				doclose = 1;
349		}
350		if (error == 0)
351			error = smbfs_smb_setfsize(np, vap->va_size, &scred);
352		if (doclose)
353			smbfs_smb_close(ssp, np->n_fid, NULL, &scred);
354		if (error) {
355			np->n_size = tsize;
356			vnode_pager_setsize(vp, (u_long)tsize);
357			return error;
358		}
359  	}
360	mtime = atime = NULL;
361	if (vap->va_mtime.tv_sec != VNOVAL)
362		mtime = &vap->va_mtime;
363	if (vap->va_atime.tv_sec != VNOVAL)
364		atime = &vap->va_atime;
365	if (mtime != atime) {
366		if (ap->a_cred->cr_uid != VTOSMBFS(vp)->sm_uid &&
367		    (error = suser_cred(ap->a_cred, SUSER_ALLOWJAIL)) &&
368		    ((vap->va_vaflags & VA_UTIMES_NULL) == 0 ||
369		    (error = VOP_ACCESS(vp, VWRITE, ap->a_cred, ap->a_td))))
370			return (error);
371#if 0
372		if (mtime == NULL)
373			mtime = &np->n_mtime;
374		if (atime == NULL)
375			atime = &np->n_atime;
376#endif
377		/*
378		 * If file is opened, then we can use handle based calls.
379		 * If not, use path based ones.
380		 */
381		if ((np->n_flag & NOPEN) == 0) {
382			if (vcp->vc_flags & SMBV_WIN95) {
383				error = VOP_OPEN(vp, FWRITE, ap->a_cred, ap->a_td, -1);
384				if (!error) {
385/*				error = smbfs_smb_setfattrNT(np, 0, mtime, atime, &scred);
386				VOP_GETATTR(vp, &vattr, ap->a_cred, ap->a_td);*/
387				if (mtime)
388					np->n_mtime = *mtime;
389				VOP_CLOSE(vp, FWRITE, ap->a_cred, ap->a_td);
390				}
391			} else if ((vcp->vc_sopt.sv_caps & SMB_CAP_NT_SMBS)) {
392				error = smbfs_smb_setptime2(np, mtime, atime, 0, &scred);
393/*				error = smbfs_smb_setpattrNT(np, 0, mtime, atime, &scred);*/
394			} else if (SMB_DIALECT(vcp) >= SMB_DIALECT_LANMAN2_0) {
395				error = smbfs_smb_setptime2(np, mtime, atime, 0, &scred);
396			} else {
397				error = smbfs_smb_setpattr(np, 0, mtime, &scred);
398			}
399		} else {
400			if (vcp->vc_sopt.sv_caps & SMB_CAP_NT_SMBS) {
401				error = smbfs_smb_setfattrNT(np, 0, mtime, atime, &scred);
402			} else if (SMB_DIALECT(vcp) >= SMB_DIALECT_LANMAN1_0) {
403				error = smbfs_smb_setftime(np, mtime, atime, &scred);
404			} else {
405				/*
406				 * I have no idea how to handle this for core
407				 * level servers. The possible solution is to
408				 * update mtime after file is closed.
409				 */
410				 SMBERROR("can't update times on an opened file\n");
411			}
412		}
413	}
414	/*
415	 * Invalidate attribute cache in case if server doesn't set
416	 * required attributes.
417	 */
418	smbfs_attr_cacheremove(vp);	/* invalidate cache */
419	VOP_GETATTR(vp, vap, ap->a_cred, ap->a_td);
420	np->n_mtime.tv_sec = vap->va_mtime.tv_sec;
421	return error;
422}
423/*
424 * smbfs_read call.
425 */
426static int
427smbfs_read(ap)
428	struct vop_read_args /* {
429		struct vnode *a_vp;
430		struct uio *a_uio;
431		int  a_ioflag;
432		struct ucred *a_cred;
433	} */ *ap;
434{
435	struct vnode *vp = ap->a_vp;
436	struct uio *uio = ap->a_uio;
437
438	SMBVDEBUG("\n");
439	if (vp->v_type != VREG && vp->v_type != VDIR)
440		return EPERM;
441	return smbfs_readvnode(vp, uio, ap->a_cred);
442}
443
444static int
445smbfs_write(ap)
446	struct vop_write_args /* {
447		struct vnode *a_vp;
448		struct uio *a_uio;
449		int  a_ioflag;
450		struct ucred *a_cred;
451	} */ *ap;
452{
453	struct vnode *vp = ap->a_vp;
454	struct uio *uio = ap->a_uio;
455
456	SMBVDEBUG("%d,ofs=%d,sz=%d\n",vp->v_type, (int)uio->uio_offset, uio->uio_resid);
457	if (vp->v_type != VREG)
458		return (EPERM);
459	return smbfs_writevnode(vp, uio, ap->a_cred,ap->a_ioflag);
460}
461/*
462 * smbfs_create call
463 * 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, cnp->cn_thread)))
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, cnp->cn_thread))) {
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	int error = 0;
849
850	SMBVDEBUG("\n");
851	if (bp->b_flags & B_ASYNC)
852		td = (struct thread *)0;
853	else
854		td = curthread;	/* XXX */
855	if (bp->b_iocmd == BIO_READ)
856		cr = bp->b_rcred;
857	else
858		cr = bp->b_wcred;
859
860	if ((bp->b_flags & B_ASYNC) == 0 )
861		error = smbfs_doio(ap->a_vp, bp, cr, td);
862	return error;
863}
864
865int
866smbfs_ioctl(ap)
867	struct vop_ioctl_args /* {
868		struct vnode *a_vp;
869		u_long a_command;
870		caddr_t a_data;
871		int fflag;
872		struct ucred *cred;
873		struct thread *td;
874	} */ *ap;
875{
876	return ENOTTY;
877}
878
879static char smbfs_atl[] = "rhsvda";
880static int
881smbfs_getextattr(struct vop_getextattr_args *ap)
882/* {
883        IN struct vnode *a_vp;
884        IN char *a_name;
885        INOUT struct uio *a_uio;
886        IN struct ucred *a_cred;
887        IN struct thread *a_td;
888};
889*/
890{
891	struct vnode *vp = ap->a_vp;
892	struct thread *td = ap->a_td;
893	struct ucred *cred = ap->a_cred;
894	struct uio *uio = ap->a_uio;
895	const char *name = ap->a_name;
896	struct smbnode *np = VTOSMB(vp);
897	struct vattr vattr;
898	char buf[10];
899	int i, attr, error;
900
901	error = VOP_ACCESS(vp, VREAD, cred, td);
902	if (error)
903		return error;
904	error = VOP_GETATTR(vp, &vattr, cred, td);
905	if (error)
906		return error;
907	if (strcmp(name, "dosattr") == 0) {
908		attr = np->n_dosattr;
909		for (i = 0; i < 6; i++, attr >>= 1)
910			buf[i] = (attr & 1) ? smbfs_atl[i] : '-';
911		buf[i] = 0;
912		error = uiomove(buf, i, uio);
913
914	} else
915		error = EINVAL;
916	return error;
917}
918
919/*
920 * Since we expected to support F_GETLK (and SMB protocol has no such function),
921 * it is necessary to use lf_advlock(). It would be nice if this function had
922 * a callback mechanism because it will help to improve a level of consistency.
923 */
924int
925smbfs_advlock(ap)
926	struct vop_advlock_args /* {
927		struct vnode *a_vp;
928		caddr_t  a_id;
929		int  a_op;
930		struct flock *a_fl;
931		int  a_flags;
932	} */ *ap;
933{
934	struct vnode *vp = ap->a_vp;
935	struct smbnode *np = VTOSMB(vp);
936	struct flock *fl = ap->a_fl;
937	caddr_t id = (caddr_t)1 /* ap->a_id */;
938/*	int flags = ap->a_flags;*/
939	struct thread *td = curthread;
940	struct smb_cred scred;
941	u_quad_t size;
942	off_t start, end, oadd;
943	int error, lkop;
944
945	if (vp->v_type == VDIR) {
946		/*
947		 * SMB protocol have no support for directory locking.
948		 * Although locks can be processed on local machine, I don't
949		 * think that this is a good idea, because some programs
950		 * can work wrong assuming directory is locked. So, we just
951		 * return 'operation not supported
952		 */
953		 return EOPNOTSUPP;
954	}
955	size = np->n_size;
956	switch (fl->l_whence) {
957
958	case SEEK_SET:
959	case SEEK_CUR:
960		start = fl->l_start;
961		break;
962
963	case SEEK_END:
964		if (size > OFF_MAX ||
965		    (fl->l_start > 0 && size > OFF_MAX - fl->l_start))
966			return EOVERFLOW;
967		start = size + fl->l_start;
968		break;
969
970	default:
971		return EINVAL;
972	}
973	if (start < 0)
974		return EINVAL;
975	if (fl->l_len < 0) {
976		if (start == 0)
977			return EINVAL;
978		end = start - 1;
979		start += fl->l_len;
980		if (start < 0)
981			return EINVAL;
982	} else if (fl->l_len == 0)
983		end = -1;
984	else {
985		oadd = fl->l_len - 1;
986		if (oadd > OFF_MAX - start)
987			return EOVERFLOW;
988		end = start + oadd;
989	}
990	smb_makescred(&scred, td, td->td_ucred);
991	switch (ap->a_op) {
992	    case F_SETLK:
993		switch (fl->l_type) {
994		    case F_WRLCK:
995			lkop = SMB_LOCK_EXCL;
996			break;
997		    case F_RDLCK:
998			lkop = SMB_LOCK_SHARED;
999			break;
1000		    case F_UNLCK:
1001			lkop = SMB_LOCK_RELEASE;
1002			break;
1003		    default:
1004			return EINVAL;
1005		}
1006		error = lf_advlock(ap, &np->n_lockf, size);
1007		if (error)
1008			break;
1009		lkop = SMB_LOCK_EXCL;
1010		error = smbfs_smb_lock(np, lkop, id, start, end, &scred);
1011		if (error) {
1012			ap->a_op = F_UNLCK;
1013			lf_advlock(ap, &np->n_lockf, size);
1014		}
1015		break;
1016	    case F_UNLCK:
1017		lf_advlock(ap, &np->n_lockf, size);
1018		error = smbfs_smb_lock(np, SMB_LOCK_RELEASE, id, start, end, &scred);
1019		break;
1020	    case F_GETLK:
1021		error = lf_advlock(ap, &np->n_lockf, size);
1022		break;
1023	    default:
1024		return EINVAL;
1025	}
1026	return error;
1027}
1028
1029static int
1030smbfs_pathcheck(struct smbmount *smp, const char *name, int nmlen, int nameiop)
1031{
1032	static const char *badchars = "*/\\:<>;?";
1033	static const char *badchars83 = " +|,[]=";
1034	const char *cp;
1035	int i, error;
1036
1037	if (nameiop == LOOKUP)
1038		return 0;
1039	error = ENOENT;
1040	if (SMB_DIALECT(SSTOVC(smp->sm_share)) < SMB_DIALECT_LANMAN2_0) {
1041		/*
1042		 * Name should conform 8.3 format
1043		 */
1044		if (nmlen > 12)
1045			return ENAMETOOLONG;
1046		cp = index(name, '.');
1047		if (cp == NULL)
1048			return error;
1049		if (cp == name || (cp - name) > 8)
1050			return error;
1051		cp = index(cp + 1, '.');
1052		if (cp != NULL)
1053			return error;
1054		for (cp = name, i = 0; i < nmlen; i++, cp++)
1055			if (index(badchars83, *cp) != NULL)
1056				return error;
1057	}
1058	for (cp = name, i = 0; i < nmlen; i++, cp++)
1059		if (index(badchars, *cp) != NULL)
1060			return error;
1061	return 0;
1062}
1063
1064#ifndef PDIRUNLOCK
1065#define	PDIRUNLOCK	0
1066#endif
1067
1068/*
1069 * Things go even weird without fixed inode numbers...
1070 */
1071int
1072smbfs_lookup(ap)
1073	struct vop_lookup_args /* {
1074		struct vnodeop_desc *a_desc;
1075		struct vnode *a_dvp;
1076		struct vnode **a_vpp;
1077		struct componentname *a_cnp;
1078	} */ *ap;
1079{
1080	struct componentname *cnp = ap->a_cnp;
1081	struct thread *td = cnp->cn_thread;
1082	struct vnode *dvp = ap->a_dvp;
1083	struct vnode **vpp = ap->a_vpp;
1084	struct vnode *vp;
1085	struct smbmount *smp;
1086	struct mount *mp = dvp->v_mount;
1087	struct smbnode *dnp;
1088	struct smbfattr fattr, *fap;
1089	struct smb_cred scred;
1090	char *name = cnp->cn_nameptr;
1091	int flags = cnp->cn_flags;
1092	int nameiop = cnp->cn_nameiop;
1093	int nmlen = cnp->cn_namelen;
1094	int lockparent, wantparent, error, islastcn, isdot;
1095	int killit;
1096
1097	SMBVDEBUG("\n");
1098	cnp->cn_flags &= ~PDIRUNLOCK;
1099	if (dvp->v_type != VDIR)
1100		return ENOTDIR;
1101	if ((flags & ISDOTDOT) && (dvp->v_vflag & VV_ROOT)) {
1102		SMBFSERR("invalid '..'\n");
1103		return EIO;
1104	}
1105#ifdef SMB_VNODE_DEBUG
1106	{
1107		char *cp, c;
1108
1109		cp = name + nmlen;
1110		c = *cp;
1111		*cp = 0;
1112		SMBVDEBUG("%d '%s' in '%s' id=d\n", nameiop, name,
1113			VTOSMB(dvp)->n_name);
1114		*cp = c;
1115	}
1116#endif
1117	islastcn = flags & ISLASTCN;
1118	if (islastcn && (mp->mnt_flag & MNT_RDONLY) && (nameiop != LOOKUP))
1119		return EROFS;
1120	if ((error = VOP_ACCESS(dvp, VEXEC, cnp->cn_cred, td)) != 0)
1121		return error;
1122	lockparent = flags & LOCKPARENT;
1123	wantparent = flags & (LOCKPARENT|WANTPARENT);
1124	smp = VFSTOSMBFS(mp);
1125	dnp = VTOSMB(dvp);
1126	isdot = (nmlen == 1 && name[0] == '.');
1127
1128	error = smbfs_pathcheck(smp, cnp->cn_nameptr, cnp->cn_namelen, nameiop);
1129
1130	if (error)
1131		return ENOENT;
1132
1133	error = cache_lookup(dvp, vpp, cnp);
1134	SMBVDEBUG("cache_lookup returned %d\n", error);
1135	if (error > 0)
1136		return error;
1137	if (error) {		/* name was found */
1138		struct vattr vattr;
1139		int vpid;
1140
1141		vp = *vpp;
1142		mp_fixme("Unlocked v_id access.");
1143		vpid = vp->v_id;
1144		if (dvp == vp) {	/* lookup on current */
1145			vref(vp);
1146			error = 0;
1147			SMBVDEBUG("cached '.'\n");
1148		} else if (flags & ISDOTDOT) {
1149			VOP_UNLOCK(dvp, 0, td);	/* unlock parent */
1150			cnp->cn_flags |= PDIRUNLOCK;
1151			error = vget(vp, LK_EXCLUSIVE, td);
1152			if (!error && lockparent && islastcn) {
1153				error = vn_lock(dvp, LK_EXCLUSIVE, td);
1154				if (error == 0)
1155					cnp->cn_flags &= ~PDIRUNLOCK;
1156			}
1157		} else {
1158			error = vget(vp, LK_EXCLUSIVE, td);
1159			if (!lockparent || error || !islastcn) {
1160				VOP_UNLOCK(dvp, 0, td);
1161				cnp->cn_flags |= PDIRUNLOCK;
1162			}
1163		}
1164		if (!error) {
1165			killit = 0;
1166			if (vpid == vp->v_id) {
1167			   error = VOP_GETATTR(vp, &vattr, cnp->cn_cred, td);
1168			   /*
1169			    * If the file type on the server is inconsistent
1170			    * with what it was when we created the vnode,
1171			    * kill the bogus vnode now and fall through to
1172			    * the code below to create a new one with the
1173			    * right type.
1174			    */
1175			   if (error == 0 &&
1176			      ((vp->v_type == VDIR &&
1177			      (VTOSMB(vp)->n_dosattr & SMB_FA_DIR) == 0) ||
1178			      (vp->v_type == VREG &&
1179			      (VTOSMB(vp)->n_dosattr & SMB_FA_DIR) != 0)))
1180			      killit = 1;
1181			   else if (error == 0
1182			/*    && vattr.va_ctime.tv_sec == VTOSMB(vp)->n_ctime*/) {
1183				if (nameiop != LOOKUP && islastcn)
1184					cnp->cn_flags |= SAVENAME;
1185				SMBVDEBUG("use cached vnode\n");
1186				return (0);
1187			   }
1188			   cache_purge(vp);
1189			}
1190			vput(vp);
1191			if (killit)
1192				vgone(vp);
1193			if (lockparent && dvp != vp && islastcn)
1194				VOP_UNLOCK(dvp, 0, td);
1195		}
1196		error = vn_lock(dvp, LK_EXCLUSIVE, td);
1197		*vpp = NULLVP;
1198		if (error) {
1199			cnp->cn_flags |= PDIRUNLOCK;
1200			return (error);
1201		}
1202		cnp->cn_flags &= ~PDIRUNLOCK;
1203	}
1204	/*
1205	 * entry is not in the cache or has been expired
1206	 */
1207	error = 0;
1208	*vpp = NULLVP;
1209	smb_makescred(&scred, td, cnp->cn_cred);
1210	fap = &fattr;
1211	if (flags & ISDOTDOT) {
1212		error = smbfs_smb_lookup(VTOSMB(dnp->n_parent), NULL, 0, fap,
1213		    &scred);
1214		SMBVDEBUG("result of dotdot lookup: %d\n", error);
1215	} else {
1216		fap = &fattr;
1217		error = smbfs_smb_lookup(dnp, name, nmlen, fap, &scred);
1218/*		if (cnp->cn_namelen == 1 && cnp->cn_nameptr[0] == '.')*/
1219		SMBVDEBUG("result of smbfs_smb_lookup: %d\n", error);
1220	}
1221	if (error && error != ENOENT)
1222		return error;
1223	if (error) {			/* entry not found */
1224		/*
1225		 * Handle RENAME or CREATE case...
1226		 */
1227		if ((nameiop == CREATE || nameiop == RENAME) && wantparent && islastcn) {
1228			error = VOP_ACCESS(dvp, VWRITE, cnp->cn_cred, td);
1229			if (error)
1230				return error;
1231			cnp->cn_flags |= SAVENAME;
1232			if (!lockparent) {
1233				VOP_UNLOCK(dvp, 0, td);
1234				cnp->cn_flags |= PDIRUNLOCK;
1235			}
1236			return (EJUSTRETURN);
1237		}
1238		return ENOENT;
1239	}/* else {
1240		SMBVDEBUG("Found entry %s with id=%d\n", fap->entryName, fap->dirEntNum);
1241	}*/
1242	/*
1243	 * handle DELETE case ...
1244	 */
1245	if (nameiop == DELETE && islastcn) { 	/* delete last component */
1246		error = VOP_ACCESS(dvp, VWRITE, cnp->cn_cred, td);
1247		if (error)
1248			return error;
1249		if (isdot) {
1250			VREF(dvp);
1251			*vpp = dvp;
1252			return 0;
1253		}
1254		error = smbfs_nget(mp, dvp, name, nmlen, fap, &vp);
1255		if (error)
1256			return error;
1257		*vpp = vp;
1258		cnp->cn_flags |= SAVENAME;
1259		if (!lockparent) {
1260			VOP_UNLOCK(dvp, 0, td);
1261			cnp->cn_flags |= PDIRUNLOCK;
1262		}
1263		return 0;
1264	}
1265	if (nameiop == RENAME && islastcn && wantparent) {
1266		error = VOP_ACCESS(dvp, VWRITE, cnp->cn_cred, td);
1267		if (error)
1268			return error;
1269		if (isdot)
1270			return EISDIR;
1271		error = smbfs_nget(mp, dvp, name, nmlen, fap, &vp);
1272		if (error)
1273			return error;
1274		*vpp = vp;
1275		cnp->cn_flags |= SAVENAME;
1276		if (!lockparent) {
1277			VOP_UNLOCK(dvp, 0, td);
1278			cnp->cn_flags |= PDIRUNLOCK;
1279		}
1280		return 0;
1281	}
1282	if (flags & ISDOTDOT) {
1283		VOP_UNLOCK(dvp, 0, td);
1284		error = smbfs_nget(mp, dvp, name, nmlen, NULL, &vp);
1285		if (error) {
1286			vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY, td);
1287			return error;
1288		}
1289		if (lockparent && islastcn) {
1290			error = vn_lock(dvp, LK_EXCLUSIVE, td);
1291			if (error) {
1292				cnp->cn_flags |= PDIRUNLOCK;
1293				vput(vp);
1294				return error;
1295			}
1296		}
1297		*vpp = vp;
1298	} else if (isdot) {
1299		vref(dvp);
1300		*vpp = dvp;
1301	} else {
1302		error = smbfs_nget(mp, dvp, name, nmlen, fap, &vp);
1303		if (error)
1304			return error;
1305		*vpp = vp;
1306		SMBVDEBUG("lookup: getnewvp!\n");
1307		if (!lockparent || !islastcn) {
1308			VOP_UNLOCK(dvp, 0, td);
1309			cnp->cn_flags |= PDIRUNLOCK;
1310		}
1311	}
1312	if ((cnp->cn_flags & MAKEENTRY)/* && !islastcn*/) {
1313/*		VTOSMB(*vpp)->n_ctime = VTOSMB(*vpp)->n_vattr.va_ctime.tv_sec;*/
1314		cache_enter(dvp, *vpp, cnp);
1315	}
1316	return 0;
1317}
1318