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