]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - sys/fs/smbfs/smbfs_vnops.c
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.2.git] / sys / fs / smbfs / smbfs_vnops.c
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  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  * $FreeBSD$
27  */
28 #include <sys/param.h>
29 #include <sys/systm.h>
30 #include <sys/namei.h>
31 #include <sys/kernel.h>
32 #include <sys/proc.h>
33 #include <sys/bio.h>
34 #include <sys/buf.h>
35 #include <sys/fcntl.h>
36 #include <sys/mount.h>
37 #include <sys/unistd.h>
38 #include <sys/vnode.h>
39 #include <sys/limits.h>
40 #include <sys/lockf.h>
41 #include <sys/stat.h>
42
43 #include <vm/vm.h>
44 #include <vm/vm_extern.h>
45
46
47 #include <netsmb/smb.h>
48 #include <netsmb/smb_conn.h>
49 #include <netsmb/smb_subr.h>
50
51 #include <fs/smbfs/smbfs.h>
52 #include <fs/smbfs/smbfs_node.h>
53 #include <fs/smbfs/smbfs_subr.h>
54
55 /*
56  * Prototypes for SMBFS vnode operations
57  */
58 static vop_create_t     smbfs_create;
59 static vop_mknod_t      smbfs_mknod;
60 static vop_open_t       smbfs_open;
61 static vop_close_t      smbfs_close;
62 static vop_access_t     smbfs_access;
63 static vop_getattr_t    smbfs_getattr;
64 static vop_setattr_t    smbfs_setattr;
65 static vop_read_t       smbfs_read;
66 static vop_write_t      smbfs_write;
67 static vop_fsync_t      smbfs_fsync;
68 static vop_remove_t     smbfs_remove;
69 static vop_link_t       smbfs_link;
70 static vop_lookup_t     smbfs_lookup;
71 static vop_rename_t     smbfs_rename;
72 static vop_mkdir_t      smbfs_mkdir;
73 static vop_rmdir_t      smbfs_rmdir;
74 static vop_symlink_t    smbfs_symlink;
75 static vop_readdir_t    smbfs_readdir;
76 static vop_strategy_t   smbfs_strategy;
77 static vop_print_t      smbfs_print;
78 static vop_pathconf_t   smbfs_pathconf;
79 static vop_advlock_t    smbfs_advlock;
80 static vop_getextattr_t smbfs_getextattr;
81
82 struct vop_vector smbfs_vnodeops = {
83         .vop_default =          &default_vnodeops,
84
85         .vop_access =           smbfs_access,
86         .vop_advlock =          smbfs_advlock,
87         .vop_close =            smbfs_close,
88         .vop_create =           smbfs_create,
89         .vop_fsync =            smbfs_fsync,
90         .vop_getattr =          smbfs_getattr,
91         .vop_getextattr =       smbfs_getextattr,
92         .vop_getpages =         smbfs_getpages,
93         .vop_inactive =         smbfs_inactive,
94         .vop_ioctl =            smbfs_ioctl,
95         .vop_link =             smbfs_link,
96         .vop_lookup =           smbfs_lookup,
97         .vop_mkdir =            smbfs_mkdir,
98         .vop_mknod =            smbfs_mknod,
99         .vop_open =             smbfs_open,
100         .vop_pathconf =         smbfs_pathconf,
101         .vop_print =            smbfs_print,
102         .vop_putpages =         smbfs_putpages,
103         .vop_read =             smbfs_read,
104         .vop_readdir =          smbfs_readdir,
105         .vop_reclaim =          smbfs_reclaim,
106         .vop_remove =           smbfs_remove,
107         .vop_rename =           smbfs_rename,
108         .vop_rmdir =            smbfs_rmdir,
109         .vop_setattr =          smbfs_setattr,
110 /*      .vop_setextattr =       smbfs_setextattr,*/
111         .vop_strategy =         smbfs_strategy,
112         .vop_symlink =          smbfs_symlink,
113         .vop_write =            smbfs_write,
114 };
115
116 static int
117 smbfs_access(ap)
118         struct vop_access_args /* {
119                 struct vnode *a_vp;
120                 accmode_t a_accmode;
121                 struct ucred *a_cred;
122                 struct thread *a_td;
123         } */ *ap;
124 {
125         struct vnode *vp = ap->a_vp;
126         accmode_t accmode = ap->a_accmode;
127         mode_t mpmode;
128         struct smbmount *smp = VTOSMBFS(vp);
129
130         SMBVDEBUG("\n");
131         if ((accmode & VWRITE) && (vp->v_mount->mnt_flag & MNT_RDONLY)) {
132                 switch (vp->v_type) {
133                     case VREG: case VDIR: case VLNK:
134                         return EROFS;
135                     default:
136                         break;
137                 }
138         }
139         mpmode = vp->v_type == VREG ? smp->sm_file_mode : smp->sm_dir_mode;
140         return (vaccess(vp->v_type, mpmode, smp->sm_uid,
141             smp->sm_gid, ap->a_accmode, ap->a_cred, NULL));
142 }
143
144 /* ARGSUSED */
145 static int
146 smbfs_open(ap)
147         struct vop_open_args /* {
148                 struct vnode *a_vp;
149                 int  a_mode;
150                 struct ucred *a_cred;
151                 struct thread *a_td;
152         } */ *ap;
153 {
154         struct vnode *vp = ap->a_vp;
155         struct smbnode *np = VTOSMB(vp);
156         struct smb_cred scred;
157         struct vattr vattr;
158         int mode = ap->a_mode;
159         int error, accmode;
160
161         SMBVDEBUG("%s,%d\n", np->n_name, (np->n_flag & NOPEN) != 0);
162         if (vp->v_type != VREG && vp->v_type != VDIR) { 
163                 SMBFSERR("open eacces vtype=%d\n", vp->v_type);
164                 return EACCES;
165         }
166         if (vp->v_type == VDIR) {
167                 np->n_flag |= NOPEN;
168                 return 0;
169         }
170         if (np->n_flag & NMODIFIED) {
171                 if ((error = smbfs_vinvalbuf(vp, ap->a_td)) == EINTR)
172                         return error;
173                 smbfs_attr_cacheremove(vp);
174                 error = VOP_GETATTR(vp, &vattr, ap->a_cred);
175                 if (error)
176                         return error;
177                 np->n_mtime.tv_sec = vattr.va_mtime.tv_sec;
178         } else {
179                 error = VOP_GETATTR(vp, &vattr, ap->a_cred);
180                 if (error)
181                         return error;
182                 if (np->n_mtime.tv_sec != vattr.va_mtime.tv_sec) {
183                         error = smbfs_vinvalbuf(vp, ap->a_td);
184                         if (error == EINTR)
185                                 return error;
186                         np->n_mtime.tv_sec = vattr.va_mtime.tv_sec;
187                 }
188         }
189         if ((np->n_flag & NOPEN) != 0)
190                 return 0;
191         /*
192          * Use DENYNONE to give unixy semantics of permitting
193          * everything not forbidden by permissions.  Ie denial
194          * is up to server with clients/openers needing to use
195          * advisory locks for further control.
196          */
197         accmode = SMB_SM_DENYNONE|SMB_AM_OPENREAD;
198         if ((vp->v_mount->mnt_flag & MNT_RDONLY) == 0)
199                 accmode = SMB_SM_DENYNONE|SMB_AM_OPENRW;
200         smb_makescred(&scred, ap->a_td, ap->a_cred);
201         error = smbfs_smb_open(np, accmode, &scred);
202         if (error) {
203                 if (mode & FWRITE)
204                         return EACCES;
205                 else if ((vp->v_mount->mnt_flag & MNT_RDONLY) == 0) {
206                         accmode = SMB_SM_DENYNONE|SMB_AM_OPENREAD;
207                         error = smbfs_smb_open(np, accmode, &scred);
208                 }
209         }
210         if (error == 0) {
211                 np->n_flag |= NOPEN;
212                 vnode_create_vobject(ap->a_vp, vattr.va_size, ap->a_td);
213         }
214         smbfs_attr_cacheremove(vp);
215         return error;
216 }
217
218 static int
219 smbfs_close(ap)
220         struct vop_close_args /* {
221                 struct vnodeop_desc *a_desc;
222                 struct vnode *a_vp;
223                 int  a_fflag;
224                 struct ucred *a_cred;
225                 struct thread *a_td;
226         } */ *ap;
227 {
228         struct vnode *vp = ap->a_vp;
229         struct thread *td = ap->a_td;
230         struct smbnode *np = VTOSMB(vp);
231         struct smb_cred scred;
232
233         if (vp->v_type == VDIR && (np->n_flag & NOPEN) != 0 &&
234             np->n_dirseq != NULL) {
235                 smb_makescred(&scred, td, ap->a_cred);
236                 smbfs_findclose(np->n_dirseq, &scred);
237                 np->n_dirseq = NULL;
238         }
239         return 0;
240 }
241
242 /*
243  * smbfs_getattr call from vfs.
244  */
245 static int
246 smbfs_getattr(ap)
247         struct vop_getattr_args /* {
248                 struct vnode *a_vp;
249                 struct vattr *a_vap;
250                 struct ucred *a_cred;
251         } */ *ap;
252 {
253         struct vnode *vp = ap->a_vp;
254         struct smbnode *np = VTOSMB(vp);
255         struct vattr *va=ap->a_vap;
256         struct smbfattr fattr;
257         struct smb_cred scred;
258         u_quad_t oldsize;
259         int error;
260
261         SMBVDEBUG("%lx: '%s' %d\n", (long)vp, np->n_name, (vp->v_vflag & VV_ROOT) != 0);
262         error = smbfs_attr_cachelookup(vp, va);
263         if (!error)
264                 return 0;
265         SMBVDEBUG("not in the cache\n");
266         smb_makescred(&scred, curthread, ap->a_cred);
267         oldsize = np->n_size;
268         error = smbfs_smb_lookup(np, NULL, 0, &fattr, &scred);
269         if (error) {
270                 SMBVDEBUG("error %d\n", error);
271                 return error;
272         }
273         smbfs_attr_cacheenter(vp, &fattr);
274         smbfs_attr_cachelookup(vp, va);
275         if (np->n_flag & NOPEN)
276                 np->n_size = oldsize;
277         return 0;
278 }
279
280 static int
281 smbfs_setattr(ap)
282         struct vop_setattr_args /* {
283                 struct vnode *a_vp;
284                 struct vattr *a_vap;
285                 struct ucred *a_cred;
286         } */ *ap;
287 {
288         struct vnode *vp = ap->a_vp;
289         struct smbnode *np = VTOSMB(vp);
290         struct vattr *vap = ap->a_vap;
291         struct timespec *mtime, *atime;
292         struct smb_cred scred;
293         struct smb_share *ssp = np->n_mount->sm_share;
294         struct smb_vc *vcp = SSTOVC(ssp);
295         struct thread *td = curthread;
296         u_quad_t tsize = 0;
297         int isreadonly, doclose, error = 0;
298         int old_n_dosattr;
299
300         SMBVDEBUG("\n");
301         if (vap->va_flags != VNOVAL)
302                 return EOPNOTSUPP;
303         isreadonly = (vp->v_mount->mnt_flag & MNT_RDONLY);
304         /*
305          * Disallow write attempts if the filesystem is mounted read-only.
306          */
307         if ((vap->va_uid != (uid_t)VNOVAL || vap->va_gid != (gid_t)VNOVAL || 
308              vap->va_atime.tv_sec != VNOVAL || vap->va_mtime.tv_sec != VNOVAL ||
309              vap->va_mode != (mode_t)VNOVAL) && isreadonly)
310                 return EROFS;
311         smb_makescred(&scred, td, ap->a_cred);
312         if (vap->va_size != VNOVAL) {
313                 switch (vp->v_type) {
314                     case VDIR:
315                         return EISDIR;
316                     case VREG:
317                         break;
318                     default:
319                         return EINVAL;
320                 };
321                 if (isreadonly)
322                         return EROFS;
323                 doclose = 0;
324                 vnode_pager_setsize(vp, (u_long)vap->va_size);
325                 tsize = np->n_size;
326                 np->n_size = vap->va_size;
327                 if ((np->n_flag & NOPEN) == 0) {
328                         error = smbfs_smb_open(np,
329                                                SMB_SM_DENYNONE|SMB_AM_OPENRW,
330                                                &scred);
331                         if (error == 0)
332                                 doclose = 1;
333                 }
334                 if (error == 0)
335                         error = smbfs_smb_setfsize(np, vap->va_size, &scred);
336                 if (doclose)
337                         smbfs_smb_close(ssp, np->n_fid, NULL, &scred);
338                 if (error) {
339                         np->n_size = tsize;
340                         vnode_pager_setsize(vp, (u_long)tsize);
341                         return error;
342                 }
343         }
344         if (vap->va_mode != (mode_t)VNOVAL) {
345                 old_n_dosattr = np->n_dosattr;
346                 if (vap->va_mode & S_IWUSR)
347                         np->n_dosattr &= ~SMB_FA_RDONLY;
348                 else
349                         np->n_dosattr |= SMB_FA_RDONLY;
350                 if (np->n_dosattr != old_n_dosattr) {
351                         error = smbfs_smb_setpattr(np, np->n_dosattr, NULL, &scred);
352                         if (error)
353                                 return error;
354                 }
355         }
356         mtime = atime = NULL;
357         if (vap->va_mtime.tv_sec != VNOVAL)
358                 mtime = &vap->va_mtime;
359         if (vap->va_atime.tv_sec != VNOVAL)
360                 atime = &vap->va_atime;
361         if (mtime != atime) {
362                 if (vap->va_vaflags & VA_UTIMES_NULL) {
363                         error = VOP_ACCESS(vp, VADMIN, ap->a_cred, td);
364                         if (error)
365                                 error = VOP_ACCESS(vp, VWRITE, ap->a_cred, td);
366                 } else
367                         error = VOP_ACCESS(vp, VADMIN, ap->a_cred, td);
368 #if 0
369                 if (mtime == NULL)
370                         mtime = &np->n_mtime;
371                 if (atime == NULL)
372                         atime = &np->n_atime;
373 #endif
374                 /*
375                  * If file is opened, then we can use handle based calls.
376                  * If not, use path based ones.
377                  */
378                 if ((np->n_flag & NOPEN) == 0) {
379                         if (vcp->vc_flags & SMBV_WIN95) {
380                                 error = VOP_OPEN(vp, FWRITE, ap->a_cred, td,
381                                     NULL);
382                                 if (!error) {
383 /*                                      error = smbfs_smb_setfattrNT(np, 0,
384                                             mtime, atime, &scred);
385                                         VOP_GETATTR(vp, &vattr, ap->a_cred); */
386                                         if (mtime)
387                                                 np->n_mtime = *mtime;
388                                         VOP_CLOSE(vp, FWRITE, ap->a_cred, 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);
419         np->n_mtime.tv_sec = vap->va_mtime.tv_sec;
420         return error;
421 }
422 /*
423  * smbfs_read call.
424  */
425 static int
426 smbfs_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
443 static int
444 smbfs_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  */
467 static int
468 smbfs_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)))
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
513 static int
514 smbfs_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  */
542 static int
543 smbfs_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
613 out_cacherem:
614         smbfs_attr_cacheremove(fdvp);
615         smbfs_attr_cacheremove(tdvp);
616 out:
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  */
636 static int
637 smbfs_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  */
651 static int
652 smbfs_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
664 static int
665 smbfs_mknod(ap) 
666         struct vop_mknod_args /* {
667         } */ *ap;
668 {
669         return EOPNOTSUPP;
670 }
671
672 static int
673 smbfs_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))) {
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  */
715 static int
716 smbfs_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  */
749 static int
750 smbfs_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 */
777 static int
778 smbfs_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
791 static 
792 int 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
809 static int
810 smbfs_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
838 static int
839 smbfs_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
848         SMBVDEBUG("\n");
849         if (bp->b_flags & B_ASYNC)
850                 td = (struct thread *)0;
851         else
852                 td = curthread; /* XXX */
853         if (bp->b_iocmd == BIO_READ)
854                 cr = bp->b_rcred;
855         else
856                 cr = bp->b_wcred;
857
858         if ((bp->b_flags & B_ASYNC) == 0 )
859                 (void)smbfs_doio(ap->a_vp, bp, cr, td);
860         return (0);
861 }
862
863 int
864 smbfs_ioctl(ap)
865         struct vop_ioctl_args /* {
866                 struct vnode *a_vp;
867                 u_long a_command;
868                 caddr_t a_data;
869                 int fflag;
870                 struct ucred *cred;
871                 struct thread *td;
872         } */ *ap;
873 {
874         return ENOTTY;
875 }
876
877 static char smbfs_atl[] = "rhsvda";
878 static int
879 smbfs_getextattr(struct vop_getextattr_args *ap)
880 /* {
881         IN struct vnode *a_vp;
882         IN char *a_name;
883         INOUT struct uio *a_uio;
884         IN struct ucred *a_cred;
885         IN struct thread *a_td;
886 };
887 */
888 {
889         struct vnode *vp = ap->a_vp;
890         struct thread *td = ap->a_td;
891         struct ucred *cred = ap->a_cred;
892         struct uio *uio = ap->a_uio;
893         const char *name = ap->a_name;
894         struct smbnode *np = VTOSMB(vp);
895         struct vattr vattr;
896         char buf[10];
897         int i, attr, error;
898
899         error = VOP_ACCESS(vp, VREAD, cred, td);
900         if (error)
901                 return error;
902         error = VOP_GETATTR(vp, &vattr, cred);
903         if (error)
904                 return error;
905         if (strcmp(name, "dosattr") == 0) {
906                 attr = np->n_dosattr;
907                 for (i = 0; i < 6; i++, attr >>= 1)
908                         buf[i] = (attr & 1) ? smbfs_atl[i] : '-';
909                 buf[i] = 0;
910                 error = uiomove(buf, i, uio);
911                 
912         } else
913                 error = EINVAL;
914         return error;
915 }
916
917 /*
918  * Since we expected to support F_GETLK (and SMB protocol has no such function),
919  * it is necessary to use lf_advlock(). It would be nice if this function had
920  * a callback mechanism because it will help to improve a level of consistency.
921  */
922 int
923 smbfs_advlock(ap)
924         struct vop_advlock_args /* {
925                 struct vnode *a_vp;
926                 caddr_t  a_id;
927                 int  a_op;
928                 struct flock *a_fl;
929                 int  a_flags;
930         } */ *ap;
931 {
932         struct vnode *vp = ap->a_vp;
933         struct smbnode *np = VTOSMB(vp);
934         struct flock *fl = ap->a_fl;
935         caddr_t id = (caddr_t)1 /* ap->a_id */;
936 /*      int flags = ap->a_flags;*/
937         struct thread *td = curthread;
938         struct smb_cred scred;
939         u_quad_t size;
940         off_t start, end, oadd;
941         int error, lkop;
942
943         if (vp->v_type == VDIR) {
944                 /*
945                  * SMB protocol have no support for directory locking.
946                  * Although locks can be processed on local machine, I don't
947                  * think that this is a good idea, because some programs
948                  * can work wrong assuming directory is locked. So, we just
949                  * return 'operation not supported
950                  */
951                  return EOPNOTSUPP;
952         }
953         size = np->n_size;
954         switch (fl->l_whence) {
955
956         case SEEK_SET:
957         case SEEK_CUR:
958                 start = fl->l_start;
959                 break;
960
961         case SEEK_END:
962                 if (size > OFF_MAX ||
963                     (fl->l_start > 0 && size > OFF_MAX - fl->l_start))
964                         return EOVERFLOW;
965                 start = size + fl->l_start;
966                 break;
967
968         default:
969                 return EINVAL;
970         }
971         if (start < 0)
972                 return EINVAL;
973         if (fl->l_len < 0) {
974                 if (start == 0)
975                         return EINVAL;
976                 end = start - 1;
977                 start += fl->l_len;
978                 if (start < 0)
979                         return EINVAL;
980         } else if (fl->l_len == 0)
981                 end = -1;
982         else {
983                 oadd = fl->l_len - 1;
984                 if (oadd > OFF_MAX - start)
985                         return EOVERFLOW;
986                 end = start + oadd;
987         }
988         smb_makescred(&scred, td, td->td_ucred);
989         switch (ap->a_op) {
990             case F_SETLK:
991                 switch (fl->l_type) {
992                     case F_WRLCK:
993                         lkop = SMB_LOCK_EXCL;
994                         break;
995                     case F_RDLCK:
996                         lkop = SMB_LOCK_SHARED;
997                         break;
998                     case F_UNLCK:
999                         lkop = SMB_LOCK_RELEASE;
1000                         break;
1001                     default:
1002                         return EINVAL;
1003                 }
1004                 error = lf_advlock(ap, &vp->v_lockf, size);
1005                 if (error)
1006                         break;
1007                 lkop = SMB_LOCK_EXCL;
1008                 error = smbfs_smb_lock(np, lkop, id, start, end, &scred);
1009                 if (error) {
1010                         int oldtype = fl->l_type;
1011                         fl->l_type = F_UNLCK;
1012                         ap->a_op = F_UNLCK;
1013                         lf_advlock(ap, &vp->v_lockf, size);
1014                         fl->l_type = oldtype;
1015                 }
1016                 break;
1017             case F_UNLCK:
1018                 lf_advlock(ap, &vp->v_lockf, size);
1019                 error = smbfs_smb_lock(np, SMB_LOCK_RELEASE, id, start, end, &scred);
1020                 break;
1021             case F_GETLK:
1022                 error = lf_advlock(ap, &vp->v_lockf, size);
1023                 break;
1024             default:
1025                 return EINVAL;
1026         }
1027         return error;
1028 }
1029
1030 static int
1031 smbfs_pathcheck(struct smbmount *smp, const char *name, int nmlen, int nameiop)
1032 {
1033         static const char *badchars = "*/:<>;?";
1034         static const char *badchars83 = " +|,[]=";
1035         const char *cp;
1036         int i, error;
1037
1038         /*
1039          * Backslash characters, being a path delimiter, are prohibited
1040          * within a path component even for LOOKUP operations.
1041          */
1042         if (index(name, '\\') != NULL)
1043                 return ENOENT;
1044
1045         if (nameiop == LOOKUP)
1046                 return 0;
1047         error = ENOENT;
1048         if (SMB_DIALECT(SSTOVC(smp->sm_share)) < SMB_DIALECT_LANMAN2_0) {
1049                 /*
1050                  * Name should conform 8.3 format
1051                  */
1052                 if (nmlen > 12)
1053                         return ENAMETOOLONG;
1054                 cp = index(name, '.');
1055                 if (cp == NULL)
1056                         return error;
1057                 if (cp == name || (cp - name) > 8)
1058                         return error;
1059                 cp = index(cp + 1, '.');
1060                 if (cp != NULL)
1061                         return error;
1062                 for (cp = name, i = 0; i < nmlen; i++, cp++)
1063                         if (index(badchars83, *cp) != NULL)
1064                                 return error;
1065         }
1066         for (cp = name, i = 0; i < nmlen; i++, cp++)
1067                 if (index(badchars, *cp) != NULL)
1068                         return error;
1069         return 0;
1070 }
1071
1072 /*
1073  * Things go even weird without fixed inode numbers...
1074  */
1075 int
1076 smbfs_lookup(ap)
1077         struct vop_lookup_args /* {
1078                 struct vnodeop_desc *a_desc;
1079                 struct vnode *a_dvp;
1080                 struct vnode **a_vpp;
1081                 struct componentname *a_cnp;
1082         } */ *ap;
1083 {
1084         struct componentname *cnp = ap->a_cnp;
1085         struct thread *td = cnp->cn_thread;
1086         struct vnode *dvp = ap->a_dvp;
1087         struct vnode **vpp = ap->a_vpp;
1088         struct vnode *vp;
1089         struct smbmount *smp;
1090         struct mount *mp = dvp->v_mount;
1091         struct smbnode *dnp;
1092         struct smbfattr fattr, *fap;
1093         struct smb_cred scred;
1094         char *name = cnp->cn_nameptr;
1095         int flags = cnp->cn_flags;
1096         int nameiop = cnp->cn_nameiop;
1097         int nmlen = cnp->cn_namelen;
1098         int error, islastcn, isdot;
1099         int killit;
1100         
1101         SMBVDEBUG("\n");
1102         if (dvp->v_type != VDIR)
1103                 return ENOTDIR;
1104         if ((flags & ISDOTDOT) && (dvp->v_vflag & VV_ROOT)) {
1105                 SMBFSERR("invalid '..'\n");
1106                 return EIO;
1107         }
1108 #ifdef SMB_VNODE_DEBUG
1109         {
1110                 char *cp, c;
1111
1112                 cp = name + nmlen;
1113                 c = *cp;
1114                 *cp = 0;
1115                 SMBVDEBUG("%d '%s' in '%s' id=d\n", nameiop, name, 
1116                         VTOSMB(dvp)->n_name);
1117                 *cp = c;
1118         }
1119 #endif
1120         islastcn = flags & ISLASTCN;
1121         if (islastcn && (mp->mnt_flag & MNT_RDONLY) && (nameiop != LOOKUP))
1122                 return EROFS;
1123         if ((error = VOP_ACCESS(dvp, VEXEC, cnp->cn_cred, td)) != 0)
1124                 return error;
1125         smp = VFSTOSMBFS(mp);
1126         dnp = VTOSMB(dvp);
1127         isdot = (nmlen == 1 && name[0] == '.');
1128
1129         error = smbfs_pathcheck(smp, cnp->cn_nameptr, cnp->cn_namelen, nameiop);
1130
1131         if (error) 
1132                 return ENOENT;
1133
1134         error = cache_lookup(dvp, vpp, cnp);
1135         SMBVDEBUG("cache_lookup returned %d\n", error);
1136         if (error > 0)
1137                 return error;
1138         if (error) {            /* name was found */
1139                 struct vattr vattr;
1140
1141                 killit = 0;
1142                 vp = *vpp;
1143                 error = VOP_GETATTR(vp, &vattr, cnp->cn_cred);
1144                 /*
1145                  * If the file type on the server is inconsistent
1146                  * with what it was when we created the vnode,
1147                  * kill the bogus vnode now and fall through to
1148                  * the code below to create a new one with the
1149                  * right type.
1150                  */
1151                 if (error == 0 &&
1152                    ((vp->v_type == VDIR &&
1153                    (VTOSMB(vp)->n_dosattr & SMB_FA_DIR) == 0) ||
1154                    (vp->v_type == VREG &&
1155                    (VTOSMB(vp)->n_dosattr & SMB_FA_DIR) != 0)))
1156                    killit = 1;
1157                 else if (error == 0
1158              /*    && vattr.va_ctime.tv_sec == VTOSMB(vp)->n_ctime*/) {
1159                      if (nameiop != LOOKUP && islastcn)
1160                              cnp->cn_flags |= SAVENAME;
1161                      SMBVDEBUG("use cached vnode\n");
1162                      return (0);
1163                 }
1164                 cache_purge(vp);
1165                 /*
1166                  * XXX This is not quite right, if '.' is
1167                  * inconsistent, we really need to start the lookup
1168                  * all over again.  Hopefully there is some other
1169                  * guarantee that prevents this case from happening.
1170                  */
1171                 if (killit && vp != dvp)
1172                         vgone(vp);
1173                 if (vp != dvp)
1174                         vput(vp);
1175                 else
1176                         vrele(vp);
1177                 *vpp = NULLVP;
1178         }
1179         /* 
1180          * entry is not in the cache or has been expired
1181          */
1182         error = 0;
1183         *vpp = NULLVP;
1184         smb_makescred(&scred, td, cnp->cn_cred);
1185         fap = &fattr;
1186         if (flags & ISDOTDOT) {
1187                 error = smbfs_smb_lookup(VTOSMB(dnp->n_parent), NULL, 0, fap,
1188                     &scred);
1189                 SMBVDEBUG("result of dotdot lookup: %d\n", error);
1190         } else {
1191                 fap = &fattr;
1192                 error = smbfs_smb_lookup(dnp, name, nmlen, fap, &scred);
1193 /*              if (cnp->cn_namelen == 1 && cnp->cn_nameptr[0] == '.')*/
1194                 SMBVDEBUG("result of smbfs_smb_lookup: %d\n", error);
1195         }
1196         if (error && error != ENOENT)
1197                 return error;
1198         if (error) {                    /* entry not found */
1199                 /*
1200                  * Handle RENAME or CREATE case...
1201                  */
1202                 if ((nameiop == CREATE || nameiop == RENAME) && islastcn) {
1203                         error = VOP_ACCESS(dvp, VWRITE, cnp->cn_cred, td);
1204                         if (error)
1205                                 return error;
1206                         cnp->cn_flags |= SAVENAME;
1207                         return (EJUSTRETURN);
1208                 }
1209                 return ENOENT;
1210         }/* else {
1211                 SMBVDEBUG("Found entry %s with id=%d\n", fap->entryName, fap->dirEntNum);
1212         }*/
1213         /*
1214          * handle DELETE case ...
1215          */
1216         if (nameiop == DELETE && islastcn) {    /* delete last component */
1217                 error = VOP_ACCESS(dvp, VWRITE, cnp->cn_cred, td);
1218                 if (error)
1219                         return error;
1220                 if (isdot) {
1221                         VREF(dvp);
1222                         *vpp = dvp;
1223                         return 0;
1224                 }
1225                 error = smbfs_nget(mp, dvp, name, nmlen, fap, &vp);
1226                 if (error)
1227                         return error;
1228                 *vpp = vp;
1229                 cnp->cn_flags |= SAVENAME;
1230                 return 0;
1231         }
1232         if (nameiop == RENAME && islastcn) {
1233                 error = VOP_ACCESS(dvp, VWRITE, cnp->cn_cred, td);
1234                 if (error)
1235                         return error;
1236                 if (isdot)
1237                         return EISDIR;
1238                 error = smbfs_nget(mp, dvp, name, nmlen, fap, &vp);
1239                 if (error)
1240                         return error;
1241                 *vpp = vp;
1242                 cnp->cn_flags |= SAVENAME;
1243                 return 0;
1244         }
1245         if (flags & ISDOTDOT) {
1246                 VOP_UNLOCK(dvp, 0);
1247                 error = smbfs_nget(mp, dvp, name, nmlen, NULL, &vp);
1248                 vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY);
1249                 if (error)
1250                         return error;
1251                 *vpp = vp;
1252         } else if (isdot) {
1253                 vref(dvp);
1254                 *vpp = dvp;
1255         } else {
1256                 error = smbfs_nget(mp, dvp, name, nmlen, fap, &vp);
1257                 if (error)
1258                         return error;
1259                 *vpp = vp;
1260                 SMBVDEBUG("lookup: getnewvp!\n");
1261         }
1262         if ((cnp->cn_flags & MAKEENTRY)/* && !islastcn*/) {
1263 /*              VTOSMB(*vpp)->n_ctime = VTOSMB(*vpp)->n_vattr.va_ctime.tv_sec;*/
1264                 cache_enter(dvp, *vpp, cnp);
1265         }
1266         return 0;
1267 }