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