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: releng/6.0/sys/fs/smbfs/smbfs_vnops.c 159118 2006-05-31 22:33:41Z cperciva $
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(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 ap->a_op = F_UNLCK;
1002 lf_advlock(ap, &np->n_lockf, size);
1003 }
1004 break;
1005 case F_UNLCK:
1006 lf_advlock(ap, &np->n_lockf, size);
1007 error = smbfs_smb_lock(np, SMB_LOCK_RELEASE, id, start, end, &scred);
1008 break;
1009 case F_GETLK:
1010 error = lf_advlock(ap, &np->n_lockf, size);
1011 break;
1012 default:
1013 return EINVAL;
1014 }
1015 return error;
1016 }
1017
1018 static int
1019 smbfs_pathcheck(struct smbmount *smp, const char *name, int nmlen, int nameiop)
1020 {
1021 static const char *badchars = "*/:<>;?";
1022 static const char *badchars83 = " +|,[]=";
1023 const char *cp;
1024 int i, error;
1025
1026 /*
1027 * Backslash characters, being a path delimiter, are prohibited
1028 * within a path component even for LOOKUP operations.
1029 */
1030 if (index(name, '\\') != NULL)
1031 return ENOENT;
1032
1033 if (nameiop == LOOKUP)
1034 return 0;
1035 error = ENOENT;
1036 if (SMB_DIALECT(SSTOVC(smp->sm_share)) < SMB_DIALECT_LANMAN2_0) {
1037 /*
1038 * Name should conform 8.3 format
1039 */
1040 if (nmlen > 12)
1041 return ENAMETOOLONG;
1042 cp = index(name, '.');
1043 if (cp == NULL)
1044 return error;
1045 if (cp == name || (cp - name) > 8)
1046 return error;
1047 cp = index(cp + 1, '.');
1048 if (cp != NULL)
1049 return error;
1050 for (cp = name, i = 0; i < nmlen; i++, cp++)
1051 if (index(badchars83, *cp) != NULL)
1052 return error;
1053 }
1054 for (cp = name, i = 0; i < nmlen; i++, cp++)
1055 if (index(badchars, *cp) != NULL)
1056 return error;
1057 return 0;
1058 }
1059
1060 /*
1061 * Things go even weird without fixed inode numbers...
1062 */
1063 int
1064 smbfs_lookup(ap)
1065 struct vop_lookup_args /* {
1066 struct vnodeop_desc *a_desc;
1067 struct vnode *a_dvp;
1068 struct vnode **a_vpp;
1069 struct componentname *a_cnp;
1070 } */ *ap;
1071 {
1072 struct componentname *cnp = ap->a_cnp;
1073 struct thread *td = cnp->cn_thread;
1074 struct vnode *dvp = ap->a_dvp;
1075 struct vnode **vpp = ap->a_vpp;
1076 struct vnode *vp;
1077 struct smbmount *smp;
1078 struct mount *mp = dvp->v_mount;
1079 struct smbnode *dnp;
1080 struct smbfattr fattr, *fap;
1081 struct smb_cred scred;
1082 char *name = cnp->cn_nameptr;
1083 int flags = cnp->cn_flags;
1084 int nameiop = cnp->cn_nameiop;
1085 int nmlen = cnp->cn_namelen;
1086 int error, islastcn, isdot;
1087 int killit;
1088
1089 SMBVDEBUG("\n");
1090 if (dvp->v_type != VDIR)
1091 return ENOTDIR;
1092 if ((flags & ISDOTDOT) && (dvp->v_vflag & VV_ROOT)) {
1093 SMBFSERR("invalid '..'\n");
1094 return EIO;
1095 }
1096 #ifdef SMB_VNODE_DEBUG
1097 {
1098 char *cp, c;
1099
1100 cp = name + nmlen;
1101 c = *cp;
1102 *cp = 0;
1103 SMBVDEBUG("%d '%s' in '%s' id=d\n", nameiop, name,
1104 VTOSMB(dvp)->n_name);
1105 *cp = c;
1106 }
1107 #endif
1108 islastcn = flags & ISLASTCN;
1109 if (islastcn && (mp->mnt_flag & MNT_RDONLY) && (nameiop != LOOKUP))
1110 return EROFS;
1111 if ((error = VOP_ACCESS(dvp, VEXEC, cnp->cn_cred, td)) != 0)
1112 return error;
1113 smp = VFSTOSMBFS(mp);
1114 dnp = VTOSMB(dvp);
1115 isdot = (nmlen == 1 && name[0] == '.');
1116
1117 error = smbfs_pathcheck(smp, cnp->cn_nameptr, cnp->cn_namelen, nameiop);
1118
1119 if (error)
1120 return ENOENT;
1121
1122 error = cache_lookup(dvp, vpp, cnp);
1123 SMBVDEBUG("cache_lookup returned %d\n", error);
1124 if (error > 0)
1125 return error;
1126 if (error) { /* name was found */
1127 struct vattr vattr;
1128
1129 killit = 0;
1130 vp = *vpp;
1131 error = VOP_GETATTR(vp, &vattr, cnp->cn_cred, td);
1132 /*
1133 * If the file type on the server is inconsistent
1134 * with what it was when we created the vnode,
1135 * kill the bogus vnode now and fall through to
1136 * the code below to create a new one with the
1137 * right type.
1138 */
1139 if (error == 0 &&
1140 ((vp->v_type == VDIR &&
1141 (VTOSMB(vp)->n_dosattr & SMB_FA_DIR) == 0) ||
1142 (vp->v_type == VREG &&
1143 (VTOSMB(vp)->n_dosattr & SMB_FA_DIR) != 0)))
1144 killit = 1;
1145 else if (error == 0
1146 /* && vattr.va_ctime.tv_sec == VTOSMB(vp)->n_ctime*/) {
1147 if (nameiop != LOOKUP && islastcn)
1148 cnp->cn_flags |= SAVENAME;
1149 SMBVDEBUG("use cached vnode\n");
1150 return (0);
1151 }
1152 cache_purge(vp);
1153 /*
1154 * XXX This is not quite right, if '.' is
1155 * inconsistent, we really need to start the lookup
1156 * all over again. Hopefully there is some other
1157 * guarantee that prevents this case from happening.
1158 */
1159 if (killit && vp != dvp)
1160 vgone(vp);
1161 if (vp != dvp)
1162 vput(vp);
1163 else
1164 vrele(vp);
1165 *vpp = NULLVP;
1166 }
1167 /*
1168 * entry is not in the cache or has been expired
1169 */
1170 error = 0;
1171 *vpp = NULLVP;
1172 smb_makescred(&scred, td, cnp->cn_cred);
1173 fap = &fattr;
1174 if (flags & ISDOTDOT) {
1175 error = smbfs_smb_lookup(VTOSMB(dnp->n_parent), NULL, 0, fap,
1176 &scred);
1177 SMBVDEBUG("result of dotdot lookup: %d\n", error);
1178 } else {
1179 fap = &fattr;
1180 error = smbfs_smb_lookup(dnp, name, nmlen, fap, &scred);
1181 /* if (cnp->cn_namelen == 1 && cnp->cn_nameptr[0] == '.')*/
1182 SMBVDEBUG("result of smbfs_smb_lookup: %d\n", error);
1183 }
1184 if (error && error != ENOENT)
1185 return error;
1186 if (error) { /* entry not found */
1187 /*
1188 * Handle RENAME or CREATE case...
1189 */
1190 if ((nameiop == CREATE || nameiop == RENAME) && islastcn) {
1191 error = VOP_ACCESS(dvp, VWRITE, cnp->cn_cred, td);
1192 if (error)
1193 return error;
1194 cnp->cn_flags |= SAVENAME;
1195 return (EJUSTRETURN);
1196 }
1197 return ENOENT;
1198 }/* else {
1199 SMBVDEBUG("Found entry %s with id=%d\n", fap->entryName, fap->dirEntNum);
1200 }*/
1201 /*
1202 * handle DELETE case ...
1203 */
1204 if (nameiop == DELETE && islastcn) { /* delete last component */
1205 error = VOP_ACCESS(dvp, VWRITE, cnp->cn_cred, td);
1206 if (error)
1207 return error;
1208 if (isdot) {
1209 VREF(dvp);
1210 *vpp = dvp;
1211 return 0;
1212 }
1213 error = smbfs_nget(mp, dvp, name, nmlen, fap, &vp);
1214 if (error)
1215 return error;
1216 *vpp = vp;
1217 cnp->cn_flags |= SAVENAME;
1218 return 0;
1219 }
1220 if (nameiop == RENAME && islastcn) {
1221 error = VOP_ACCESS(dvp, VWRITE, cnp->cn_cred, td);
1222 if (error)
1223 return error;
1224 if (isdot)
1225 return EISDIR;
1226 error = smbfs_nget(mp, dvp, name, nmlen, fap, &vp);
1227 if (error)
1228 return error;
1229 *vpp = vp;
1230 cnp->cn_flags |= SAVENAME;
1231 return 0;
1232 }
1233 if (flags & ISDOTDOT) {
1234 VOP_UNLOCK(dvp, 0, td);
1235 error = smbfs_nget(mp, dvp, name, nmlen, NULL, &vp);
1236 vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY, td);
1237 if (error)
1238 return error;
1239 *vpp = vp;
1240 } else if (isdot) {
1241 vref(dvp);
1242 *vpp = dvp;
1243 } else {
1244 error = smbfs_nget(mp, dvp, name, nmlen, fap, &vp);
1245 if (error)
1246 return error;
1247 *vpp = vp;
1248 SMBVDEBUG("lookup: getnewvp!\n");
1249 }
1250 if ((cnp->cn_flags & MAKEENTRY)/* && !islastcn*/) {
1251 /* VTOSMB(*vpp)->n_ctime = VTOSMB(*vpp)->n_vattr.va_ctime.tv_sec;*/
1252 cache_enter(dvp, *vpp, cnp);
1253 }
1254 return 0;
1255 }
Cache object: 98d120e40c8a121786b591010ea28f4f
|