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