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$
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 */
63 static vop_create_t smbfs_create;
64 static vop_mknod_t smbfs_mknod;
65 static vop_open_t smbfs_open;
66 static vop_close_t smbfs_close;
67 static vop_access_t smbfs_access;
68 static vop_getattr_t smbfs_getattr;
69 static vop_setattr_t smbfs_setattr;
70 static vop_read_t smbfs_read;
71 static vop_write_t smbfs_write;
72 static vop_fsync_t smbfs_fsync;
73 static vop_remove_t smbfs_remove;
74 static vop_link_t smbfs_link;
75 static vop_lookup_t smbfs_lookup;
76 static vop_rename_t smbfs_rename;
77 static vop_mkdir_t smbfs_mkdir;
78 static vop_rmdir_t smbfs_rmdir;
79 static vop_symlink_t smbfs_symlink;
80 static vop_readdir_t smbfs_readdir;
81 static vop_strategy_t smbfs_strategy;
82 static vop_print_t smbfs_print;
83 static vop_pathconf_t smbfs_pathconf;
84 static vop_advlock_t smbfs_advlock;
85 static vop_getextattr_t smbfs_getextattr;
86
87 struct vop_vector smbfs_vnodeops = {
88 .vop_default = &default_vnodeops,
89
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,
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,*/
116 .vop_strategy = smbfs_strategy,
117 .vop_symlink = smbfs_symlink,
118 .vop_write = smbfs_write,
119 };
120
121 static int
122 smbfs_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 */
150 static int
151 smbfs_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, ap->a_td)) == 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, ap->a_td);
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 vnode_create_vobject_off(ap->a_vp, vattr.va_size, ap->a_td);
218 }
219 smbfs_attr_cacheremove(vp);
220 return error;
221 }
222
223 static int
224 smbfs_close(ap)
225 struct vop_close_args /* {
226 struct vnodeop_desc *a_desc;
227 struct vnode *a_vp;
228 int a_fflag;
229 struct ucred *a_cred;
230 struct thread *a_td;
231 } */ *ap;
232 {
233 struct vnode *vp = ap->a_vp;
234 struct thread *td = ap->a_td;
235 struct smbnode *np = VTOSMB(vp);
236 struct smb_cred scred;
237
238 if (vp->v_type == VDIR && (np->n_flag & NOPEN) != 0 &&
239 np->n_dirseq != NULL) {
240 smb_makescred(&scred, td, ap->a_cred);
241 smbfs_findclose(np->n_dirseq, &scred);
242 np->n_dirseq = NULL;
243 }
244 return 0;
245 }
246
247 /*
248 * smbfs_getattr call from vfs.
249 */
250 static int
251 smbfs_getattr(ap)
252 struct vop_getattr_args /* {
253 struct vnode *a_vp;
254 struct vattr *a_vap;
255 struct ucred *a_cred;
256 struct thread *a_td;
257 } */ *ap;
258 {
259 struct vnode *vp = ap->a_vp;
260 struct smbnode *np = VTOSMB(vp);
261 struct vattr *va=ap->a_vap;
262 struct smbfattr fattr;
263 struct smb_cred scred;
264 u_quad_t oldsize;
265 int error;
266
267 SMBVDEBUG("%lx: '%s' %d\n", (long)vp, np->n_name, (vp->v_vflag & VV_ROOT) != 0);
268 error = smbfs_attr_cachelookup(vp, va);
269 if (!error)
270 return 0;
271 SMBVDEBUG("not in the cache\n");
272 smb_makescred(&scred, ap->a_td, ap->a_cred);
273 oldsize = np->n_size;
274 error = smbfs_smb_lookup(np, NULL, 0, &fattr, &scred);
275 if (error) {
276 SMBVDEBUG("error %d\n", error);
277 return error;
278 }
279 smbfs_attr_cacheenter(vp, &fattr);
280 smbfs_attr_cachelookup(vp, va);
281 if (np->n_flag & NOPEN)
282 np->n_size = oldsize;
283 return 0;
284 }
285
286 static int
287 smbfs_setattr(ap)
288 struct vop_setattr_args /* {
289 struct vnode *a_vp;
290 struct vattr *a_vap;
291 struct ucred *a_cred;
292 struct thread *a_td;
293 } */ *ap;
294 {
295 struct vnode *vp = ap->a_vp;
296 struct smbnode *np = VTOSMB(vp);
297 struct vattr *vap = ap->a_vap;
298 struct timespec *mtime, *atime;
299 struct smb_cred scred;
300 struct smb_share *ssp = np->n_mount->sm_share;
301 struct smb_vc *vcp = SSTOVC(ssp);
302 u_quad_t tsize = 0;
303 int isreadonly, doclose, error = 0;
304
305 SMBVDEBUG("\n");
306 if (vap->va_flags != VNOVAL)
307 return EOPNOTSUPP;
308 isreadonly = (vp->v_mount->mnt_flag & MNT_RDONLY);
309 /*
310 * Disallow write attempts if the filesystem is mounted read-only.
311 */
312 if ((vap->va_uid != (uid_t)VNOVAL || vap->va_gid != (gid_t)VNOVAL ||
313 vap->va_atime.tv_sec != VNOVAL || vap->va_mtime.tv_sec != VNOVAL ||
314 vap->va_mode != (mode_t)VNOVAL) && isreadonly)
315 return EROFS;
316 smb_makescred(&scred, ap->a_td, ap->a_cred);
317 if (vap->va_size != VNOVAL) {
318 switch (vp->v_type) {
319 case VDIR:
320 return EISDIR;
321 case VREG:
322 break;
323 default:
324 return EINVAL;
325 };
326 if (isreadonly)
327 return EROFS;
328 doclose = 0;
329 vnode_pager_setsize(vp, (u_long)vap->va_size);
330 tsize = np->n_size;
331 np->n_size = vap->va_size;
332 if ((np->n_flag & NOPEN) == 0) {
333 error = smbfs_smb_open(np,
334 SMB_SM_DENYNONE|SMB_AM_OPENRW,
335 &scred);
336 if (error == 0)
337 doclose = 1;
338 }
339 if (error == 0)
340 error = smbfs_smb_setfsize(np, vap->va_size, &scred);
341 if (doclose)
342 smbfs_smb_close(ssp, np->n_fid, NULL, &scred);
343 if (error) {
344 np->n_size = tsize;
345 vnode_pager_setsize(vp, (u_long)tsize);
346 return error;
347 }
348 }
349 mtime = atime = NULL;
350 if (vap->va_mtime.tv_sec != VNOVAL)
351 mtime = &vap->va_mtime;
352 if (vap->va_atime.tv_sec != VNOVAL)
353 atime = &vap->va_atime;
354 if (mtime != atime) {
355 if (ap->a_cred->cr_uid != VTOSMBFS(vp)->sm_uid &&
356 (error = suser_cred(ap->a_cred, SUSER_ALLOWJAIL)) &&
357 ((vap->va_vaflags & VA_UTIMES_NULL) == 0 ||
358 (error = VOP_ACCESS(vp, VWRITE, ap->a_cred, ap->a_td))))
359 return (error);
360 #if 0
361 if (mtime == NULL)
362 mtime = &np->n_mtime;
363 if (atime == NULL)
364 atime = &np->n_atime;
365 #endif
366 /*
367 * If file is opened, then we can use handle based calls.
368 * If not, use path based ones.
369 */
370 if ((np->n_flag & NOPEN) == 0) {
371 if (vcp->vc_flags & SMBV_WIN95) {
372 error = VOP_OPEN(vp, FWRITE, ap->a_cred, ap->a_td, -1);
373 if (!error) {
374 /* error = smbfs_smb_setfattrNT(np, 0, mtime, atime, &scred);
375 VOP_GETATTR(vp, &vattr, ap->a_cred, ap->a_td);*/
376 if (mtime)
377 np->n_mtime = *mtime;
378 VOP_CLOSE(vp, FWRITE, ap->a_cred, ap->a_td);
379 }
380 } else if ((vcp->vc_sopt.sv_caps & SMB_CAP_NT_SMBS)) {
381 error = smbfs_smb_setptime2(np, mtime, atime, 0, &scred);
382 /* error = smbfs_smb_setpattrNT(np, 0, mtime, atime, &scred);*/
383 } else if (SMB_DIALECT(vcp) >= SMB_DIALECT_LANMAN2_0) {
384 error = smbfs_smb_setptime2(np, mtime, atime, 0, &scred);
385 } else {
386 error = smbfs_smb_setpattr(np, 0, mtime, &scred);
387 }
388 } else {
389 if (vcp->vc_sopt.sv_caps & SMB_CAP_NT_SMBS) {
390 error = smbfs_smb_setfattrNT(np, 0, mtime, atime, &scred);
391 } else if (SMB_DIALECT(vcp) >= SMB_DIALECT_LANMAN1_0) {
392 error = smbfs_smb_setftime(np, mtime, atime, &scred);
393 } else {
394 /*
395 * I have no idea how to handle this for core
396 * level servers. The possible solution is to
397 * update mtime after file is closed.
398 */
399 SMBERROR("can't update times on an opened file\n");
400 }
401 }
402 }
403 /*
404 * Invalidate attribute cache in case if server doesn't set
405 * required attributes.
406 */
407 smbfs_attr_cacheremove(vp); /* invalidate cache */
408 VOP_GETATTR(vp, vap, ap->a_cred, ap->a_td);
409 np->n_mtime.tv_sec = vap->va_mtime.tv_sec;
410 return error;
411 }
412 /*
413 * smbfs_read call.
414 */
415 static int
416 smbfs_read(ap)
417 struct vop_read_args /* {
418 struct vnode *a_vp;
419 struct uio *a_uio;
420 int a_ioflag;
421 struct ucred *a_cred;
422 } */ *ap;
423 {
424 struct vnode *vp = ap->a_vp;
425 struct uio *uio = ap->a_uio;
426
427 SMBVDEBUG("\n");
428 if (vp->v_type != VREG && vp->v_type != VDIR)
429 return EPERM;
430 return smbfs_readvnode(vp, uio, ap->a_cred);
431 }
432
433 static int
434 smbfs_write(ap)
435 struct vop_write_args /* {
436 struct vnode *a_vp;
437 struct uio *a_uio;
438 int a_ioflag;
439 struct ucred *a_cred;
440 } */ *ap;
441 {
442 struct vnode *vp = ap->a_vp;
443 struct uio *uio = ap->a_uio;
444
445 SMBVDEBUG("%d,ofs=%d,sz=%d\n",vp->v_type, (int)uio->uio_offset, uio->uio_resid);
446 if (vp->v_type != VREG)
447 return (EPERM);
448 return smbfs_writevnode(vp, uio, ap->a_cred,ap->a_ioflag);
449 }
450 /*
451 * smbfs_create call
452 * Create a regular file. On entry the directory to contain the file being
453 * created is locked. We must release before we return. We must also free
454 * the pathname buffer pointed at by cnp->cn_pnbuf, always on error, or
455 * only if the SAVESTART bit in cn_flags is clear on success.
456 */
457 static int
458 smbfs_create(ap)
459 struct vop_create_args /* {
460 struct vnode *a_dvp;
461 struct vnode **a_vpp;
462 struct componentname *a_cnp;
463 struct vattr *a_vap;
464 } */ *ap;
465 {
466 struct vnode *dvp = ap->a_dvp;
467 struct vattr *vap = ap->a_vap;
468 struct vnode **vpp=ap->a_vpp;
469 struct componentname *cnp = ap->a_cnp;
470 struct smbnode *dnp = VTOSMB(dvp);
471 struct vnode *vp;
472 struct vattr vattr;
473 struct smbfattr fattr;
474 struct smb_cred scred;
475 char *name = cnp->cn_nameptr;
476 int nmlen = cnp->cn_namelen;
477 int error;
478
479
480 SMBVDEBUG("\n");
481 *vpp = NULL;
482 if (vap->va_type != VREG)
483 return EOPNOTSUPP;
484 if ((error = VOP_GETATTR(dvp, &vattr, cnp->cn_cred, cnp->cn_thread)))
485 return error;
486 smb_makescred(&scred, cnp->cn_thread, cnp->cn_cred);
487
488 error = smbfs_smb_create(dnp, name, nmlen, &scred);
489 if (error)
490 return error;
491 error = smbfs_smb_lookup(dnp, name, nmlen, &fattr, &scred);
492 if (error)
493 return error;
494 error = smbfs_nget(VTOVFS(dvp), dvp, name, nmlen, &fattr, &vp);
495 if (error)
496 return error;
497 *vpp = vp;
498 if (cnp->cn_flags & MAKEENTRY)
499 cache_enter(dvp, vp, cnp);
500 return error;
501 }
502
503 static int
504 smbfs_remove(ap)
505 struct vop_remove_args /* {
506 struct vnodeop_desc *a_desc;
507 struct vnode * a_dvp;
508 struct vnode * a_vp;
509 struct componentname * a_cnp;
510 } */ *ap;
511 {
512 struct vnode *vp = ap->a_vp;
513 /* struct vnode *dvp = ap->a_dvp;*/
514 struct componentname *cnp = ap->a_cnp;
515 struct smbnode *np = VTOSMB(vp);
516 struct smb_cred scred;
517 int error;
518
519 if (vp->v_type == VDIR || (np->n_flag & NOPEN) != 0 || vrefcnt(vp) != 1)
520 return EPERM;
521 smb_makescred(&scred, cnp->cn_thread, cnp->cn_cred);
522 error = smbfs_smb_delete(np, &scred);
523 if (error == 0)
524 np->n_flag |= NGONE;
525 cache_purge(vp);
526 return error;
527 }
528
529 /*
530 * smbfs_file rename call
531 */
532 static int
533 smbfs_rename(ap)
534 struct vop_rename_args /* {
535 struct vnode *a_fdvp;
536 struct vnode *a_fvp;
537 struct componentname *a_fcnp;
538 struct vnode *a_tdvp;
539 struct vnode *a_tvp;
540 struct componentname *a_tcnp;
541 } */ *ap;
542 {
543 struct vnode *fvp = ap->a_fvp;
544 struct vnode *tvp = ap->a_tvp;
545 struct vnode *fdvp = ap->a_fdvp;
546 struct vnode *tdvp = ap->a_tdvp;
547 struct componentname *tcnp = ap->a_tcnp;
548 /* struct componentname *fcnp = ap->a_fcnp;*/
549 struct smb_cred scred;
550 u_int16_t flags = 6;
551 int error=0;
552
553 /* Check for cross-device rename */
554 if ((fvp->v_mount != tdvp->v_mount) ||
555 (tvp && (fvp->v_mount != tvp->v_mount))) {
556 error = EXDEV;
557 goto out;
558 }
559
560 if (tvp && vrefcnt(tvp) > 1) {
561 error = EBUSY;
562 goto out;
563 }
564 flags = 0x10; /* verify all writes */
565 if (fvp->v_type == VDIR) {
566 flags |= 2;
567 } else if (fvp->v_type == VREG) {
568 flags |= 1;
569 } else {
570 error = EINVAL;
571 goto out;
572 }
573 smb_makescred(&scred, tcnp->cn_thread, tcnp->cn_cred);
574 /*
575 * It seems that Samba doesn't implement SMB_COM_MOVE call...
576 */
577 #ifdef notnow
578 if (SMB_DIALECT(SSTOCN(smp->sm_share)) >= SMB_DIALECT_LANMAN1_0) {
579 error = smbfs_smb_move(VTOSMB(fvp), VTOSMB(tdvp),
580 tcnp->cn_nameptr, tcnp->cn_namelen, flags, &scred);
581 } else
582 #endif
583 {
584 /*
585 * We have to do the work atomicaly
586 */
587 if (tvp && tvp != fvp) {
588 error = smbfs_smb_delete(VTOSMB(tvp), &scred);
589 if (error)
590 goto out_cacherem;
591 VTOSMB(fvp)->n_flag |= NGONE;
592 }
593 error = smbfs_smb_rename(VTOSMB(fvp), VTOSMB(tdvp),
594 tcnp->cn_nameptr, tcnp->cn_namelen, &scred);
595 }
596
597 if (fvp->v_type == VDIR) {
598 if (tvp != NULL && tvp->v_type == VDIR)
599 cache_purge(tdvp);
600 cache_purge(fdvp);
601 }
602
603 out_cacherem:
604 smbfs_attr_cacheremove(fdvp);
605 smbfs_attr_cacheremove(tdvp);
606 out:
607 if (tdvp == tvp)
608 vrele(tdvp);
609 else
610 vput(tdvp);
611 if (tvp)
612 vput(tvp);
613 vrele(fdvp);
614 vrele(fvp);
615 #ifdef possible_mistake
616 vgone(fvp);
617 if (tvp)
618 vgone(tvp);
619 #endif
620 return error;
621 }
622
623 /*
624 * somtime it will come true...
625 */
626 static int
627 smbfs_link(ap)
628 struct vop_link_args /* {
629 struct vnode *a_tdvp;
630 struct vnode *a_vp;
631 struct componentname *a_cnp;
632 } */ *ap;
633 {
634 return EOPNOTSUPP;
635 }
636
637 /*
638 * smbfs_symlink link create call.
639 * Sometime it will be functional...
640 */
641 static int
642 smbfs_symlink(ap)
643 struct vop_symlink_args /* {
644 struct vnode *a_dvp;
645 struct vnode **a_vpp;
646 struct componentname *a_cnp;
647 struct vattr *a_vap;
648 char *a_target;
649 } */ *ap;
650 {
651 return EOPNOTSUPP;
652 }
653
654 static int
655 smbfs_mknod(ap)
656 struct vop_mknod_args /* {
657 } */ *ap;
658 {
659 return EOPNOTSUPP;
660 }
661
662 static int
663 smbfs_mkdir(ap)
664 struct vop_mkdir_args /* {
665 struct vnode *a_dvp;
666 struct vnode **a_vpp;
667 struct componentname *a_cnp;
668 struct vattr *a_vap;
669 } */ *ap;
670 {
671 struct vnode *dvp = ap->a_dvp;
672 /* struct vattr *vap = ap->a_vap;*/
673 struct vnode *vp;
674 struct componentname *cnp = ap->a_cnp;
675 struct smbnode *dnp = VTOSMB(dvp);
676 struct vattr vattr;
677 struct smb_cred scred;
678 struct smbfattr fattr;
679 char *name = cnp->cn_nameptr;
680 int len = cnp->cn_namelen;
681 int error;
682
683 if ((error = VOP_GETATTR(dvp, &vattr, cnp->cn_cred, cnp->cn_thread))) {
684 return error;
685 }
686 if ((name[0] == '.') && ((len == 1) || ((len == 2) && (name[1] == '.'))))
687 return EEXIST;
688 smb_makescred(&scred, cnp->cn_thread, cnp->cn_cred);
689 error = smbfs_smb_mkdir(dnp, name, len, &scred);
690 if (error)
691 return error;
692 error = smbfs_smb_lookup(dnp, name, len, &fattr, &scred);
693 if (error)
694 return error;
695 error = smbfs_nget(VTOVFS(dvp), dvp, name, len, &fattr, &vp);
696 if (error)
697 return error;
698 *ap->a_vpp = vp;
699 return 0;
700 }
701
702 /*
703 * smbfs_remove directory call
704 */
705 static int
706 smbfs_rmdir(ap)
707 struct vop_rmdir_args /* {
708 struct vnode *a_dvp;
709 struct vnode *a_vp;
710 struct componentname *a_cnp;
711 } */ *ap;
712 {
713 struct vnode *vp = ap->a_vp;
714 struct vnode *dvp = ap->a_dvp;
715 struct componentname *cnp = ap->a_cnp;
716 /* struct smbmount *smp = VTOSMBFS(vp);*/
717 struct smbnode *dnp = VTOSMB(dvp);
718 struct smbnode *np = VTOSMB(vp);
719 struct smb_cred scred;
720 int error;
721
722 if (dvp == vp)
723 return EINVAL;
724
725 smb_makescred(&scred, cnp->cn_thread, cnp->cn_cred);
726 error = smbfs_smb_rmdir(np, &scred);
727 if (error == 0)
728 np->n_flag |= NGONE;
729 dnp->n_flag |= NMODIFIED;
730 smbfs_attr_cacheremove(dvp);
731 /* cache_purge(dvp);*/
732 cache_purge(vp);
733 return error;
734 }
735
736 /*
737 * smbfs_readdir call
738 */
739 static int
740 smbfs_readdir(ap)
741 struct vop_readdir_args /* {
742 struct vnode *a_vp;
743 struct uio *a_uio;
744 struct ucred *a_cred;
745 int *a_eofflag;
746 u_long *a_cookies;
747 int a_ncookies;
748 } */ *ap;
749 {
750 struct vnode *vp = ap->a_vp;
751 struct uio *uio = ap->a_uio;
752 int error;
753
754 if (vp->v_type != VDIR)
755 return (EPERM);
756 #ifdef notnow
757 if (ap->a_ncookies) {
758 printf("smbfs_readdir: no support for cookies now...");
759 return (EOPNOTSUPP);
760 }
761 #endif
762 error = smbfs_readvnode(vp, uio, ap->a_cred);
763 return error;
764 }
765
766 /* ARGSUSED */
767 static int
768 smbfs_fsync(ap)
769 struct vop_fsync_args /* {
770 struct vnodeop_desc *a_desc;
771 struct vnode * a_vp;
772 struct ucred * a_cred;
773 int a_waitfor;
774 struct thread * a_td;
775 } */ *ap;
776 {
777 /* return (smb_flush(ap->a_vp, ap->a_cred, ap->a_waitfor, ap->a_td, 1));*/
778 return (0);
779 }
780
781 static
782 int smbfs_print (ap)
783 struct vop_print_args /* {
784 struct vnode *a_vp;
785 } */ *ap;
786 {
787 struct vnode *vp = ap->a_vp;
788 struct smbnode *np = VTOSMB(vp);
789
790 if (np == NULL) {
791 printf("no smbnode data\n");
792 return (0);
793 }
794 printf("\tname = %s, parent = %p, open = %d\n", np->n_name,
795 np->n_parent ? np->n_parent : NULL, (np->n_flag & NOPEN) != 0);
796 return (0);
797 }
798
799 static int
800 smbfs_pathconf (ap)
801 struct vop_pathconf_args /* {
802 struct vnode *vp;
803 int name;
804 register_t *retval;
805 } */ *ap;
806 {
807 struct smbmount *smp = VFSTOSMBFS(VTOVFS(ap->a_vp));
808 struct smb_vc *vcp = SSTOVC(smp->sm_share);
809 register_t *retval = ap->a_retval;
810 int error = 0;
811
812 switch (ap->a_name) {
813 case _PC_LINK_MAX:
814 *retval = 0;
815 break;
816 case _PC_NAME_MAX:
817 *retval = (vcp->vc_hflags2 & SMB_FLAGS2_KNOWS_LONG_NAMES) ? 255 : 12;
818 break;
819 case _PC_PATH_MAX:
820 *retval = 800; /* XXX: a correct one ? */
821 break;
822 default:
823 error = EINVAL;
824 }
825 return error;
826 }
827
828 static int
829 smbfs_strategy (ap)
830 struct vop_strategy_args /* {
831 struct buf *a_bp
832 } */ *ap;
833 {
834 struct buf *bp=ap->a_bp;
835 struct ucred *cr;
836 struct thread *td;
837 int error = 0;
838
839 SMBVDEBUG("\n");
840 if (bp->b_flags & B_ASYNC)
841 td = (struct thread *)0;
842 else
843 td = curthread; /* XXX */
844 if (bp->b_iocmd == BIO_READ)
845 cr = bp->b_rcred;
846 else
847 cr = bp->b_wcred;
848
849 if ((bp->b_flags & B_ASYNC) == 0 )
850 error = smbfs_doio(ap->a_vp, bp, cr, td);
851 return error;
852 }
853
854 int
855 smbfs_ioctl(ap)
856 struct vop_ioctl_args /* {
857 struct vnode *a_vp;
858 u_long a_command;
859 caddr_t a_data;
860 int fflag;
861 struct ucred *cred;
862 struct thread *td;
863 } */ *ap;
864 {
865 return ENOTTY;
866 }
867
868 static char smbfs_atl[] = "rhsvda";
869 static int
870 smbfs_getextattr(struct vop_getextattr_args *ap)
871 /* {
872 IN struct vnode *a_vp;
873 IN char *a_name;
874 INOUT struct uio *a_uio;
875 IN struct ucred *a_cred;
876 IN struct thread *a_td;
877 };
878 */
879 {
880 struct vnode *vp = ap->a_vp;
881 struct thread *td = ap->a_td;
882 struct ucred *cred = ap->a_cred;
883 struct uio *uio = ap->a_uio;
884 const char *name = ap->a_name;
885 struct smbnode *np = VTOSMB(vp);
886 struct vattr vattr;
887 char buf[10];
888 int i, attr, error;
889
890 error = VOP_ACCESS(vp, VREAD, cred, td);
891 if (error)
892 return error;
893 error = VOP_GETATTR(vp, &vattr, cred, td);
894 if (error)
895 return error;
896 if (strcmp(name, "dosattr") == 0) {
897 attr = np->n_dosattr;
898 for (i = 0; i < 6; i++, attr >>= 1)
899 buf[i] = (attr & 1) ? smbfs_atl[i] : '-';
900 buf[i] = 0;
901 error = uiomove(buf, i, uio);
902
903 } else
904 error = EINVAL;
905 return error;
906 }
907
908 /*
909 * Since we expected to support F_GETLK (and SMB protocol has no such function),
910 * it is necessary to use lf_advlock(). It would be nice if this function had
911 * a callback mechanism because it will help to improve a level of consistency.
912 */
913 int
914 smbfs_advlock(ap)
915 struct vop_advlock_args /* {
916 struct vnode *a_vp;
917 caddr_t a_id;
918 int a_op;
919 struct flock *a_fl;
920 int a_flags;
921 } */ *ap;
922 {
923 struct vnode *vp = ap->a_vp;
924 struct smbnode *np = VTOSMB(vp);
925 struct flock *fl = ap->a_fl;
926 caddr_t id = (caddr_t)1 /* ap->a_id */;
927 /* int flags = ap->a_flags;*/
928 struct thread *td = curthread;
929 struct smb_cred scred;
930 u_quad_t size;
931 off_t start, end, oadd;
932 int error, lkop;
933
934 if (vp->v_type == VDIR) {
935 /*
936 * SMB protocol have no support for directory locking.
937 * Although locks can be processed on local machine, I don't
938 * think that this is a good idea, because some programs
939 * can work wrong assuming directory is locked. So, we just
940 * return 'operation not supported
941 */
942 return EOPNOTSUPP;
943 }
944 size = np->n_size;
945 switch (fl->l_whence) {
946
947 case SEEK_SET:
948 case SEEK_CUR:
949 start = fl->l_start;
950 break;
951
952 case SEEK_END:
953 if (size > OFF_MAX ||
954 (fl->l_start > 0 && size > OFF_MAX - fl->l_start))
955 return EOVERFLOW;
956 start = size + fl->l_start;
957 break;
958
959 default:
960 return EINVAL;
961 }
962 if (start < 0)
963 return EINVAL;
964 if (fl->l_len < 0) {
965 if (start == 0)
966 return EINVAL;
967 end = start - 1;
968 start += fl->l_len;
969 if (start < 0)
970 return EINVAL;
971 } else if (fl->l_len == 0)
972 end = -1;
973 else {
974 oadd = fl->l_len - 1;
975 if (oadd > OFF_MAX - start)
976 return EOVERFLOW;
977 end = start + oadd;
978 }
979 smb_makescred(&scred, td, td->td_ucred);
980 switch (ap->a_op) {
981 case F_SETLK:
982 switch (fl->l_type) {
983 case F_WRLCK:
984 lkop = SMB_LOCK_EXCL;
985 break;
986 case F_RDLCK:
987 lkop = SMB_LOCK_SHARED;
988 break;
989 case F_UNLCK:
990 lkop = SMB_LOCK_RELEASE;
991 break;
992 default:
993 return EINVAL;
994 }
995 error = lf_advlock(ap, &np->n_lockf, size);
996 if (error)
997 break;
998 lkop = SMB_LOCK_EXCL;
999 error = smbfs_smb_lock(np, lkop, id, start, end, &scred);
1000 if (error) {
1001 int oldtype = fl->l_type;
1002 fl->l_type = F_UNLCK;
1003 ap->a_op = F_UNLCK;
1004 lf_advlock(ap, &np->n_lockf, size);
1005 fl->l_type = oldtype;
1006 }
1007 break;
1008 case F_UNLCK:
1009 lf_advlock(ap, &np->n_lockf, size);
1010 error = smbfs_smb_lock(np, SMB_LOCK_RELEASE, id, start, end, &scred);
1011 break;
1012 case F_GETLK:
1013 error = lf_advlock(ap, &np->n_lockf, size);
1014 break;
1015 default:
1016 return EINVAL;
1017 }
1018 return error;
1019 }
1020
1021 static int
1022 smbfs_pathcheck(struct smbmount *smp, const char *name, int nmlen, int nameiop)
1023 {
1024 static const char *badchars = "*/:<>;?";
1025 static const char *badchars83 = " +|,[]=";
1026 const char *cp;
1027 int i, error;
1028
1029 /*
1030 * Backslash characters, being a path delimiter, are prohibited
1031 * within a path component even for LOOKUP operations.
1032 */
1033 if (index(name, '\\') != NULL)
1034 return ENOENT;
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 /*
1064 * Things go even weird without fixed inode numbers...
1065 */
1066 int
1067 smbfs_lookup(ap)
1068 struct vop_lookup_args /* {
1069 struct vnodeop_desc *a_desc;
1070 struct vnode *a_dvp;
1071 struct vnode **a_vpp;
1072 struct componentname *a_cnp;
1073 } */ *ap;
1074 {
1075 struct componentname *cnp = ap->a_cnp;
1076 struct thread *td = cnp->cn_thread;
1077 struct vnode *dvp = ap->a_dvp;
1078 struct vnode **vpp = ap->a_vpp;
1079 struct vnode *vp;
1080 struct smbmount *smp;
1081 struct mount *mp = dvp->v_mount;
1082 struct smbnode *dnp;
1083 struct smbfattr fattr, *fap;
1084 struct smb_cred scred;
1085 char *name = cnp->cn_nameptr;
1086 int flags = cnp->cn_flags;
1087 int nameiop = cnp->cn_nameiop;
1088 int nmlen = cnp->cn_namelen;
1089 int error, islastcn, isdot;
1090 int killit;
1091
1092 SMBVDEBUG("\n");
1093 if (dvp->v_type != VDIR)
1094 return ENOTDIR;
1095 if ((flags & ISDOTDOT) && (dvp->v_vflag & VV_ROOT)) {
1096 SMBFSERR("invalid '..'\n");
1097 return EIO;
1098 }
1099 #ifdef SMB_VNODE_DEBUG
1100 {
1101 char *cp, c;
1102
1103 cp = name + nmlen;
1104 c = *cp;
1105 *cp = 0;
1106 SMBVDEBUG("%d '%s' in '%s' id=d\n", nameiop, name,
1107 VTOSMB(dvp)->n_name);
1108 *cp = c;
1109 }
1110 #endif
1111 islastcn = flags & ISLASTCN;
1112 if (islastcn && (mp->mnt_flag & MNT_RDONLY) && (nameiop != LOOKUP))
1113 return EROFS;
1114 if ((error = VOP_ACCESS(dvp, VEXEC, cnp->cn_cred, td)) != 0)
1115 return error;
1116 smp = VFSTOSMBFS(mp);
1117 dnp = VTOSMB(dvp);
1118 isdot = (nmlen == 1 && name[0] == '.');
1119
1120 error = smbfs_pathcheck(smp, cnp->cn_nameptr, cnp->cn_namelen, nameiop);
1121
1122 if (error)
1123 return ENOENT;
1124
1125 error = cache_lookup(dvp, vpp, cnp);
1126 SMBVDEBUG("cache_lookup returned %d\n", error);
1127 if (error > 0)
1128 return error;
1129 if (error) { /* name was found */
1130 struct vattr vattr;
1131
1132 killit = 0;
1133 vp = *vpp;
1134 error = VOP_GETATTR(vp, &vattr, cnp->cn_cred, td);
1135 /*
1136 * If the file type on the server is inconsistent
1137 * with what it was when we created the vnode,
1138 * kill the bogus vnode now and fall through to
1139 * the code below to create a new one with the
1140 * right type.
1141 */
1142 if (error == 0 &&
1143 ((vp->v_type == VDIR &&
1144 (VTOSMB(vp)->n_dosattr & SMB_FA_DIR) == 0) ||
1145 (vp->v_type == VREG &&
1146 (VTOSMB(vp)->n_dosattr & SMB_FA_DIR) != 0)))
1147 killit = 1;
1148 else if (error == 0
1149 /* && vattr.va_ctime.tv_sec == VTOSMB(vp)->n_ctime*/) {
1150 if (nameiop != LOOKUP && islastcn)
1151 cnp->cn_flags |= SAVENAME;
1152 SMBVDEBUG("use cached vnode\n");
1153 return (0);
1154 }
1155 cache_purge(vp);
1156 /*
1157 * XXX This is not quite right, if '.' is
1158 * inconsistent, we really need to start the lookup
1159 * all over again. Hopefully there is some other
1160 * guarantee that prevents this case from happening.
1161 */
1162 if (killit && vp != dvp)
1163 vgone(vp);
1164 if (vp != dvp)
1165 vput(vp);
1166 else
1167 vrele(vp);
1168 *vpp = NULLVP;
1169 }
1170 /*
1171 * entry is not in the cache or has been expired
1172 */
1173 error = 0;
1174 *vpp = NULLVP;
1175 smb_makescred(&scred, td, cnp->cn_cred);
1176 fap = &fattr;
1177 if (flags & ISDOTDOT) {
1178 error = smbfs_smb_lookup(VTOSMB(dnp->n_parent), NULL, 0, fap,
1179 &scred);
1180 SMBVDEBUG("result of dotdot lookup: %d\n", error);
1181 } else {
1182 fap = &fattr;
1183 error = smbfs_smb_lookup(dnp, name, nmlen, fap, &scred);
1184 /* if (cnp->cn_namelen == 1 && cnp->cn_nameptr[0] == '.')*/
1185 SMBVDEBUG("result of smbfs_smb_lookup: %d\n", error);
1186 }
1187 if (error && error != ENOENT)
1188 return error;
1189 if (error) { /* entry not found */
1190 /*
1191 * Handle RENAME or CREATE case...
1192 */
1193 if ((nameiop == CREATE || nameiop == RENAME) && islastcn) {
1194 error = VOP_ACCESS(dvp, VWRITE, cnp->cn_cred, td);
1195 if (error)
1196 return error;
1197 cnp->cn_flags |= SAVENAME;
1198 return (EJUSTRETURN);
1199 }
1200 return ENOENT;
1201 }/* else {
1202 SMBVDEBUG("Found entry %s with id=%d\n", fap->entryName, fap->dirEntNum);
1203 }*/
1204 /*
1205 * handle DELETE case ...
1206 */
1207 if (nameiop == DELETE && islastcn) { /* delete last component */
1208 error = VOP_ACCESS(dvp, VWRITE, cnp->cn_cred, td);
1209 if (error)
1210 return error;
1211 if (isdot) {
1212 VREF(dvp);
1213 *vpp = dvp;
1214 return 0;
1215 }
1216 error = smbfs_nget(mp, dvp, name, nmlen, fap, &vp);
1217 if (error)
1218 return error;
1219 *vpp = vp;
1220 cnp->cn_flags |= SAVENAME;
1221 return 0;
1222 }
1223 if (nameiop == RENAME && islastcn) {
1224 error = VOP_ACCESS(dvp, VWRITE, cnp->cn_cred, td);
1225 if (error)
1226 return error;
1227 if (isdot)
1228 return EISDIR;
1229 error = smbfs_nget(mp, dvp, name, nmlen, fap, &vp);
1230 if (error)
1231 return error;
1232 *vpp = vp;
1233 cnp->cn_flags |= SAVENAME;
1234 return 0;
1235 }
1236 if (flags & ISDOTDOT) {
1237 VOP_UNLOCK(dvp, 0, td);
1238 error = smbfs_nget(mp, dvp, name, nmlen, NULL, &vp);
1239 vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY, td);
1240 if (error)
1241 return error;
1242 *vpp = vp;
1243 } else if (isdot) {
1244 vref(dvp);
1245 *vpp = dvp;
1246 } else {
1247 error = smbfs_nget(mp, dvp, name, nmlen, fap, &vp);
1248 if (error)
1249 return error;
1250 *vpp = vp;
1251 SMBVDEBUG("lookup: getnewvp!\n");
1252 }
1253 if ((cnp->cn_flags & MAKEENTRY)/* && !islastcn*/) {
1254 /* VTOSMB(*vpp)->n_ctime = VTOSMB(*vpp)->n_vattr.va_ctime.tv_sec;*/
1255 cache_enter(dvp, *vpp, cnp);
1256 }
1257 return 0;
1258 }
Cache object: 2d6b69d3dbce098937f8a77aada4f78e
|