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