Sync usage with man page.
[netbsd-mini2440.git] / sys / fs / smbfs / smbfs_vnops.c
blob96ed3b1645d21a21be1c293812a7dd92bf8d59a0
1 /* $NetBSD: smbfs_vnops.c,v 1.73 2009/07/04 07:36:46 cegger Exp $ */
3 /*-
4 * Copyright (c) 2003 The NetBSD Foundation, Inc.
5 * All rights reserved.
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Jaromir Dolecek.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
33 * Copyright (c) 2000-2001 Boris Popov
34 * All rights reserved.
36 * Redistribution and use in source and binary forms, with or without
37 * modification, are permitted provided that the following conditions
38 * are met:
39 * 1. Redistributions of source code must retain the above copyright
40 * notice, this list of conditions and the following disclaimer.
41 * 2. Redistributions in binary form must reproduce the above copyright
42 * notice, this list of conditions and the following disclaimer in the
43 * documentation and/or other materials provided with the distribution.
44 * 3. All advertising materials mentioning features or use of this software
45 * must display the following acknowledgement:
46 * This product includes software developed by Boris Popov.
47 * 4. Neither the name of the author nor the names of any co-contributors
48 * may be used to endorse or promote products derived from this software
49 * without specific prior written permission.
51 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
52 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
53 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
54 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
55 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
56 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
57 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
58 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
59 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
60 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
61 * SUCH DAMAGE.
63 * FreeBSD: src/sys/fs/smbfs/smbfs_vnops.c,v 1.15 2001/12/20 15:56:45 bp Exp
66 #include <sys/cdefs.h>
67 __KERNEL_RCSID(0, "$NetBSD: smbfs_vnops.c,v 1.73 2009/07/04 07:36:46 cegger Exp $");
69 #include <sys/param.h>
70 #include <sys/systm.h>
71 #include <sys/namei.h>
72 #include <sys/kernel.h>
73 #include <sys/proc.h>
74 #include <sys/buf.h>
75 #include <sys/fcntl.h>
76 #include <sys/mount.h>
77 #include <sys/unistd.h>
78 #include <sys/vnode.h>
79 #include <sys/lockf.h>
80 #include <sys/kauth.h>
82 #include <machine/limits.h>
84 #include <uvm/uvm.h>
85 #include <uvm/uvm_extern.h>
87 #include <netsmb/smb.h>
88 #include <netsmb/smb_conn.h>
89 #include <netsmb/smb_subr.h>
91 #include <fs/smbfs/smbfs.h>
92 #include <fs/smbfs/smbfs_node.h>
93 #include <fs/smbfs/smbfs_subr.h>
95 #include <miscfs/genfs/genfs.h>
98 * Prototypes for SMBFS vnode operations
100 int smbfs_create(void *);
101 int smbfs_open(void *);
102 int smbfs_close(void *);
103 int smbfs_access(void *);
104 int smbfs_getattr(void *);
105 int smbfs_setattr(void *);
106 int smbfs_read(void *);
107 int smbfs_write(void *);
108 int smbfs_fsync(void *);
109 int smbfs_remove(void *);
110 int smbfs_link(void *);
111 int smbfs_lookup(void *);
112 int smbfs_rename(void *);
113 int smbfs_mkdir(void *);
114 int smbfs_rmdir(void *);
115 int smbfs_symlink(void *);
116 int smbfs_readdir(void *);
117 int smbfs_strategy(void *);
118 int smbfs_print(void *);
119 int smbfs_pathconf(void *ap);
120 int smbfs_advlock(void *);
122 int (**smbfs_vnodeop_p)(void *);
123 static struct vnodeopv_entry_desc smbfs_vnodeop_entries[] = {
124 { &vop_default_desc, vn_default_error },
125 { &vop_access_desc, smbfs_access },
126 { &vop_advlock_desc, smbfs_advlock },
127 { &vop_close_desc, smbfs_close },
128 { &vop_create_desc, smbfs_create },
129 { &vop_fsync_desc, smbfs_fsync },
130 { &vop_getattr_desc, smbfs_getattr },
131 { &vop_getpages_desc, genfs_compat_getpages },
132 { &vop_inactive_desc, smbfs_inactive },
133 { &vop_ioctl_desc, genfs_enoioctl },
134 { &vop_islocked_desc, genfs_islocked },
135 { &vop_link_desc, smbfs_link },
136 { &vop_lock_desc, genfs_lock },
137 { &vop_lookup_desc, smbfs_lookup },
138 { &vop_mkdir_desc, smbfs_mkdir },
139 { &vop_mknod_desc, genfs_eopnotsupp },
140 { &vop_open_desc, smbfs_open },
141 { &vop_pathconf_desc, smbfs_pathconf },
142 { &vop_print_desc, smbfs_print },
143 { &vop_putpages_desc, genfs_putpages },
144 { &vop_read_desc, smbfs_read },
145 { &vop_readdir_desc, smbfs_readdir },
146 { &vop_reclaim_desc, smbfs_reclaim },
147 { &vop_remove_desc, smbfs_remove },
148 { &vop_rename_desc, smbfs_rename },
149 { &vop_rmdir_desc, smbfs_rmdir },
150 { &vop_setattr_desc, smbfs_setattr },
151 { &vop_strategy_desc, smbfs_strategy },
152 { &vop_symlink_desc, smbfs_symlink },
153 { &vop_unlock_desc, genfs_unlock },
154 { &vop_write_desc, smbfs_write },
155 { &vop_mmap_desc, genfs_mmap }, /* mmap */
156 { &vop_seek_desc, genfs_seek }, /* seek */
157 { &vop_kqfilter_desc, smbfs_kqfilter}, /* kqfilter */
158 { NULL, NULL }
160 const struct vnodeopv_desc smbfs_vnodeop_opv_desc =
161 { &smbfs_vnodeop_p, smbfs_vnodeop_entries };
163 static int
164 smbfs_check_possible(struct vnode *vp, struct smbnode *np, mode_t mode)
168 * Disallow write attempts on read-only file systems;
169 * unless the file is a socket, fifo, or a block or
170 * character device resident on the file system.
172 if (mode & VWRITE) {
173 switch (vp->v_type) {
174 case VREG:
175 case VDIR:
176 case VLNK:
177 if (vp->v_mount->mnt_flag & MNT_RDONLY)
178 return EROFS;
179 default:
180 break;
184 return 0;
187 static int
188 smbfs_check_permitted(struct vnode *vp, struct smbnode *np, mode_t mode,
189 kauth_cred_t cred)
191 struct smbmount *smp = VTOSMBFS(vp);
193 return genfs_can_access(vp->v_type,
194 (vp->v_type == VDIR) ? smp->sm_args.dir_mode : smp->sm_args.file_mode,
195 smp->sm_args.uid, smp->sm_args.gid, mode, cred);
199 smbfs_access(void *v)
201 struct vop_access_args /* {
202 struct vnode *a_vp;
203 int a_mode;
204 kauth_cred_t a_cred;
205 } */ *ap = v;
206 struct vnode *vp = ap->a_vp;
207 struct smbnode *np = VTOSMB(vp);
208 u_int acc_mode = ap->a_mode;
209 int error;
210 #ifdef SMB_VNODE_DEBUG
211 struct smbmount *smp = VTOSMBFS(vp);
212 #endif
214 SMBVDEBUG("file '%.*s', node mode=%o, acc mode=%x\n",
215 (int) np->n_nmlen, np->n_name,
216 (vp->v_type == VDIR) ? smp->sm_args.dir_mode : smp->sm_args.file_mode,
217 acc_mode);
219 error = smbfs_check_possible(vp, np, acc_mode);
220 if (error)
221 return error;
223 error = smbfs_check_permitted(vp, np, acc_mode, ap->a_cred);
225 return error;
228 /* ARGSUSED */
230 smbfs_open(void *v)
232 struct vop_open_args /* {
233 struct vnode *a_vp;
234 int a_mode;
235 kauth_cred_t a_cred;
236 } */ *ap = v;
237 struct lwp *l = curlwp;
238 struct vnode *vp = ap->a_vp;
239 struct smbnode *np = VTOSMB(vp);
240 struct smb_cred scred;
241 struct vattr vattr;
242 u_int32_t sv_caps = SMB_CAPS(SSTOVC(np->n_mount->sm_share));
243 int error, accmode;
245 SMBVDEBUG("%.*s,%d\n", (int) np->n_nmlen, np->n_name,
246 (np->n_flag & NOPEN) != 0);
247 if (vp->v_type != VREG && vp->v_type != VDIR) {
248 SMBFSERR("open eacces vtype=%d\n", vp->v_type);
249 return EACCES;
251 if (vp->v_type == VDIR) {
252 np->n_flag |= NOPEN;
253 if ((sv_caps & SMB_CAP_NT_SMBS) == 0)
254 return 0;
255 goto do_open; /* skip 'modified' check */
258 if (np->n_flag & NMODIFIED) {
259 if ((error = smbfs_vinvalbuf(vp, V_SAVE, ap->a_cred, l, 1)) == EINTR)
260 return error;
261 smbfs_attr_cacheremove(vp);
262 error = VOP_GETATTR(vp, &vattr, ap->a_cred);
263 if (error)
264 return error;
265 np->n_mtime.tv_sec = vattr.va_mtime.tv_sec;
266 } else {
267 error = VOP_GETATTR(vp, &vattr, ap->a_cred);
268 if (error)
269 return error;
270 if (np->n_mtime.tv_sec != vattr.va_mtime.tv_sec) {
271 error = smbfs_vinvalbuf(vp, V_SAVE, ap->a_cred, l, 1);
272 if (error == EINTR)
273 return error;
274 np->n_mtime.tv_sec = vattr.va_mtime.tv_sec;
278 do_open:
279 if ((np->n_flag & NOPEN) != 0)
280 return 0;
282 smb_makescred(&scred, l, ap->a_cred);
283 if (vp->v_type == VDIR)
284 error = smbfs_smb_ntcreatex(np,
285 SMB_SM_DENYNONE|SMB_AM_OPENREAD, &scred);
286 else {
288 * Use DENYNONE to give unixy semantics of permitting
289 * everything not forbidden by permissions. Ie denial
290 * is up to server with clients/openers needing to use
291 * advisory locks for further control.
293 accmode = SMB_SM_DENYNONE|SMB_AM_OPENREAD;
294 if ((vp->v_mount->mnt_flag & MNT_RDONLY) == 0)
295 accmode = SMB_SM_DENYNONE|SMB_AM_OPENRW;
296 error = smbfs_smb_open(np, accmode, &scred);
297 if (error) {
298 if (ap->a_mode & FWRITE)
299 return EACCES;
301 error = smbfs_smb_open(np,
302 SMB_SM_DENYNONE|SMB_AM_OPENREAD, &scred);
305 if (!error)
306 np->n_flag |= NOPEN;
307 smbfs_attr_cacheremove(vp);
308 return error;
312 * Close called.
315 smbfs_close(void *v)
317 struct vop_close_args /* {
318 struct vnodeop_desc *a_desc;
319 struct vnode *a_vp;
320 int a_fflag;
321 kauth_cred_t a_cred;
322 } */ *ap = v;
323 int error;
324 struct lwp *l = curlwp;
325 struct vnode *vp = ap->a_vp;
326 struct smbnode *np = VTOSMB(vp);
328 /* Flush all file data */
329 error = smbfs_vinvalbuf(vp, V_SAVE, ap->a_cred, l, 1);
330 if (error)
331 return (error);
334 * We must close the directory lookup context now, so that
335 * later directory changes would be properly detected.
336 * Ideally, the lookup routines should handle such case, and
337 * the context would be removed only in smbfs_inactive().
339 if (vp->v_type == VDIR && (np->n_flag & NOPEN) != 0 &&
340 np->n_dirseq != NULL) {
341 struct smb_cred scred;
343 smb_makescred(&scred, l, ap->a_cred);
344 smbfs_findclose(np->n_dirseq, &scred);
345 np->n_dirseq = NULL;
348 return (0);
352 * smbfs_getattr call from vfs.
355 smbfs_getattr(void *v)
357 struct vop_getattr_args /* {
358 struct vnode *a_vp;
359 struct vattr *a_vap;
360 kauth_cred_t a_cred;
361 } */ *ap = v;
362 struct vnode *vp = ap->a_vp;
363 struct smbnode *np = VTOSMB(vp);
364 struct vattr *va=ap->a_vap;
365 struct smbfattr fattr;
366 struct smb_cred scred;
367 u_quad_t oldsize;
368 int error;
370 SMBVDEBUG("%p: '%.*s' isroot %d\n", vp,
371 (int) np->n_nmlen, np->n_name, (vp->v_vflag & VV_ROOT) != 0);
373 if ((error = smbfs_attr_cachelookup(vp, va)) == 0)
374 return (0);
376 SMBVDEBUG0("not in the cache\n");
377 smb_makescred(&scred, curlwp, ap->a_cred);
378 oldsize = np->n_size;
379 error = smbfs_smb_lookup(np, NULL, 0, &fattr, &scred);
380 if (error) {
381 SMBVDEBUG("error %d\n", error);
382 return error;
384 smbfs_attr_cacheenter(vp, &fattr);
385 smbfs_attr_cachelookup(vp, va);
386 if ((np->n_flag & NOPEN) != 0)
387 np->n_size = oldsize;
388 return 0;
392 smbfs_setattr(void *v)
394 struct vop_setattr_args /* {
395 struct vnode *a_vp;
396 struct vattr *a_vap;
397 kauth_cred_t a_cred;
398 } */ *ap = v;
399 struct lwp *l = curlwp;
400 struct vnode *vp = ap->a_vp;
401 struct smbnode *np = VTOSMB(vp);
402 struct vattr *vap = ap->a_vap;
403 struct timespec *mtime, *atime;
404 struct smb_cred scred;
405 struct smb_share *ssp = np->n_mount->sm_share;
406 struct smb_vc *vcp = SSTOVC(ssp);
407 u_quad_t tsize = 0;
408 int isreadonly, doclose, error = 0;
410 SMBVDEBUG0("\n");
411 if (vap->va_flags != VNOVAL)
412 return EOPNOTSUPP;
413 isreadonly = (vp->v_mount->mnt_flag & MNT_RDONLY);
415 * Disallow write attempts if the filesystem is mounted read-only.
417 if ((vap->va_uid != (uid_t)VNOVAL || vap->va_gid != (gid_t)VNOVAL ||
418 vap->va_atime.tv_sec != VNOVAL || vap->va_mtime.tv_sec != VNOVAL ||
419 vap->va_mode != (mode_t)VNOVAL) && isreadonly)
420 return EROFS;
421 smb_makescred(&scred, l, ap->a_cred);
422 if (vap->va_size != VNOVAL) {
423 switch (vp->v_type) {
424 case VDIR:
425 return EISDIR;
426 case VREG:
427 break;
428 default:
429 return EINVAL;
431 if (isreadonly)
432 return EROFS;
433 doclose = 0;
434 tsize = np->n_size;
435 np->n_size = vap->va_size;
436 uvm_vnp_setsize(vp, vap->va_size);
437 if ((np->n_flag & NOPEN) == 0) {
438 error = smbfs_smb_open(np,
439 SMB_SM_DENYNONE|SMB_AM_OPENRW, &scred);
440 if (error == 0)
441 doclose = 1;
443 if (error == 0)
444 error = smbfs_smb_setfsize(np, vap->va_size, &scred);
445 if (doclose)
446 smbfs_smb_close(ssp, np->n_fid, NULL, &scred);
447 if (error) {
448 np->n_size = tsize;
449 uvm_vnp_setsize(vp, tsize);
450 return (error);
453 mtime = atime = NULL;
454 if (vap->va_mtime.tv_sec != VNOVAL)
455 mtime = &vap->va_mtime;
456 if (vap->va_atime.tv_sec != VNOVAL)
457 atime = &vap->va_atime;
458 if (mtime != atime) {
459 error = genfs_can_chtimes(ap->a_vp, vap->va_vaflags,
460 VTOSMBFS(vp)->sm_args.uid, ap->a_cred);
461 if (error)
462 return (error);
464 #if 0
465 if (mtime == NULL)
466 mtime = &np->n_mtime;
467 if (atime == NULL)
468 atime = &np->n_atime;
469 #endif
471 * If file is opened, then we can use handle based calls.
472 * If not, use path based ones.
474 if ((np->n_flag & NOPEN) == 0) {
475 if (vcp->vc_flags & SMBV_WIN95) {
476 error = VOP_OPEN(vp, FWRITE, ap->a_cred);
477 if (!error) {
478 /* error = smbfs_smb_setfattrNT(np, 0, mtime, atime, &scred);
479 VOP_GETATTR(vp, &vattr, ap->a_cred);*/
480 if (mtime)
481 np->n_mtime = *mtime;
482 VOP_CLOSE(vp, FWRITE, ap->a_cred);
484 } else if (SMB_CAPS(vcp) & SMB_CAP_NT_SMBS) {
485 error = smbfs_smb_setptime2(np, mtime, atime, 0, &scred);
486 /* error = smbfs_smb_setpattrNT(np, 0, mtime, atime, &scred);*/
487 } else if (SMB_DIALECT(vcp) >= SMB_DIALECT_LANMAN2_0) {
488 error = smbfs_smb_setptime2(np, mtime, atime, 0, &scred);
489 } else {
490 error = smbfs_smb_setpattr(np, 0, mtime, &scred);
492 } else {
493 if (SMB_CAPS(vcp) & SMB_CAP_NT_SMBS) {
494 error = smbfs_smb_setfattrNT(np, 0, mtime, atime, &scred);
495 } else if (SMB_DIALECT(vcp) >= SMB_DIALECT_LANMAN1_0) {
496 error = smbfs_smb_setftime(np, mtime, atime, &scred);
497 } else {
499 * XXX I have no idea how to handle this for core
500 * level servers. The possible solution is to
501 * update mtime after file is closed.
507 * Invalidate attribute cache in case if server doesn't set
508 * required attributes.
510 smbfs_attr_cacheremove(vp); /* invalidate cache */
511 VOP_GETATTR(vp, vap, ap->a_cred);
512 np->n_mtime.tv_sec = vap->va_mtime.tv_sec;
513 VN_KNOTE(vp, NOTE_ATTRIB);
514 return error;
517 * smbfs_read call.
520 smbfs_read(void *v)
522 struct vop_read_args /* {
523 struct vnode *a_vp;
524 struct uio *a_uio;
525 int a_ioflag;
526 kauth_cred_t a_cred;
527 } */ *ap = v;
528 struct vnode *vp = ap->a_vp;
530 if (vp->v_type != VREG && vp->v_type != VDIR)
531 return EPERM;
533 return smbfs_readvnode(vp, ap->a_uio, ap->a_cred);
537 smbfs_write(void *v)
539 struct vop_write_args /* {
540 struct vnode *a_vp;
541 struct uio *a_uio;
542 int a_ioflag;
543 kauth_cred_t a_cred;
544 } */ *ap = v;
545 struct vnode *vp = ap->a_vp;
546 struct uio *uio = ap->a_uio;
548 SMBVDEBUG("%d,ofs=%lld,sz=%zu\n",vp->v_type,
549 (long long int)uio->uio_offset, uio->uio_resid);
550 if (vp->v_type != VREG)
551 return (EPERM);
552 return smbfs_writevnode(vp, uio, ap->a_cred, ap->a_ioflag);
555 * smbfs_create call
556 * Create a regular file. On entry the directory to contain the file being
557 * created is locked. We must release before we return. We must also free
558 * the pathname buffer pointed at by cnp->cn_pnbuf, always on error, or
559 * only if the SAVESTART bit in cn_flags is clear on success.
562 smbfs_create(void *v)
564 struct vop_create_args /* {
565 struct vnode *a_dvp;
566 struct vnode **a_vpp;
567 struct componentname *a_cnp;
568 struct vattr *a_vap;
569 } */ *ap = v;
570 struct vnode *dvp = ap->a_dvp;
571 struct vattr *vap = ap->a_vap;
572 struct componentname *cnp = ap->a_cnp;
573 struct smbnode *dnp = VTOSMB(dvp);
574 struct smbfattr fattr;
575 struct smb_cred scred;
576 const char *name = cnp->cn_nameptr;
577 int nmlen = cnp->cn_namelen;
578 int error = EINVAL;
581 if (vap->va_type != VREG)
582 goto out;
584 smb_makescred(&scred, curlwp, cnp->cn_cred);
585 error = smbfs_smb_create(dnp, name, nmlen, &scred);
586 if (error)
587 goto out;
589 error = smbfs_smb_lookup(dnp, name, nmlen, &fattr, &scred);
590 if (error)
591 goto out;
592 error = smbfs_nget(VTOVFS(dvp), dvp, name, nmlen, &fattr, ap->a_vpp);
593 if (error)
594 goto out;
596 /* No error */
597 if (cnp->cn_flags & MAKEENTRY)
598 cache_enter(dvp, *ap->a_vpp, cnp);
600 out:
601 if (error || ((cnp->cn_flags & SAVESTART) == 0))
602 PNBUF_PUT(cnp->cn_pnbuf);
603 VN_KNOTE(dvp, NOTE_WRITE);
604 vput(dvp);
605 return (error);
611 smbfs_remove(void *v)
613 struct vop_remove_args /* {
614 struct vnodeop_desc *a_desc;
615 struct vnode * a_dvp;
616 struct vnode * a_vp;
617 struct componentname * a_cnp;
618 } */ *ap = v;
619 struct vnode *vp = ap->a_vp;
620 struct vnode *dvp = ap->a_dvp;
621 struct componentname *cnp = ap->a_cnp;
622 struct smbnode *np = VTOSMB(vp);
623 struct smb_cred scred;
624 int error;
626 if (vp->v_type == VDIR || (np->n_flag & NOPEN) != 0
627 || vp->v_usecount != 1) {
628 /* XXX Eventually should do something along NFS sillyrename */
629 error = EPERM;
630 } else {
631 smb_makescred(&scred, curlwp, cnp->cn_cred);
632 error = smbfs_smb_delete(np, &scred);
635 VN_KNOTE(ap->a_vp, NOTE_DELETE);
636 VN_KNOTE(ap->a_dvp, NOTE_WRITE);
637 if (dvp == vp)
638 vrele(vp);
639 else
640 vput(vp);
641 vput(dvp);
642 return (error);
646 * smbfs_file rename call
649 smbfs_rename(void *v)
651 struct vop_rename_args /* {
652 struct vnode *a_fdvp;
653 struct vnode *a_fvp;
654 struct componentname *a_fcnp;
655 struct vnode *a_tdvp;
656 struct vnode *a_tvp;
657 struct componentname *a_tcnp;
658 } */ *ap = v;
659 struct vnode *fvp = ap->a_fvp;
660 struct vnode *tvp = ap->a_tvp;
661 struct vnode *fdvp = ap->a_fdvp;
662 struct vnode *tdvp = ap->a_tdvp;
663 struct componentname *tcnp = ap->a_tcnp;
664 /* struct componentname *fcnp = ap->a_fcnp;*/
665 struct smb_cred scred;
666 #ifdef notyet
667 u_int16_t flags = 6;
668 #endif
669 int error=0;
671 /* Check for cross-device rename */
672 if ((fvp->v_mount != tdvp->v_mount) ||
673 (tvp && (fvp->v_mount != tvp->v_mount))) {
674 error = EXDEV;
675 goto out;
678 if (tvp && tvp->v_usecount > 1) {
679 error = EBUSY;
680 goto out;
682 #ifdef notnow
683 flags = 0x10; /* verify all writes */
684 if (fvp->v_type == VDIR) {
685 flags |= 2;
686 } else if (fvp->v_type == VREG) {
687 flags |= 1;
688 } else {
689 error = EINVAL;
690 goto out;
692 #endif
693 smb_makescred(&scred, curlwp, tcnp->cn_cred);
695 * It seems that Samba doesn't implement SMB_COM_MOVE call...
697 #ifdef notnow
698 if (SMB_DIALECT(SSTOCN(smp->sm_share)) >= SMB_DIALECT_LANMAN1_0) {
699 error = smbfs_smb_move(VTOSMB(fvp), VTOSMB(tdvp),
700 tcnp->cn_nameptr, tcnp->cn_namelen, flags, &scred);
701 } else
702 #endif
705 * We have to do the work atomicaly
707 if (tvp && tvp != fvp) {
708 error = smbfs_smb_delete(VTOSMB(tvp), &scred);
709 if (error)
710 goto out;
711 VN_KNOTE(tdvp, NOTE_WRITE);
712 VN_KNOTE(tvp, NOTE_DELETE);
713 cache_purge(tvp);
715 error = smbfs_smb_rename(VTOSMB(fvp), VTOSMB(tdvp),
716 tcnp->cn_nameptr, tcnp->cn_namelen, &scred);
717 VN_KNOTE(fdvp, NOTE_WRITE);
718 VN_KNOTE(fvp, NOTE_RENAME);
721 if (fvp->v_type == VDIR) {
722 if (tvp != NULL && tvp->v_type == VDIR)
723 cache_purge(tdvp);
724 cache_purge(fdvp);
727 smbfs_attr_cacheremove(fdvp);
728 smbfs_attr_cacheremove(tdvp);
730 out:
732 if (tdvp == tvp)
733 vrele(tdvp);
734 else
735 vput(tdvp);
736 if (tvp)
737 vput(tvp);
739 vrele(fdvp);
740 vrele(fvp);
742 return (error);
746 * somtime it will come true...
749 smbfs_link(void *v)
751 return genfs_eopnotsupp(v);
755 * smbfs_symlink link create call.
756 * Sometime it will be functional...
759 smbfs_symlink(void *v)
761 return genfs_eopnotsupp(v);
765 smbfs_mkdir(void *v)
767 struct vop_mkdir_args /* {
768 struct vnode *a_dvp;
769 struct vnode **a_vpp;
770 struct componentname *a_cnp;
771 struct vattr *a_vap;
772 } */ *ap = v;
773 struct vnode *dvp = ap->a_dvp;
774 /* struct vattr *vap = ap->a_vap;*/
775 struct vnode *vp;
776 struct componentname *cnp = ap->a_cnp;
777 struct smbnode *dnp = VTOSMB(dvp);
778 struct smb_cred scred;
779 struct smbfattr fattr;
780 const char *name = cnp->cn_nameptr;
781 int len = cnp->cn_namelen;
782 int error;
784 if ((name[0] == '.') && ((len == 1) || ((len == 2) && (name[1] == '.')))){
785 error = EEXIST;
786 goto out;
789 smb_makescred(&scred, curlwp, cnp->cn_cred);
790 error = smbfs_smb_mkdir(dnp, name, len, &scred);
791 if (error)
792 goto out;
793 error = smbfs_smb_lookup(dnp, name, len, &fattr, &scred);
794 if (error)
795 goto out;
796 error = smbfs_nget(VTOVFS(dvp), dvp, name, len, &fattr, &vp);
797 if (error)
798 goto out;
799 *ap->a_vpp = vp;
801 out:
802 if (error || ((cnp->cn_flags & SAVESTART) == 0))
803 PNBUF_PUT(cnp->cn_pnbuf);
804 VN_KNOTE(dvp, NOTE_WRITE | NOTE_LINK);
805 vput(dvp);
807 return (error);
811 * smbfs_remove directory call
814 smbfs_rmdir(void *v)
816 struct vop_rmdir_args /* {
817 struct vnode *a_dvp;
818 struct vnode *a_vp;
819 struct componentname *a_cnp;
820 } */ *ap = v;
821 struct vnode *vp = ap->a_vp;
822 struct vnode *dvp = ap->a_dvp;
823 struct componentname *cnp = ap->a_cnp;
824 /* struct smbmount *smp = VTOSMBFS(vp);*/
825 struct smbnode *dnp = VTOSMB(dvp);
826 struct smbnode *np = VTOSMB(vp);
827 struct smb_cred scred;
828 int error;
830 if (dvp == vp) {
831 vrele(dvp);
832 vput(dvp);
833 return (EINVAL);
836 smb_makescred(&scred, curlwp, cnp->cn_cred);
837 error = smbfs_smb_rmdir(np, &scred);
838 dnp->n_flag |= NMODIFIED;
839 smbfs_attr_cacheremove(dvp);
840 VN_KNOTE(dvp, NOTE_WRITE | NOTE_LINK);
841 VN_KNOTE(vp, NOTE_DELETE);
842 cache_purge(dvp);
843 cache_purge(vp);
844 vput(vp);
845 vput(dvp);
847 return (error);
851 * smbfs_readdir call
854 smbfs_readdir(void *v)
856 struct vop_readdir_args /* {
857 struct vnode *a_vp;
858 struct uio *a_uio;
859 kauth_cred_t a_cred;
860 int *a_eofflag;
861 u_long *a_cookies;
862 int a_ncookies;
863 } */ *ap = v;
864 struct vnode *vp = ap->a_vp;
866 if (vp->v_type != VDIR)
867 return (EPERM);
868 #ifdef notnow
869 if (ap->a_ncookies) {
870 printf("smbfs_readdir: no support for cookies now...");
871 return (EOPNOTSUPP);
873 #endif
874 return (smbfs_readvnode(vp, ap->a_uio, ap->a_cred));
877 /* ARGSUSED */
879 smbfs_fsync(void *v)
881 /*return (smb_flush(ap->a_vp, ap->a_cred, ap->a_waitfor, ap->a_l, 1));*/
882 return (0);
886 smbfs_print(void *v)
888 struct vop_print_args /* {
889 struct vnode *a_vp;
890 } */ *ap = v;
891 struct vnode *vp = ap->a_vp;
892 struct smbnode *np = VTOSMB(vp);
894 printf("tag VT_SMBFS, name = %.*s, parent = %p, open = %d\n",
895 (int)np->n_nmlen, np->n_name,
896 np->n_parent ? np->n_parent : NULL,
897 (np->n_flag & NOPEN) != 0);
898 return (0);
902 smbfs_pathconf(void *v)
904 struct vop_pathconf_args /* {
905 struct vnode *vp;
906 int a_name;
907 register_t *a_retval;
908 } */ *ap = v;
909 register_t *retval = ap->a_retval;
910 int error = 0;
912 switch (ap->a_name) {
913 case _PC_PIPE_BUF:
914 *retval = PIPE_BUF;
915 break;
916 case _PC_SYNC_IO:
917 *retval = 1;
918 break;
919 case _PC_LINK_MAX:
920 *retval = 0;
921 break;
922 case _PC_NAME_MAX:
923 *retval = ap->a_vp->v_mount->mnt_stat.f_namemax;
924 break;
925 case _PC_PATH_MAX:
926 *retval = 800; /* XXX: a correct one ? */
927 break;
928 default:
929 error = EINVAL;
930 break;
933 return (error);
937 smbfs_strategy(void *v)
939 struct vop_strategy_args /* {
940 struct vnode *a_vp;
941 struct buf *a_bp;
942 } */ *ap = v;
943 struct buf *bp = ap->a_bp;
944 kauth_cred_t cr;
945 struct lwp *l;
946 int error = 0;
948 SMBVDEBUG0("\n");
949 if ((bp->b_flags & (B_PHYS|B_ASYNC)) == (B_PHYS|B_ASYNC))
950 panic("smbfs physio/async");
951 if (bp->b_flags & B_ASYNC) {
952 l = NULL;
953 cr = NULL;
954 } else {
955 l = curlwp; /* XXX */
956 cr = l->l_cred;
959 if ((bp->b_flags & B_ASYNC) == 0)
960 error = smbfs_doio(bp, cr, l);
962 return (error);
965 #ifndef __NetBSD__
966 static char smbfs_atl[] = "rhsvda";
967 static int
968 smbfs_getextattr(struct vop_getextattr_args *ap)
969 /* {
970 IN struct vnode *a_vp;
971 IN char *a_name;
972 INOUT struct uio *a_uio;
973 IN kauth_cred_t a_cred;
977 struct vnode *vp = ap->a_vp;
978 struct lwp *l = ap->a_l;
979 kauth_cred_t cred = ap->a_cred;
980 struct uio *uio = ap->a_uio;
981 const char *name = ap->a_name;
982 struct smbnode *np = VTOSMB(vp);
983 struct vattr vattr;
984 char buf[10];
985 int i, attr, error;
987 error = VOP_ACCESS(vp, VREAD, cred, td);
988 if (error)
989 return error;
990 error = VOP_GETATTR(vp, &vattr, cred, td);
991 if (error)
992 return error;
993 if (strcmp(name, "dosattr") == 0) {
994 attr = np->n_dosattr;
995 for (i = 0; i < 6; i++, attr >>= 1)
996 buf[i] = (attr & 1) ? smbfs_atl[i] : '-';
997 buf[i] = 0;
998 error = uiomove(buf, i, uio);
1000 } else
1001 error = EINVAL;
1002 return error;
1004 #endif /* !__NetBSD__ */
1007 * Since we expected to support F_GETLK (and SMB protocol has no such function),
1008 * it is necessary to use lf_advlock(). It would be nice if this function had
1009 * a callback mechanism because it will help to improve a level of consistency.
1012 smbfs_advlock(void *v)
1014 struct vop_advlock_args /* {
1015 struct vnode *a_vp;
1016 void *a_id;
1017 int a_op;
1018 struct flock *a_fl;
1019 int a_flags;
1020 } */ *ap = v;
1021 struct vnode *vp = ap->a_vp;
1022 struct smbnode *np = VTOSMB(vp);
1023 struct flock *fl = ap->a_fl;
1024 struct lwp *l = curlwp;
1025 struct smb_cred scred;
1026 u_quad_t size;
1027 off_t start, end, oadd;
1028 int error, lkop;
1030 if (vp->v_type == VDIR) {
1032 * SMB protocol have no support for directory locking.
1033 * Although locks can be processed on local machine, I don't
1034 * think that this is a good idea, because some programs
1035 * can work wrong assuming directory is locked. So, we just
1036 * return 'operation not supported'.
1038 return EOPNOTSUPP;
1040 size = np->n_size;
1041 switch (fl->l_whence) {
1043 case SEEK_SET:
1044 case SEEK_CUR:
1045 start = fl->l_start;
1046 break;
1048 case SEEK_END:
1049 #ifndef __NetBSD__
1050 if (size > OFF_MAX ||
1051 (fl->l_start > 0 && size > OFF_MAX - fl->l_start))
1052 return EOVERFLOW;
1053 #endif
1054 start = size + fl->l_start;
1055 break;
1057 default:
1058 return EINVAL;
1060 if (start < 0)
1061 return EINVAL;
1062 if (fl->l_len < 0) {
1063 if (start == 0)
1064 return EINVAL;
1065 end = start - 1;
1066 start += fl->l_len;
1067 if (start < 0)
1068 return EINVAL;
1069 } else if (fl->l_len == 0)
1070 end = -1;
1071 else {
1072 oadd = fl->l_len - 1;
1073 #ifndef __NetBSD__
1074 if (oadd > OFF_MAX - start)
1075 return EOVERFLOW;
1076 #endif
1077 end = start + oadd;
1079 smb_makescred(&scred, l, l ? l->l_cred : NULL);
1080 switch (ap->a_op) {
1081 case F_SETLK:
1082 switch (fl->l_type) {
1083 case F_WRLCK:
1084 lkop = SMB_LOCK_EXCL;
1085 break;
1086 case F_RDLCK:
1087 lkop = SMB_LOCK_SHARED;
1088 break;
1089 case F_UNLCK:
1090 lkop = SMB_LOCK_RELEASE;
1091 break;
1092 default:
1093 return EINVAL;
1095 error = lf_advlock(ap, &np->n_lockf, size);
1096 if (error)
1097 break;
1099 * The ID we use for smb_lock is passed as PID to SMB
1100 * server. It MUST agree with PID as setup in basic
1101 * SMB header in later write requests, otherwise SMB server
1102 * returns EDEADLK. See also smb_rq_new() on SMB header setup.
1104 error = smbfs_smb_lock(np, lkop,(void *)1, start, end, &scred);
1105 if (error) {
1106 ap->a_op = F_UNLCK;
1107 lf_advlock(ap, &np->n_lockf, size);
1109 break;
1110 case F_UNLCK:
1111 lf_advlock(ap, &np->n_lockf, size);
1112 error = smbfs_smb_lock(np, SMB_LOCK_RELEASE, ap->a_id, start, end, &scred);
1113 break;
1114 case F_GETLK:
1115 error = lf_advlock(ap, &np->n_lockf, size);
1116 break;
1117 default:
1118 return EINVAL;
1121 return error;
1124 static int
1125 smbfs_pathcheck(struct smbmount *smp, const char *name, int nmlen)
1127 static const char * const badchars = "*/\\:<>;?";
1128 static const char * const badchars83 = " +|,[]=";
1129 const char *cp;
1130 int i;
1132 if (SMB_DIALECT(SSTOVC(smp->sm_share)) < SMB_DIALECT_LANMAN2_0) {
1134 * Name should conform 8.3 format
1136 if (nmlen > 12)
1137 return (ENAMETOOLONG);
1139 if ((cp = memchr(name, '.', nmlen)) == NULL
1140 || cp == name || (cp - name) > 8
1141 || (cp = memchr(cp + 1, '.', nmlen - (cp - name))) != NULL)
1142 goto bad;
1144 for (cp = name, i = 0; i < nmlen; i++, cp++)
1145 if (strchr(badchars83, *cp) != NULL)
1146 goto bad;
1149 for (cp = name, i = 0; i < nmlen; i++, cp++)
1150 if (strchr(badchars, *cp) != NULL)
1151 goto bad;
1153 /* name is fine */
1154 return (0);
1156 bad:
1157 return (ENOENT);
1161 * Things go even weird without fixed inode numbers...
1164 smbfs_lookup(void *v)
1166 struct vop_lookup_args /* {
1167 struct vnode *a_dvp;
1168 struct vnode **a_vpp;
1169 struct componentname *a_cnp;
1170 } */ *ap = v;
1171 struct componentname *cnp = ap->a_cnp;
1172 struct vnode *dvp = ap->a_dvp;
1173 struct vnode **vpp = ap->a_vpp;
1174 struct mount *mp = dvp->v_mount;
1175 struct smbnode *dnp;
1176 struct smbfattr fattr;
1177 struct smb_cred scred;
1178 const char *name = cnp->cn_nameptr;
1179 int flags = cnp->cn_flags;
1180 int nameiop = cnp->cn_nameiop;
1181 int nmlen = cnp->cn_namelen;
1182 int error, islastcn, isdot;
1185 * Check accessiblity of directory.
1187 error = VOP_ACCESS(dvp, VEXEC, cnp->cn_cred);
1188 if (error)
1189 return (error);
1191 if ((cnp->cn_flags & ISLASTCN) &&
1192 (dvp->v_mount->mnt_flag & MNT_RDONLY) &&
1193 (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME))
1194 return (EROFS);
1196 SMBVDEBUG("%d '%.*s' in '%.*s'\n", nameiop, nmlen, name,
1197 (int) VTOSMB(dvp)->n_nmlen, VTOSMB(dvp)->n_name);
1199 islastcn = flags & ISLASTCN;
1202 * Before tediously performing a linear scan of the directory,
1203 * check the name cache to see if the directory/name pair
1204 * we are looking for is known already.
1205 * If the directory/name pair is found in the name cache,
1206 * we have to ensure the directory has not changed from
1207 * the time the cache entry has been created. If it has,
1208 * the cache entry has to be ignored.
1210 if ((error = cache_lookup(dvp, vpp, cnp)) >= 0) {
1211 struct vattr vattr;
1212 struct vnode *newvp;
1213 int err2;
1215 if (error && error != ENOENT) {
1216 *vpp = NULLVP;
1217 return error;
1220 err2 = VOP_ACCESS(dvp, VEXEC, cnp->cn_cred);
1221 if (err2 != 0) {
1222 if (error == 0) {
1223 if (*vpp != dvp)
1224 vput(*vpp);
1225 else
1226 vrele(*vpp);
1228 *vpp = NULLVP;
1229 return err2;
1232 if (error == ENOENT) {
1233 if (!VOP_GETATTR(dvp, &vattr, cnp->cn_cred)
1234 && vattr.va_mtime.tv_sec == VTOSMB(dvp)->n_nctime)
1235 return ENOENT;
1236 cache_purge(dvp);
1237 VTOSMB(dvp)->n_nctime = 0;
1238 goto dolookup;
1241 newvp = *vpp;
1242 if (!VOP_GETATTR(newvp, &vattr, cnp->cn_cred)
1243 && vattr.va_ctime.tv_sec == VTOSMB(newvp)->n_ctime)
1245 /* nfsstats.lookupcache_hits++; */
1246 if (cnp->cn_nameiop != LOOKUP && islastcn)
1247 cnp->cn_flags |= SAVENAME;
1249 return (0);
1252 cache_purge(newvp);
1253 if (newvp != dvp)
1254 vput(newvp);
1255 else
1256 vrele(newvp);
1257 *vpp = NULLVP;
1260 dolookup:
1262 /* ensure the name is sane */
1263 if (nameiop != LOOKUP) {
1264 error = smbfs_pathcheck(VFSTOSMBFS(mp), cnp->cn_nameptr,
1265 cnp->cn_namelen);
1266 if (error)
1267 return (error);
1270 dnp = VTOSMB(dvp);
1271 isdot = (nmlen == 1 && name[0] == '.');
1274 * entry is not in the cache or has been expired
1276 smb_makescred(&scred, curlwp, cnp->cn_cred);
1277 if (flags & ISDOTDOT)
1278 error = smbfs_smb_lookup(VTOSMB(dnp->n_parent), NULL, 0,
1279 &fattr, &scred);
1280 else
1281 error = smbfs_smb_lookup(dnp, name, nmlen, &fattr, &scred);
1283 if (error) {
1284 /* Not found */
1286 if (error != ENOENT)
1287 return (error);
1290 * Handle RENAME or CREATE case...
1292 if ((nameiop == CREATE || nameiop == RENAME) && islastcn) {
1294 * Access for write is interpreted as allowing
1295 * creation of files in the directory.
1297 error = VOP_ACCESS(dvp, VWRITE, cnp->cn_cred);
1298 if (error)
1299 return (error);
1301 cnp->cn_flags |= SAVENAME;
1302 return (EJUSTRETURN);
1306 * Insert name into cache (as non-existent) if appropriate.
1308 if ((cnp->cn_flags & MAKEENTRY) && nameiop != CREATE)
1309 cache_enter(dvp, *vpp, cnp);
1311 return (ENOENT);
1314 /* Found */
1316 /* Handle RENAME case... */
1317 if (nameiop == RENAME && islastcn) {
1318 error = VOP_ACCESS(dvp, VWRITE, cnp->cn_cred);
1319 if (error)
1320 return (error);
1322 if (isdot)
1323 return (EISDIR);
1324 if (flags & ISDOTDOT)
1325 VOP_UNLOCK(dvp, 0);
1326 error = smbfs_nget(mp, dvp, name, nmlen, &fattr, vpp);
1327 if (flags & ISDOTDOT)
1328 vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY);
1329 if (error)
1330 return (error);
1331 cnp->cn_flags |= SAVENAME;
1332 return (0);
1335 if (isdot) {
1338 * "." lookup
1340 vref(dvp);
1341 *vpp = dvp;
1342 } else if (flags & ISDOTDOT) {
1345 * ".." lookup
1347 VOP_UNLOCK(dvp, 0);
1348 error = smbfs_nget(mp, dvp, name, nmlen, NULL, vpp);
1349 vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY);
1350 if (error) {
1351 return error;
1353 } else {
1355 * Other lookups.
1357 error = smbfs_nget(mp, dvp, name, nmlen, &fattr, vpp);
1358 if (error)
1359 return error;
1362 if (cnp->cn_nameiop != LOOKUP && (flags & ISLASTCN))
1363 cnp->cn_flags |= SAVENAME;
1365 if ((cnp->cn_flags & MAKEENTRY)) {
1366 KASSERT(error == 0);
1367 if (cnp->cn_nameiop != DELETE || !islastcn) {
1368 VTOSMB(*vpp)->n_ctime = VTOSMB(*vpp)->n_mtime.tv_sec;
1369 cache_enter(dvp, *vpp, cnp);
1370 #ifdef notdef
1371 } else if (error == ENOENT && cnp->cn_nameiop != CREATE) {
1372 VTOSMB(*vpp)->n_nctime = VTOSMB(*vpp)->n_mtime.tv_sec;
1373 cache_enter(dvp, *vpp, cnp);
1374 #endif
1378 return (0);