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