xref: /dragonfly/sys/vfs/smbfs/smbfs_vnops.c (revision b40e316c)
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: src/sys/fs/smbfs/smbfs_vnops.c,v 1.2.2.8 2003/04/04 08:57:23 tjr Exp $
33  * $DragonFly: src/sys/vfs/smbfs/smbfs_vnops.c,v 1.21 2004/11/12 00:09:48 dillon Exp $
34  */
35 #include <sys/param.h>
36 #include <sys/systm.h>
37 #include <sys/kernel.h>
38 #include <sys/proc.h>
39 #include <sys/namei.h>
40 #include <sys/fcntl.h>
41 #include <sys/mount.h>
42 #include <sys/unistd.h>
43 #include <sys/vnode.h>
44 #include <sys/lockf.h>
45 
46 #include <vm/vm.h>
47 #include <vm/vm_extern.h>
48 #include <vm/vm_zone.h>
49 
50 
51 #include <netproto/smb/smb.h>
52 #include <netproto/smb/smb_conn.h>
53 #include <netproto/smb/smb_subr.h>
54 
55 #include "smbfs.h"
56 #include "smbfs_node.h"
57 #include "smbfs_subr.h"
58 
59 #include <sys/buf.h>
60 
61 /*
62  * Prototypes for SMBFS vnode operations
63  */
64 static int smbfs_create(struct vop_create_args *);
65 static int smbfs_mknod(struct vop_mknod_args *);
66 static int smbfs_open(struct vop_open_args *);
67 static int smbfs_closel(struct vop_close_args *);
68 static int smbfs_access(struct vop_access_args *);
69 static int smbfs_getattr(struct vop_getattr_args *);
70 static int smbfs_setattr(struct vop_setattr_args *);
71 static int smbfs_read(struct vop_read_args *);
72 static int smbfs_write(struct vop_write_args *);
73 static int smbfs_fsync(struct vop_fsync_args *);
74 static int smbfs_remove(struct vop_remove_args *);
75 static int smbfs_link(struct vop_link_args *);
76 static int smbfs_lookup(struct vop_lookup_args *);
77 static int smbfs_rename(struct vop_rename_args *);
78 static int smbfs_mkdir(struct vop_mkdir_args *);
79 static int smbfs_rmdir(struct vop_rmdir_args *);
80 static int smbfs_symlink(struct vop_symlink_args *);
81 static int smbfs_readdir(struct vop_readdir_args *);
82 static int smbfs_bmap(struct vop_bmap_args *);
83 static int smbfs_strategy(struct vop_strategy_args *);
84 static int smbfs_print(struct vop_print_args *);
85 static int smbfs_pathconf(struct vop_pathconf_args *ap);
86 static int smbfs_advlock(struct vop_advlock_args *);
87 static int smbfs_getextattr(struct vop_getextattr_args *ap);
88 
89 struct vnodeopv_entry_desc smbfs_vnodeop_entries[] = {
90 	{ &vop_default_desc,		vop_defaultop },
91 	{ &vop_access_desc,		(void *) smbfs_access },
92 	{ &vop_advlock_desc,		(void *) smbfs_advlock },
93 	{ &vop_bmap_desc,		(void *) smbfs_bmap },
94 	{ &vop_close_desc,		(void *) smbfs_closel },
95 	{ &vop_create_desc,		(void *) smbfs_create },
96 	{ &vop_fsync_desc,		(void *) smbfs_fsync },
97 	{ &vop_getattr_desc,		(void *) smbfs_getattr },
98 	{ &vop_getpages_desc,		(void *) smbfs_getpages },
99 	{ &vop_inactive_desc,		(void *) smbfs_inactive },
100 	{ &vop_ioctl_desc,		(void *) smbfs_ioctl },
101 	{ &vop_islocked_desc,		(void *) vop_stdislocked },
102 	{ &vop_link_desc,		(void *) smbfs_link },
103 	{ &vop_lock_desc,		(void *) vop_stdlock },
104 	{ &vop_lookup_desc,		(void *) smbfs_lookup },
105 	{ &vop_mkdir_desc,		(void *) smbfs_mkdir },
106 	{ &vop_mknod_desc,		(void *) smbfs_mknod },
107 	{ &vop_open_desc,		(void *) smbfs_open },
108 	{ &vop_pathconf_desc,		(void *) smbfs_pathconf },
109 	{ &vop_print_desc,		(void *) smbfs_print },
110 	{ &vop_putpages_desc,		(void *) smbfs_putpages },
111 	{ &vop_read_desc,		(void *) smbfs_read },
112 	{ &vop_readdir_desc,		(void *) smbfs_readdir },
113 	{ &vop_reclaim_desc,		(void *) smbfs_reclaim },
114 	{ &vop_remove_desc,		(void *) smbfs_remove },
115 	{ &vop_rename_desc,		(void *) smbfs_rename },
116 	{ &vop_rmdir_desc,		(void *) smbfs_rmdir },
117 	{ &vop_setattr_desc,		(void *) smbfs_setattr },
118 	{ &vop_strategy_desc,		(void *) smbfs_strategy },
119 	{ &vop_symlink_desc,		(void *) smbfs_symlink },
120 	{ &vop_unlock_desc,		(void *) vop_stdunlock },
121 	{ &vop_write_desc,		(void *) smbfs_write },
122 	{ &vop_getextattr_desc, 	(void *) smbfs_getextattr },
123 /*	{ &vop_setextattr_desc,		(void *) smbfs_setextattr },*/
124 	{ NULL, NULL }
125 };
126 
127 /*
128  * smbfs_access(struct vnode *a_vp, int a_mode, struct ucred *a_cred,
129  *		struct thread *a_td)
130  */
131 static int
132 smbfs_access(struct vop_access_args *ap)
133 {
134 	struct vnode *vp = ap->a_vp;
135 	struct ucred *cred = ap->a_cred;
136 	u_int mode = ap->a_mode;
137 	struct smbmount *smp = VTOSMBFS(vp);
138 	int error = 0;
139 
140 	SMBVDEBUG("\n");
141 	if ((mode & VWRITE) && (vp->v_mount->mnt_flag & MNT_RDONLY)) {
142 		switch (vp->v_type) {
143 		    case VREG: case VDIR: case VLNK:
144 			return EROFS;
145 		    default:
146 			break;
147 		}
148 	}
149 	if (cred->cr_uid == 0)
150 		return 0;
151 	if (cred->cr_uid != smp->sm_args.uid) {
152 		mode >>= 3;
153 		if (!groupmember(smp->sm_args.gid, cred))
154 			mode >>= 3;
155 	}
156 	error = (((vp->v_type == VREG) ? smp->sm_args.file_mode : smp->sm_args.dir_mode) & mode) == mode ? 0 : EACCES;
157 	return error;
158 }
159 
160 /*
161  * smbfs_open(struct vnode *a_vp, int a_mode, struct ucred *a_cred,
162  *	      struct thread *a_td)
163  */
164 /* ARGSUSED */
165 static int
166 smbfs_open(struct vop_open_args *ap)
167 {
168 	struct vnode *vp = ap->a_vp;
169 	struct smbnode *np = VTOSMB(vp);
170 	struct smb_cred scred;
171 	struct vattr vattr;
172 	int mode = ap->a_mode;
173 	int error, accmode;
174 
175 	SMBVDEBUG("%s,%d\n", np->n_name, np->n_opencount);
176 	if (vp->v_type != VREG && vp->v_type != VDIR) {
177 		SMBFSERR("open eacces vtype=%d\n", vp->v_type);
178 		return EACCES;
179 	}
180 	if (vp->v_type == VDIR) {
181 		np->n_opencount++;
182 		return 0;
183 	}
184 	if (np->n_flag & NMODIFIED) {
185 		if ((error = smbfs_vinvalbuf(vp, V_SAVE, ap->a_td, 1)) == EINTR)
186 			return error;
187 		smbfs_attr_cacheremove(vp);
188 		error = VOP_GETATTR(vp, &vattr, ap->a_td);
189 		if (error)
190 			return error;
191 		np->n_mtime.tv_sec = vattr.va_mtime.tv_sec;
192 	} else {
193 		error = VOP_GETATTR(vp, &vattr, ap->a_td);
194 		if (error)
195 			return error;
196 		if (np->n_mtime.tv_sec != vattr.va_mtime.tv_sec) {
197 			error = smbfs_vinvalbuf(vp, V_SAVE, ap->a_td, 1);
198 			if (error == EINTR)
199 				return error;
200 			np->n_mtime.tv_sec = vattr.va_mtime.tv_sec;
201 		}
202 	}
203 	if (np->n_opencount) {
204 		np->n_opencount++;
205 		return 0;
206 	}
207 	accmode = SMB_AM_OPENREAD;
208 	if ((vp->v_mount->mnt_flag & MNT_RDONLY) == 0)
209 		accmode = SMB_AM_OPENRW;
210 	smb_makescred(&scred, ap->a_td, ap->a_cred);
211 	error = smbfs_smb_open(np, accmode, &scred);
212 	if (error) {
213 		if (mode & FWRITE)
214 			return EACCES;
215 		accmode = SMB_AM_OPENREAD;
216 		error = smbfs_smb_open(np, accmode, &scred);
217 	}
218 	if (!error) {
219 		np->n_opencount++;
220 	}
221 	smbfs_attr_cacheremove(vp);
222 	return error;
223 }
224 
225 static int
226 smbfs_closel(struct vop_close_args *ap)
227 {
228 	struct vnode *vp = ap->a_vp;
229 	struct smbnode *np = VTOSMB(vp);
230 	struct thread *td = ap->a_td;
231 	struct smb_cred scred;
232 	struct vattr vattr;
233 	int error;
234 
235 	SMBVDEBUG("name=%s, pid=%d, c=%d\n",np->n_name, p->p_pid, np->n_opencount);
236 
237 	smb_makescred(&scred, td, proc0.p_ucred);
238 
239 	if (np->n_opencount == 0) {
240 		if (vp->v_type != VDIR)
241 			SMBERROR("Negative opencount\n");
242 		return 0;
243 	}
244 	np->n_opencount--;
245 	if (vp->v_type == VDIR) {
246 		if (np->n_opencount)
247 			return 0;
248 		if (np->n_dirseq) {
249 			smbfs_findclose(np->n_dirseq, &scred);
250 			np->n_dirseq = NULL;
251 		}
252 		error = 0;
253 	} else {
254 		error = smbfs_vinvalbuf(vp, V_SAVE, td, 1);
255 		if (np->n_opencount)
256 			return error;
257 		VOP_GETATTR(vp, &vattr, td);
258 		error = smbfs_smb_close(np->n_mount->sm_share, np->n_fid,
259 			   &np->n_mtime, &scred);
260 	}
261 	smbfs_attr_cacheremove(vp);
262 	return error;
263 }
264 
265 /*
266  * smbfs_getattr call from vfs.
267  *
268  * smbfs_getattr(struct vnode *a_vp, struct vattr *a_vap, struct thread *a_td)
269  */
270 static int
271 smbfs_getattr(struct vop_getattr_args *ap)
272 {
273 	struct vnode *vp = ap->a_vp;
274 	struct smbnode *np = VTOSMB(vp);
275 	struct vattr *va=ap->a_vap;
276 	struct smbfattr fattr;
277 	struct smb_cred scred;
278 	u_quad_t oldsize;
279 	int error;
280 
281 	SMBVDEBUG("%lx: '%s' %d\n", (long)vp, np->n_name, (vp->v_flag & VROOT) != 0);
282 	error = smbfs_attr_cachelookup(vp, va);
283 	if (!error)
284 		return 0;
285 	SMBVDEBUG("not in the cache\n");
286 	smb_makescred(&scred, ap->a_td, proc0.p_ucred);
287 	oldsize = np->n_size;
288 	error = smbfs_smb_lookup(np, NULL, 0, &fattr, &scred);
289 	if (error) {
290 		SMBVDEBUG("error %d\n", error);
291 		return error;
292 	}
293 	smbfs_attr_cacheenter(vp, &fattr);
294 	smbfs_attr_cachelookup(vp, va);
295 	if (np->n_opencount)
296 		np->n_size = oldsize;
297 	return 0;
298 }
299 
300 /*
301  * smbfs_setattr(struct vnode *a_vp, struct vattr *a_vap, struct ucred *a_cred,
302  *		 struct thread *a_td)
303  */
304 static int
305 smbfs_setattr(struct vop_setattr_args *ap)
306 {
307 	struct vnode *vp = ap->a_vp;
308 	struct smbnode *np = VTOSMB(vp);
309 	struct vattr *vap = ap->a_vap;
310 	struct timespec *mtime, *atime;
311 	struct smb_cred scred;
312 	struct smb_share *ssp = np->n_mount->sm_share;
313 	struct smb_vc *vcp = SSTOVC(ssp);
314 	u_quad_t tsize = 0;
315 	int isreadonly, doclose, error = 0;
316 
317 	SMBVDEBUG("\n");
318 	if (vap->va_flags != VNOVAL)
319 		return EOPNOTSUPP;
320 	isreadonly = (vp->v_mount->mnt_flag & MNT_RDONLY);
321 	/*
322 	 * Disallow write attempts if the filesystem is mounted read-only.
323 	 */
324   	if ((vap->va_uid != (uid_t)VNOVAL || vap->va_gid != (gid_t)VNOVAL ||
325 	     vap->va_atime.tv_sec != VNOVAL || vap->va_mtime.tv_sec != VNOVAL ||
326 	     vap->va_mode != (mode_t)VNOVAL) && isreadonly)
327 		return EROFS;
328 	smb_makescred(&scred, ap->a_td, ap->a_cred);
329 	if (vap->va_size != VNOVAL) {
330  		switch (vp->v_type) {
331  		    case VDIR:
332  			return EISDIR;
333  		    case VREG:
334 			break;
335  		    default:
336 			return EINVAL;
337   		};
338 		if (isreadonly)
339 			return EROFS;
340 		doclose = 0;
341 		vnode_pager_setsize(vp, (u_long)vap->va_size);
342  		tsize = np->n_size;
343  		np->n_size = vap->va_size;
344 		if (np->n_opencount == 0) {
345 			error = smbfs_smb_open(np, SMB_AM_OPENRW, &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_args.uid &&
366 		    (error = suser_cred(ap->a_cred, PRISON_ROOT)) &&
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_opencount == 0) {
381 			if (vcp->vc_flags & SMBV_WIN95) {
382 				error = VOP_OPEN(vp, FWRITE, ap->a_cred, NULL, ap->a_td);
383 				if (!error) {
384 /*				error = smbfs_smb_setfattrNT(np, 0, mtime, atime, &scred);
385 				VOP_GETATTR(vp, &vattr, ap->a_td);*/
386 				if (mtime)
387 					np->n_mtime = *mtime;
388 				VOP_CLOSE(vp, FWRITE, 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_td);
419 	np->n_mtime.tv_sec = vap->va_mtime.tv_sec;
420 	return error;
421 }
422 /*
423  * smbfs_read call.
424  *
425  * smbfs_read(struct vnode *a_vp, struct uio *a_uio, int a_ioflag,
426  *	      struct ucred *a_cred)
427  */
428 static int
429 smbfs_read(struct vop_read_args *ap)
430 {
431 	struct vnode *vp = ap->a_vp;
432 	struct uio *uio = ap->a_uio;
433 
434 	SMBVDEBUG("\n");
435 	if (vp->v_type != VREG && vp->v_type != VDIR)
436 		return EPERM;
437 	return smbfs_readvnode(vp, uio, ap->a_cred);
438 }
439 
440 /*
441  * smbfs_write(struct vnode *a_vp, struct uio *a_uio, int a_ioflag,
442  *	       struct ucred *a_cred)
443  */
444 static int
445 smbfs_write(struct vop_write_args *ap)
446 {
447 	struct vnode *vp = ap->a_vp;
448 	struct uio *uio = ap->a_uio;
449 
450 	SMBVDEBUG("%d,ofs=%d,sz=%d\n",vp->v_type, (int)uio->uio_offset, uio->uio_resid);
451 	if (vp->v_type != VREG)
452 		return (EPERM);
453 	return smbfs_writevnode(vp, uio, ap->a_cred,ap->a_ioflag);
454 }
455 /*
456  * smbfs_create call
457  * Create a regular file. On entry the directory to contain the file being
458  * created is locked.  We must release before we return.
459  *
460  * smbfs_create(struct vnode *a_dvp, struct vnode **a_vpp,
461  *		struct componentname *a_cnp, struct vattr *a_vap)
462  */
463 static int
464 smbfs_create(struct vop_create_args *ap)
465 {
466 	struct vnode *dvp = ap->a_dvp;
467 	struct vattr *vap = ap->a_vap;
468 	struct vnode **vpp=ap->a_vpp;
469 	struct componentname *cnp = ap->a_cnp;
470 	struct smbnode *dnp = VTOSMB(dvp);
471 	struct vnode *vp;
472 	struct vattr vattr;
473 	struct smbfattr fattr;
474 	struct smb_cred scred;
475 	char *name = cnp->cn_nameptr;
476 	int nmlen = cnp->cn_namelen;
477 	int error;
478 
479 
480 	SMBVDEBUG("\n");
481 	*vpp = NULL;
482 	if (vap->va_type != VREG)
483 		return EOPNOTSUPP;
484 	if ((error = VOP_GETATTR(dvp, &vattr, cnp->cn_td)))
485 		return error;
486 	smb_makescred(&scred, cnp->cn_td, cnp->cn_cred);
487 
488 	error = smbfs_smb_create(dnp, name, nmlen, &scred);
489 	if (error)
490 		return error;
491 	error = smbfs_smb_lookup(dnp, name, nmlen, &fattr, &scred);
492 	if (error)
493 		return error;
494 	error = smbfs_nget(VTOVFS(dvp), dvp, name, nmlen, &fattr, &vp);
495 	if (error)
496 		return error;
497 	*vpp = vp;
498 	return error;
499 }
500 
501 /*
502  * smbfs_remove(struct vnodeop_desc *a_desc, struct vnode *a_dvp,
503  *		struct vnode *a_vp, struct componentname *a_cnp)
504  */
505 static int
506 smbfs_remove(struct vop_remove_args *ap)
507 {
508 	struct vnode *vp = ap->a_vp;
509 /*	struct vnode *dvp = ap->a_dvp;*/
510 	struct componentname *cnp = ap->a_cnp;
511 	struct smbnode *np = VTOSMB(vp);
512 	struct smb_cred scred;
513 	int error;
514 
515 	if (vp->v_type == VDIR || np->n_opencount || vp->v_usecount != 1)
516 		return EPERM;
517 	smb_makescred(&scred, cnp->cn_td, cnp->cn_cred);
518 	error = smbfs_smb_delete(np, &scred);
519 	return error;
520 }
521 
522 /*
523  * smbfs_file rename call
524  *
525  * smbfs_rename(struct vnode *a_fdvp, struct vnode *a_fvp,
526  *		struct componentname *a_fcnp, struct vnode *a_tdvp,
527  *		struct vnode *a_tvp, struct componentname *a_tcnp)
528  */
529 static int
530 smbfs_rename(struct vop_rename_args *ap)
531 {
532 	struct vnode *fvp = ap->a_fvp;
533 	struct vnode *tvp = ap->a_tvp;
534 	struct vnode *fdvp = ap->a_fdvp;
535 	struct vnode *tdvp = ap->a_tdvp;
536 	struct componentname *tcnp = ap->a_tcnp;
537 /*	struct componentname *fcnp = ap->a_fcnp;*/
538 	struct smb_cred scred;
539 	u_int16_t flags = 6;
540 	int error=0;
541 
542 	/* Check for cross-device rename */
543 	if ((fvp->v_mount != tdvp->v_mount) ||
544 	    (tvp && (fvp->v_mount != tvp->v_mount))) {
545 		error = EXDEV;
546 		goto out;
547 	}
548 
549 	if (tvp && tvp->v_usecount > 1) {
550 		error = EBUSY;
551 		goto out;
552 	}
553 	flags = 0x10;			/* verify all writes */
554 	if (fvp->v_type == VDIR) {
555 		flags |= 2;
556 	} else if (fvp->v_type == VREG) {
557 		flags |= 1;
558 	} else {
559 		error = EINVAL;
560 		goto out;
561 	}
562 	smb_makescred(&scred, tcnp->cn_td, tcnp->cn_cred);
563 	/*
564 	 * It seems that Samba doesn't implement SMB_COM_MOVE call...
565 	 */
566 #ifdef notnow
567 	if (SMB_DIALECT(SSTOCN(smp->sm_share)) >= SMB_DIALECT_LANMAN1_0) {
568 		error = smbfs_smb_move(VTOSMB(fvp), VTOSMB(tdvp),
569 		    tcnp->cn_nameptr, tcnp->cn_namelen, flags, &scred);
570 	} else
571 #endif
572 	{
573 		/*
574 		 * We have to do the work atomicaly
575 		 */
576 		if (tvp && tvp != fvp) {
577 			error = smbfs_smb_delete(VTOSMB(tvp), &scred);
578 			if (error)
579 				goto out_cacherem;
580 		}
581 		error = smbfs_smb_rename(VTOSMB(fvp), VTOSMB(tdvp),
582 		    tcnp->cn_nameptr, tcnp->cn_namelen, &scred);
583 	}
584 
585 out_cacherem:
586 	smbfs_attr_cacheremove(fdvp);
587 	smbfs_attr_cacheremove(tdvp);
588 out:
589 	if (tdvp == tvp)
590 		vrele(tdvp);
591 	else
592 		vput(tdvp);
593 	if (tvp)
594 		vput(tvp);
595 	vrele(fdvp);
596 	vrele(fvp);
597 #ifdef possible_mistake
598 #error x
599 	vgone(fvp);
600 	if (tvp)
601 		vgone(tvp);
602 #endif
603 	return error;
604 }
605 
606 /*
607  * somtime it will come true...
608  *
609  * smbfs_link(struct vnode *a_tdvp, struct vnode *a_vp,
610  *	      struct componentname *a_cnp)
611  */
612 static int
613 smbfs_link(struct vop_link_args *ap)
614 {
615 	return EOPNOTSUPP;
616 }
617 
618 /*
619  * smbfs_symlink link create call.
620  * Sometime it will be functional...
621  *
622  * smbfs_symlink(struct vnode *a_dvp, struct vnode **a_vpp,
623  *		 struct componentname *a_cnp, struct vattr *a_vap,
624  *		 char *a_target)
625  */
626 static int
627 smbfs_symlink(struct vop_symlink_args *ap)
628 {
629 	return EOPNOTSUPP;
630 }
631 
632 static int
633 smbfs_mknod(struct vop_mknod_args *ap)
634 {
635 	return EOPNOTSUPP;
636 }
637 
638 /*
639  * smbfs_mkdir(struct vnode *a_dvp, struct vnode **a_vpp,
640  *		struct componentname *a_cnp, struct vattr *a_vap)
641  */
642 static int
643 smbfs_mkdir(struct vop_mkdir_args *ap)
644 {
645 	struct vnode *dvp = ap->a_dvp;
646 /*	struct vattr *vap = ap->a_vap;*/
647 	struct vnode *vp;
648 	struct componentname *cnp = ap->a_cnp;
649 	struct smbnode *dnp = VTOSMB(dvp);
650 	struct vattr vattr;
651 	struct smb_cred scred;
652 	struct smbfattr fattr;
653 	char *name = cnp->cn_nameptr;
654 	int len = cnp->cn_namelen;
655 	int error;
656 
657 	if ((error = VOP_GETATTR(dvp, &vattr, cnp->cn_td))) {
658 		return error;
659 	}
660 	if ((name[0] == '.') && ((len == 1) || ((len == 2) && (name[1] == '.'))))
661 		return EEXIST;
662 	smb_makescred(&scred, cnp->cn_td, cnp->cn_cred);
663 	error = smbfs_smb_mkdir(dnp, name, len, &scred);
664 	if (error)
665 		return error;
666 	error = smbfs_smb_lookup(dnp, name, len, &fattr, &scred);
667 	if (error)
668 		return error;
669 	error = smbfs_nget(VTOVFS(dvp), dvp, name, len, &fattr, &vp);
670 	if (error)
671 		return error;
672 	*ap->a_vpp = vp;
673 	return 0;
674 }
675 
676 /*
677  * smbfs_remove directory call
678  *
679  * smbfs_rmdir(struct vnode *a_dvp, struct vnode *a_vp,
680  *		struct componentname *a_cnp)
681  */
682 static int
683 smbfs_rmdir(struct vop_rmdir_args *ap)
684 {
685 	struct vnode *vp = ap->a_vp;
686 	struct vnode *dvp = ap->a_dvp;
687 	struct componentname *cnp = ap->a_cnp;
688 /*	struct smbmount *smp = VTOSMBFS(vp);*/
689 	struct smbnode *dnp = VTOSMB(dvp);
690 	struct smbnode *np = VTOSMB(vp);
691 	struct smb_cred scred;
692 	int error;
693 
694 	if (dvp == vp)
695 		return EINVAL;
696 
697 	smb_makescred(&scred, cnp->cn_td, cnp->cn_cred);
698 	error = smbfs_smb_rmdir(np, &scred);
699 	dnp->n_flag |= NMODIFIED;
700 	smbfs_attr_cacheremove(dvp);
701 	return error;
702 }
703 
704 /*
705  * smbfs_readdir call
706  *
707  * smbfs_readdir(struct vnode *a_vp, struct uio *a_uio, struct ucred *a_cred,
708  *		 int *a_eofflag, u_long *a_cookies, int a_ncookies)
709  */
710 static int
711 smbfs_readdir(struct vop_readdir_args *ap)
712 {
713 	struct vnode *vp = ap->a_vp;
714 	struct uio *uio = ap->a_uio;
715 	int error;
716 
717 	if (vp->v_type != VDIR)
718 		return (EPERM);
719 #ifdef notnow
720 	if (ap->a_ncookies) {
721 		printf("smbfs_readdir: no support for cookies now...");
722 		return (EOPNOTSUPP);
723 	}
724 #endif
725 	error = smbfs_readvnode(vp, uio, ap->a_cred);
726 	return error;
727 }
728 
729 /*
730  * smbfs_fsync(struct vnodeop_desc *a_desc, struct vnode *a_vp,
731  *		struct ucred *a_cred, int a_waitfor, struct thread *a_td)
732  */
733 /* ARGSUSED */
734 static int
735 smbfs_fsync(struct vop_fsync_args *ap)
736 {
737 /*	return (smb_flush(ap->a_vp, ap->a_cred, ap->a_waitfor, ap->a_td, 1));*/
738     return (0);
739 }
740 
741 /*
742  * smbfs_print(struct vnode *a_vp)
743  */
744 static int
745 smbfs_print(struct vop_print_args *ap)
746 {
747 	struct vnode *vp = ap->a_vp;
748 	struct smbnode *np = VTOSMB(vp);
749 
750 	if (np == NULL) {
751 		printf("no smbnode data\n");
752 		return (0);
753 	}
754 	printf("tag VT_SMBFS, name = %s, parent = %p, opencount = %d",
755 	    np->n_name, np->n_parent ? np->n_parent : NULL,
756 	    np->n_opencount);
757 	lockmgr_printinfo(&vp->v_lock);
758 	printf("\n");
759 	return (0);
760 }
761 
762 /*
763  * smbfs_pathconf(struct vnode *vp, int name, register_t *retval)
764  */
765 static int
766 smbfs_pathconf(struct vop_pathconf_args *ap)
767 {
768 	struct smbmount *smp = VFSTOSMBFS(VTOVFS(ap->a_vp));
769 	struct smb_vc *vcp = SSTOVC(smp->sm_share);
770 	register_t *retval = ap->a_retval;
771 	int error = 0;
772 
773 	switch (ap->a_name) {
774 	    case _PC_LINK_MAX:
775 		*retval = 0;
776 		break;
777 	    case _PC_NAME_MAX:
778 		*retval = (vcp->vc_hflags2 & SMB_FLAGS2_KNOWS_LONG_NAMES) ? 255 : 12;
779 		break;
780 	    case _PC_PATH_MAX:
781 		*retval = 800;	/* XXX: a correct one ? */
782 		break;
783 	    default:
784 		error = EINVAL;
785 	}
786 	return error;
787 }
788 
789 /*
790  * smbfs_strategy(struct buf *a_bp)
791  */
792 static int
793 smbfs_strategy(struct vop_strategy_args *ap)
794 {
795 	struct buf *bp=ap->a_bp;
796 	struct thread *td = NULL;
797 	int error = 0;
798 
799 	SMBVDEBUG("\n");
800 	if (bp->b_flags & B_PHYS)
801 		panic("smbfs physio");
802 	if ((bp->b_flags & B_ASYNC) == 0)
803 		td = curthread;		/* XXX */
804 
805 	if ((bp->b_flags & B_ASYNC) == 0 )
806 		error = smbfs_doio(bp, proc0.p_ucred, td);
807 	return error;
808 }
809 
810 /*
811  * smbfs_bmap(struct vnode *a_vp, daddr_t a_bn, struct vnode **a_vpp,
812  *	      daddr_t *a_bnp, int *a_runp, int *a_runb)
813  */
814 static int
815 smbfs_bmap(struct vop_bmap_args *ap)
816 {
817 	struct vnode *vp = ap->a_vp;
818 
819 	if (ap->a_vpp != NULL)
820 		*ap->a_vpp = vp;
821 	if (ap->a_bnp != NULL)
822 		*ap->a_bnp = ap->a_bn * btodb(vp->v_mount->mnt_stat.f_iosize);
823 	if (ap->a_runp != NULL)
824 		*ap->a_runp = 0;
825 	if (ap->a_runb != NULL)
826 		*ap->a_runb = 0;
827 	return (0);
828 }
829 
830 /*
831  * smbfs_ioctl(struct vnode *a_vp, u_long a_command, caddr_t a_data,
832  *		int fflag, struct ucred *cred, struct proc *p)
833  */
834 int
835 smbfs_ioctl(struct vop_ioctl_args *ap)
836 {
837 	return EINVAL;
838 }
839 
840 static char smbfs_atl[] = "rhsvda";
841 static int
842 smbfs_getextattr(struct vop_getextattr_args *ap)
843 /* {
844         IN struct vnode *a_vp;
845         IN char *a_name;
846         INOUT struct uio *a_uio;
847         IN struct ucred *a_cred;
848         IN struct thread *a_td;
849 };
850 */
851 {
852 	struct vnode *vp = ap->a_vp;
853 	struct thread *td = ap->a_td;
854 	struct ucred *cred = ap->a_cred;
855 	struct uio *uio = ap->a_uio;
856 	const char *name = ap->a_name;
857 	struct smbnode *np = VTOSMB(vp);
858 	struct vattr vattr;
859 	char buf[10];
860 	int i, attr, error;
861 
862 	error = VOP_ACCESS(vp, VREAD, cred, td);
863 	if (error)
864 		return error;
865 	error = VOP_GETATTR(vp, &vattr, td);
866 	if (error)
867 		return error;
868 	if (strcmp(name, "dosattr") == 0) {
869 		attr = np->n_dosattr;
870 		for (i = 0; i < 6; i++, attr >>= 1)
871 			buf[i] = (attr & 1) ? smbfs_atl[i] : '-';
872 		buf[i] = 0;
873 		error = uiomove(buf, i, uio);
874 
875 	} else
876 		error = EINVAL;
877 	return error;
878 }
879 
880 /*
881  * Since we expected to support F_GETLK (and SMB protocol has no such function),
882  * it is necessary to use lf_advlock(). It would be nice if this function had
883  * a callback mechanism because it will help to improve a level of consistency.
884  *
885  * smbfs_advlock(struct vnode *a_vp, caddr_t a_id, int a_op,
886  *		 struct flock *a_fl, int a_flags)
887  */
888 int
889 smbfs_advlock(struct vop_advlock_args *ap)
890 {
891 	struct vnode *vp = ap->a_vp;
892 	struct smbnode *np = VTOSMB(vp);
893 	struct flock *fl = ap->a_fl;
894 	caddr_t id = (caddr_t)1 /* ap->a_id */;
895 /*	int flags = ap->a_flags;*/
896 	struct thread *td = curthread;		/* XXX */
897 	struct smb_cred scred;
898 	off_t start, end, size;
899 	int error, lkop;
900 
901 	if (vp->v_type == VDIR) {
902 		/*
903 		 * SMB protocol have no support for directory locking.
904 		 * Although locks can be processed on local machine, I don't
905 		 * think that this is a good idea, because some programs
906 		 * can work wrong assuming directory is locked. So, we just
907 		 * return 'operation not supported
908 		 */
909 		 return EOPNOTSUPP;
910 	}
911 	size = np->n_size;
912 	switch (fl->l_whence) {
913 	    case SEEK_SET:
914 	    case SEEK_CUR:
915 		start = fl->l_start;
916 		break;
917 	    case SEEK_END:
918 		start = fl->l_start + size;
919 	    default:
920 		return EINVAL;
921 	}
922 	if (start < 0)
923 		return EINVAL;
924 	if (fl->l_len == 0)
925 		end = -1;
926 	else {
927 		end = start + fl->l_len - 1;
928 		if (end < start)
929 			return EINVAL;
930 	}
931 	smb_makescred(&scred, td, td->td_proc ? td->td_proc->p_ucred : NULL);
932 	switch (ap->a_op) {
933 	    case F_SETLK:
934 		switch (fl->l_type) {
935 		    case F_WRLCK:
936 			lkop = SMB_LOCK_EXCL;
937 			break;
938 		    case F_RDLCK:
939 			lkop = SMB_LOCK_SHARED;
940 			break;
941 		    case F_UNLCK:
942 			lkop = SMB_LOCK_RELEASE;
943 			break;
944 		    default:
945 			return EINVAL;
946 		}
947 		error = lf_advlock(ap, &np->n_lockf, size);
948 		if (error)
949 			break;
950 		lkop = SMB_LOCK_EXCL;
951 		error = smbfs_smb_lock(np, lkop, id, start, end, &scred);
952 		if (error) {
953 			ap->a_op = F_UNLCK;
954 			lf_advlock(ap, &np->n_lockf, size);
955 		}
956 		break;
957 	    case F_UNLCK:
958 		lf_advlock(ap, &np->n_lockf, size);
959 		error = smbfs_smb_lock(np, SMB_LOCK_RELEASE, id, start, end, &scred);
960 		break;
961 	    case F_GETLK:
962 		error = lf_advlock(ap, &np->n_lockf, size);
963 		break;
964 	    default:
965 		return EINVAL;
966 	}
967 	return error;
968 }
969 
970 static int
971 smbfs_pathcheck(struct smbmount *smp, const char *name, int nmlen, int nameiop)
972 {
973 	static const char *badchars = "*/\[]:<>=;?";
974 	static const char *badchars83 = " +|,";
975 	const char *cp;
976 	int i, error;
977 
978 	if (nameiop == NAMEI_LOOKUP)
979 		return 0;
980 	error = ENOENT;
981 	if (SMB_DIALECT(SSTOVC(smp->sm_share)) < SMB_DIALECT_LANMAN2_0) {
982 		/*
983 		 * Name should conform 8.3 format
984 		 */
985 		if (nmlen > 12)
986 			return ENAMETOOLONG;
987 		cp = index(name, '.');
988 		if (cp == NULL)
989 			return error;
990 		if (cp == name || (cp - name) > 8)
991 			return error;
992 		cp = index(cp + 1, '.');
993 		if (cp != NULL)
994 			return error;
995 		for (cp = name, i = 0; i < nmlen; i++, cp++)
996 			if (index(badchars83, *cp) != NULL)
997 				return error;
998 	}
999 	for (cp = name, i = 0; i < nmlen; i++, cp++)
1000 		if (index(badchars, *cp) != NULL)
1001 			return error;
1002 	return 0;
1003 }
1004 
1005 /*
1006  * Things go even weird without fixed inode numbers...
1007  *
1008  * smbfs_lookup(struct vnodeop_desc *a_desc, struct vnode *a_dvp,
1009  *		struct vnode **a_vpp, struct componentname *a_cnp)
1010  */
1011 int
1012 smbfs_lookup(struct vop_lookup_args *ap)
1013 {
1014 	struct componentname *cnp = ap->a_cnp;
1015 	struct thread *td = cnp->cn_td;
1016 	struct vnode *dvp = ap->a_dvp;
1017 	struct vnode **vpp = ap->a_vpp;
1018 	struct vnode *vp;
1019 	struct smbmount *smp;
1020 	struct mount *mp = dvp->v_mount;
1021 	struct smbnode *dnp;
1022 	struct smbfattr fattr, *fap;
1023 	struct smb_cred scred;
1024 	char *name = cnp->cn_nameptr;
1025 	int flags = cnp->cn_flags;
1026 	int nameiop = cnp->cn_nameiop;
1027 	int nmlen = cnp->cn_namelen;
1028 	int lockparent, wantparent, error, isdot;
1029 
1030 	SMBVDEBUG("\n");
1031 	cnp->cn_flags &= ~CNP_PDIRUNLOCK;
1032 	if (dvp->v_type != VDIR)
1033 		return ENOTDIR;
1034 	if ((flags & CNP_ISDOTDOT) && (dvp->v_flag & VROOT)) {
1035 		SMBFSERR("invalid '..'\n");
1036 		return EIO;
1037 	}
1038 #ifdef SMB_VNODE_DEBUG
1039 	{
1040 		char *cp, c;
1041 
1042 		cp = name + nmlen;
1043 		c = *cp;
1044 		*cp = 0;
1045 		SMBVDEBUG("%d '%s' in '%s' id=d\n", nameiop, name,
1046 			VTOSMB(dvp)->n_name);
1047 		*cp = c;
1048 	}
1049 #endif
1050 	if ((mp->mnt_flag & MNT_RDONLY) && nameiop != NAMEI_LOOKUP)
1051 		return EROFS;
1052 	if ((error = VOP_ACCESS(dvp, VEXEC, cnp->cn_cred, td)) != 0)
1053 		return error;
1054 	lockparent = flags & CNP_LOCKPARENT;
1055 	wantparent = flags & (CNP_LOCKPARENT | CNP_WANTPARENT);
1056 	smp = VFSTOSMBFS(mp);
1057 	dnp = VTOSMB(dvp);
1058 	isdot = (nmlen == 1 && name[0] == '.');
1059 
1060 	error = smbfs_pathcheck(smp, cnp->cn_nameptr, cnp->cn_namelen, nameiop);
1061 
1062 	if (error)
1063 		return ENOENT;
1064 
1065 	error = 0;
1066 	*vpp = NULLVP;
1067 	smb_makescred(&scred, td, cnp->cn_cred);
1068 	fap = &fattr;
1069 	if (flags & CNP_ISDOTDOT) {
1070 		error = smbfs_smb_lookup(VTOSMB(dnp->n_parent), NULL, 0, fap,
1071 		    &scred);
1072 		SMBVDEBUG("result of dotdot lookup: %d\n", error);
1073 	} else {
1074 		fap = &fattr;
1075 		error = smbfs_smb_lookup(dnp, name, nmlen, fap, &scred);
1076 /*		if (cnp->cn_namelen == 1 && cnp->cn_nameptr[0] == '.')*/
1077 		SMBVDEBUG("result of smbfs_smb_lookup: %d\n", error);
1078 	}
1079 	if (error && error != ENOENT)
1080 		return error;
1081 	if (error) {			/* entry not found */
1082 		/*
1083 		 * Handle RENAME or CREATE case...
1084 		 */
1085 		if ((nameiop == NAMEI_CREATE || nameiop == NAMEI_RENAME) && wantparent) {
1086 			error = VOP_ACCESS(dvp, VWRITE, cnp->cn_cred, td);
1087 			if (error)
1088 				return error;
1089 			if (!lockparent) {
1090 				VOP_UNLOCK(dvp, 0, td);
1091 				cnp->cn_flags |= CNP_PDIRUNLOCK;
1092 			}
1093 			return (EJUSTRETURN);
1094 		}
1095 		return ENOENT;
1096 	}/* else {
1097 		SMBVDEBUG("Found entry %s with id=%d\n", fap->entryName, fap->dirEntNum);
1098 	}*/
1099 	/*
1100 	 * handle DELETE case ...
1101 	 */
1102 	if (nameiop == NAMEI_DELETE) { 	/* delete last component */
1103 		error = VOP_ACCESS(dvp, VWRITE, cnp->cn_cred, td);
1104 		if (error)
1105 			return error;
1106 		if (isdot) {
1107 			vref(dvp);
1108 			*vpp = dvp;
1109 			return 0;
1110 		}
1111 		error = smbfs_nget(mp, dvp, name, nmlen, fap, &vp);
1112 		if (error)
1113 			return error;
1114 		*vpp = vp;
1115 		if (!lockparent) {
1116 			VOP_UNLOCK(dvp, 0, td);
1117 			cnp->cn_flags |= CNP_PDIRUNLOCK;
1118 		}
1119 		return 0;
1120 	}
1121 	if (nameiop == NAMEI_RENAME && wantparent) {
1122 		error = VOP_ACCESS(dvp, VWRITE, cnp->cn_cred, td);
1123 		if (error)
1124 			return error;
1125 		if (isdot)
1126 			return EISDIR;
1127 		error = smbfs_nget(mp, dvp, name, nmlen, fap, &vp);
1128 		if (error)
1129 			return error;
1130 		*vpp = vp;
1131 		if (!lockparent) {
1132 			VOP_UNLOCK(dvp, 0, td);
1133 			cnp->cn_flags |= CNP_PDIRUNLOCK;
1134 		}
1135 		return 0;
1136 	}
1137 	if (flags & CNP_ISDOTDOT) {
1138 		VOP_UNLOCK(dvp, 0, td);
1139 		error = smbfs_nget(mp, dvp, name, nmlen, NULL, &vp);
1140 		if (error) {
1141 			vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY, td);
1142 			return error;
1143 		}
1144 		if (lockparent) {
1145 			error = vn_lock(dvp, LK_EXCLUSIVE, td);
1146 			if (error) {
1147 				cnp->cn_flags |= CNP_PDIRUNLOCK;
1148 				vput(vp);
1149 				return error;
1150 			}
1151 		}
1152 		*vpp = vp;
1153 	} else if (isdot) {
1154 		vref(dvp);
1155 		*vpp = dvp;
1156 	} else {
1157 		error = smbfs_nget(mp, dvp, name, nmlen, fap, &vp);
1158 		if (error)
1159 			return error;
1160 		*vpp = vp;
1161 		SMBVDEBUG("lookup: getnewvp!\n");
1162 		if (!lockparent) {
1163 			VOP_UNLOCK(dvp, 0, td);
1164 			cnp->cn_flags |= CNP_PDIRUNLOCK;
1165 		}
1166 	}
1167 	return 0;
1168 }
1169