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