Per-CPU VFS Namecache Effectiveness Statistics:
[dragonfly.git] / sys / vfs / nwfs / nwfs_vnops.c
1 /*
2  * Copyright (c) 1999, 2000 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/nwfs/nwfs_vnops.c,v 1.6.2.3 2001/03/14 11:26:59 bp Exp $
33  * $DragonFly: src/sys/vfs/nwfs/nwfs_vnops.c,v 1.10 2004/03/01 06:33:22 dillon Exp $
34  */
35 #include <sys/param.h>
36 #include <sys/systm.h>
37 #include <sys/kernel.h>
38 #include <sys/buf.h>
39 #include <sys/proc.h>
40 #include <sys/namei.h>
41 #include <sys/fcntl.h>
42 #include <sys/mount.h>
43 #include <sys/unistd.h>
44 #include <sys/vnode.h>
45
46 #include <vm/vm.h>
47 #include <vm/vm_extern.h>
48 #include <vm/vm_zone.h>
49
50 #include <netproto/ncp/ncp.h>
51 #include <netproto/ncp/ncp_conn.h>
52 #include <netproto/ncp/ncp_subr.h>
53 #include <netproto/ncp/nwerror.h>
54 #include <netproto/ncp/ncp_nls.h>
55
56 #include "nwfs.h"
57 #include "nwfs_node.h"
58 #include "nwfs_subr.h"
59
60 /*
61  * Prototypes for NWFS vnode operations
62  */
63 static int nwfs_create(struct vop_create_args *);
64 static int nwfs_mknod(struct vop_mknod_args *);
65 static int nwfs_open(struct vop_open_args *);
66 static int nwfs_close(struct vop_close_args *);
67 static int nwfs_access(struct vop_access_args *);
68 static int nwfs_getattr(struct vop_getattr_args *);
69 static int nwfs_setattr(struct vop_setattr_args *);
70 static int nwfs_read(struct vop_read_args *);
71 static int nwfs_write(struct vop_write_args *);
72 static int nwfs_fsync(struct vop_fsync_args *);
73 static int nwfs_remove(struct vop_remove_args *);
74 static int nwfs_link(struct vop_link_args *);
75 static int nwfs_lookup(struct vop_lookup_args *);
76 static int nwfs_rename(struct vop_rename_args *);
77 static int nwfs_mkdir(struct vop_mkdir_args *);
78 static int nwfs_rmdir(struct vop_rmdir_args *);
79 static int nwfs_symlink(struct vop_symlink_args *);
80 static int nwfs_readdir(struct vop_readdir_args *);
81 static int nwfs_bmap(struct vop_bmap_args *);
82 static int nwfs_strategy(struct vop_strategy_args *);
83 static int nwfs_print(struct vop_print_args *);
84 static int nwfs_pathconf(struct vop_pathconf_args *ap);
85
86 /* Global vfs data structures for nwfs */
87 vop_t **nwfs_vnodeop_p;
88 static struct vnodeopv_entry_desc nwfs_vnodeop_entries[] = {
89         { &vop_default_desc,            (vop_t *) vop_defaultop },
90         { &vop_access_desc,             (vop_t *) nwfs_access },
91         { &vop_bmap_desc,               (vop_t *) nwfs_bmap },
92         { &vop_open_desc,               (vop_t *) nwfs_open },
93         { &vop_close_desc,              (vop_t *) nwfs_close },
94         { &vop_create_desc,             (vop_t *) nwfs_create },
95         { &vop_fsync_desc,              (vop_t *) nwfs_fsync },
96         { &vop_getattr_desc,            (vop_t *) nwfs_getattr },
97         { &vop_getpages_desc,           (vop_t *) nwfs_getpages },
98         { &vop_putpages_desc,           (vop_t *) nwfs_putpages },
99         { &vop_ioctl_desc,              (vop_t *) nwfs_ioctl },
100         { &vop_inactive_desc,           (vop_t *) nwfs_inactive },
101         { &vop_islocked_desc,           (vop_t *) vop_stdislocked },
102         { &vop_link_desc,               (vop_t *) nwfs_link },
103         { &vop_lock_desc,               (vop_t *) vop_stdlock },
104         { &vop_lookup_desc,             (vop_t *) nwfs_lookup },
105         { &vop_mkdir_desc,              (vop_t *) nwfs_mkdir },
106         { &vop_mknod_desc,              (vop_t *) nwfs_mknod },
107         { &vop_pathconf_desc,           (vop_t *) nwfs_pathconf },
108         { &vop_print_desc,              (vop_t *) nwfs_print },
109         { &vop_read_desc,               (vop_t *) nwfs_read },
110         { &vop_readdir_desc,            (vop_t *) nwfs_readdir },
111         { &vop_reclaim_desc,            (vop_t *) nwfs_reclaim },
112         { &vop_remove_desc,             (vop_t *) nwfs_remove },
113         { &vop_rename_desc,             (vop_t *) nwfs_rename },
114         { &vop_rmdir_desc,              (vop_t *) nwfs_rmdir },
115         { &vop_setattr_desc,            (vop_t *) nwfs_setattr },
116         { &vop_strategy_desc,           (vop_t *) nwfs_strategy },
117         { &vop_symlink_desc,            (vop_t *) nwfs_symlink },
118         { &vop_unlock_desc,             (vop_t *) vop_stdunlock },
119         { &vop_write_desc,              (vop_t *) nwfs_write },
120         { NULL, NULL }
121 };
122 static struct vnodeopv_desc nwfs_vnodeop_opv_desc =
123         { &nwfs_vnodeop_p, nwfs_vnodeop_entries };
124
125 VNODEOP_SET(nwfs_vnodeop_opv_desc);
126
127 /*
128  * nwfs_access vnode op
129  * for now just return ok
130  */
131 static int
132 nwfs_access(ap)
133         struct vop_access_args /* {
134                 struct vnode *a_vp;
135                 int  a_mode;
136                 struct ucred *a_cred;
137                 struct thread *a_td;
138         } */ *ap;
139 {
140         struct vnode *vp = ap->a_vp;
141         struct ucred *cred = ap->a_cred;
142         u_int mode = ap->a_mode;
143         struct nwmount *nmp = VTONWFS(vp);
144         int error = 0;
145
146         NCPVNDEBUG("\n");
147         if ((ap->a_mode & VWRITE) && (vp->v_mount->mnt_flag & MNT_RDONLY)) {
148                 switch (vp->v_type) {
149                     case VREG: case VDIR: case VLNK:
150                         return (EROFS);
151                     default:
152                         break;
153                 }
154         }
155         if (cred->cr_uid == 0)
156                 return 0;
157         if (cred->cr_uid != nmp->m.uid) {
158                 mode >>= 3;
159                 if (!groupmember(nmp->m.gid, cred))
160                         mode >>= 3;
161         }
162         error = (((vp->v_type == VREG) ? nmp->m.file_mode : nmp->m.dir_mode) & mode) == mode ? 0 : EACCES;
163         return error;
164 }
165 /*
166  * nwfs_open vnode op
167  */
168 /* ARGSUSED */
169 static int
170 nwfs_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         int mode = ap->a_mode;
180         struct nwnode *np = VTONW(vp);
181         struct ncp_open_info no;
182         struct nwmount *nmp = VTONWFS(vp);
183         struct vattr vattr;
184         int error, nwm;
185
186         NCPVNDEBUG("%s,%d\n",np->n_name, np->opened);
187         if (vp->v_type != VREG && vp->v_type != VDIR) { 
188                 NCPFATAL("open vtype = %d\n", vp->v_type);
189                 return (EACCES);
190         }
191         if (vp->v_type == VDIR) return 0;       /* nothing to do now */
192         if (np->n_flag & NMODIFIED) {
193                 if ((error = nwfs_vinvalbuf(vp, V_SAVE, ap->a_td, 1)) == EINTR)
194                         return (error);
195                 np->n_atime = 0;
196                 error = VOP_GETATTR(vp, &vattr, ap->a_td);
197                 if (error) return (error);
198                 np->n_mtime = vattr.va_mtime.tv_sec;
199         } else {
200                 error = VOP_GETATTR(vp, &vattr, ap->a_td);
201                 if (error) return (error);
202                 if (np->n_mtime != vattr.va_mtime.tv_sec) {
203                         if ((error = nwfs_vinvalbuf(vp, V_SAVE, ap->a_td, 1)) == EINTR)
204                                 return (error);
205                         np->n_mtime = vattr.va_mtime.tv_sec;
206                 }
207         }
208         if (np->opened) {
209                 np->opened++;
210                 return 0;
211         }
212         nwm = AR_READ;
213         if ((vp->v_mount->mnt_flag & MNT_RDONLY) == 0)
214                 nwm |= AR_WRITE;
215         error = ncp_open_create_file_or_subdir(nmp, vp, 0, NULL, OC_MODE_OPEN,
216                                                0, nwm, &no, ap->a_td, ap->a_cred);
217         if (error) {
218                 if (mode & FWRITE)
219                         return EACCES;
220                 nwm = AR_READ;
221                 error = ncp_open_create_file_or_subdir(nmp, vp, 0, NULL, OC_MODE_OPEN, 0,
222                                                    nwm, &no, ap->a_td,ap->a_cred);
223         }
224         if (!error) {
225                 np->opened++;
226                 np->n_fh = no.fh;
227                 np->n_origfh = no.origfh;
228         }
229         np->n_atime = 0;
230         return (error);
231 }
232
233 static int
234 nwfs_close(ap)
235         struct vop_close_args /* {
236                 struct vnodeop_desc *a_desc;
237                 struct vnode *a_vp;
238                 int  a_fflag;
239                 struct ucred *a_cred;
240                 struct thread *a_td;
241         } */ *ap;
242 {
243         struct vnode *vp = ap->a_vp;
244         struct nwnode *np = VTONW(vp);
245         lwkt_tokref vlock;
246         int error;
247
248         NCPVNDEBUG("name=%s,td=%p,c=%d\n",np->n_name,ap->a_td,np->opened);
249
250         if (vp->v_type == VDIR) return 0;       /* nothing to do now */
251         error = 0;
252         lwkt_gettoken(&vlock, vp->v_interlock);
253         if (np->opened == 0) {
254                 lwkt_reltoken(&vlock);
255                 return 0;
256         }
257         lwkt_reltoken(&vlock);
258         error = nwfs_vinvalbuf(vp, V_SAVE, ap->a_td, 1);
259         lwkt_gettokref(&vlock);
260         if (np->opened == 0) {
261                 lwkt_reltoken(&vlock);
262                 return 0;
263         }
264         if (--np->opened == 0) {
265                 lwkt_reltoken(&vlock);
266                 error = ncp_close_file(NWFSTOCONN(VTONWFS(vp)), &np->n_fh, 
267                    ap->a_td, proc0.p_ucred);
268         } else {
269                 lwkt_reltoken(&vlock);
270         }
271         np->n_atime = 0;
272         return (error);
273 }
274
275 /*
276  * nwfs_getattr call from vfs.
277  */
278 static int
279 nwfs_getattr(ap)
280         struct vop_getattr_args /* {
281                 struct vnode *a_vp;
282                 struct vattr *a_vap;
283                 struct ucred *a_cred;
284                 struct thread *a_td;
285         } */ *ap;
286 {
287         struct vnode *vp = ap->a_vp;
288         struct nwnode *np = VTONW(vp);
289         struct vattr *va=ap->a_vap;
290         struct nwmount *nmp = VTONWFS(vp);
291         struct nw_entry_info fattr;
292         int error;
293         u_int32_t oldsize;
294
295         NCPVNDEBUG("%lx:%d: '%s' %d\n", (long)vp, nmp->n_volume, np->n_name, (vp->v_flag & VROOT) != 0);
296         error = nwfs_attr_cachelookup(vp,va);
297         if (!error) return 0;
298         NCPVNDEBUG("not in cache\n");
299         oldsize = np->n_size;
300         if (np->n_flag & NVOLUME) {
301                 error = ncp_obtain_info(nmp, np->n_fid.f_id, 0, NULL, &fattr,
302                     ap->a_td,proc0.p_ucred);
303         } else {
304                 error = ncp_obtain_info(nmp, np->n_fid.f_parent, np->n_nmlen, 
305                     np->n_name, &fattr, ap->a_td, proc0.p_ucred);
306         }
307         if (error) {
308                 NCPVNDEBUG("error %d\n", error);
309                 return error;
310         }
311         nwfs_attr_cacheenter(vp, &fattr);
312         *va = np->n_vattr;
313         if (np->opened)
314                 np->n_size = oldsize;
315         return (0);
316 }
317 /*
318  * nwfs_setattr call from vfs.
319  */
320 static int
321 nwfs_setattr(ap)
322         struct vop_setattr_args /* {
323                 struct vnode *a_vp;
324                 struct vattr *a_vap;
325                 struct ucred *a_cred;
326                 struct thread *a_td;
327         } */ *ap;
328 {
329         struct vnode *vp = ap->a_vp;
330         struct nwnode *np = VTONW(vp);
331         struct vattr *vap = ap->a_vap;
332         u_quad_t tsize=0;
333         int error = 0;
334
335         NCPVNDEBUG("\n");
336         if (vap->va_flags != VNOVAL)
337                 return (EOPNOTSUPP);
338         /*
339          * Disallow write attempts if the filesystem is mounted read-only.
340          */
341         if ((vap->va_uid != (uid_t)VNOVAL || vap->va_gid != (gid_t)VNOVAL || 
342              vap->va_atime.tv_sec != VNOVAL || vap->va_mtime.tv_sec != VNOVAL ||
343              vap->va_mode != (mode_t)VNOVAL) &&(vp->v_mount->mnt_flag & MNT_RDONLY))
344                 return (EROFS);
345         if (vap->va_size != VNOVAL) {
346                 switch (vp->v_type) {
347                 case VDIR:
348                         return (EISDIR);
349                 case VREG:
350                         /*
351                          * Disallow write attempts if the filesystem is
352                          * mounted read-only.
353                          */
354                         if (vp->v_mount->mnt_flag & MNT_RDONLY)
355                                 return (EROFS);
356                         vnode_pager_setsize(vp, (u_long)vap->va_size);
357                         tsize = np->n_size;
358                         np->n_size = vap->va_size;
359                         break;
360                 default:
361                         return EINVAL;
362                 };
363         }
364         error = ncp_setattr(vp, vap, ap->a_cred, ap->a_td);
365         if (error && vap->va_size != VNOVAL) {
366                 np->n_size = tsize;
367                 vnode_pager_setsize(vp, (u_long)tsize);
368         }
369         np->n_atime = 0;        /* invalidate cache */
370         VOP_GETATTR(vp, vap, ap->a_td);
371         np->n_mtime = vap->va_mtime.tv_sec;
372         return (0);
373 }
374 /*
375  * nwfs_read call.
376  */
377 static int
378 nwfs_read(ap)
379         struct vop_read_args /* {
380                 struct vnode *a_vp;
381                 struct uio *a_uio;
382                 int  a_ioflag;
383                 struct ucred *a_cred;
384         } */ *ap;
385 {
386         struct vnode *vp = ap->a_vp;
387         struct uio *uio=ap->a_uio;
388         int error;
389         NCPVNDEBUG("nwfs_read:\n");
390
391         if (vp->v_type != VREG && vp->v_type != VDIR)
392                 return (EPERM);
393         error = nwfs_readvnode(vp, uio, ap->a_cred);
394         return error;
395 }
396
397 static int
398 nwfs_write(ap)
399         struct vop_write_args /* {
400                 struct vnode *a_vp;
401                 struct uio *a_uio;
402                 int  a_ioflag;
403                 struct ucred *a_cred;
404         } */ *ap;
405 {
406         struct vnode *vp = ap->a_vp;
407         struct uio *uio = ap->a_uio;
408         int error;
409
410         NCPVNDEBUG("%d,ofs=%d,sz=%d\n",vp->v_type, (int)uio->uio_offset, uio->uio_resid);
411
412         if (vp->v_type != VREG)
413                 return (EPERM);
414         error = nwfs_writevnode(vp, uio, ap->a_cred,ap->a_ioflag);
415         return(error);
416 }
417 /*
418  * nwfs_create call
419  * Create a regular file. On entry the directory to contain the file being
420  * created is locked.  We must release before we return. We must also free
421  * the pathname buffer pointed at by cnp->cn_pnbuf, always on error, or
422  * only if the SAVESTART bit in cn_flags is clear on success.
423  */
424 static int
425 nwfs_create(ap)
426         struct vop_create_args /* {
427                 struct vnode *a_dvp;
428                 struct vnode **a_vpp;
429                 struct componentname *a_cnp;
430                 struct vattr *a_vap;
431         } */ *ap;
432 {
433         struct vnode *dvp = ap->a_dvp;
434         struct vattr *vap = ap->a_vap;
435         struct vnode **vpp=ap->a_vpp;
436         struct componentname *cnp = ap->a_cnp;
437         struct vnode *vp = (struct vnode *)0;
438         int error = 0, fmode;
439         struct vattr vattr;
440         struct nwnode *np;
441         struct ncp_open_info no;
442         struct nwmount *nmp=VTONWFS(dvp);
443         ncpfid fid;
444         
445
446         NCPVNDEBUG("\n");
447         *vpp = NULL;
448         if (vap->va_type == VSOCK)
449                 return (EOPNOTSUPP);
450         if ((error = VOP_GETATTR(dvp, &vattr, cnp->cn_td))) {
451                 return (error);
452         }
453         fmode = AR_READ | AR_WRITE;
454 /*      if (vap->va_vaflags & VA_EXCLUSIVE)
455                 fmode |= AR_DENY_READ | AR_DENY_WRITE;*/
456         
457         error = ncp_open_create_file_or_subdir(nmp,dvp,cnp->cn_namelen,cnp->cn_nameptr, 
458                            OC_MODE_CREATE | OC_MODE_OPEN | OC_MODE_REPLACE,
459                            0, fmode, &no, cnp->cn_td, cnp->cn_cred);
460         if (!error) {
461                 error = ncp_close_file(NWFSTOCONN(nmp), &no.fh, cnp->cn_td,cnp->cn_cred);
462                 fid.f_parent = VTONW(dvp)->n_fid.f_id;
463                 fid.f_id = no.fattr.dirEntNum;
464                 error = nwfs_nget(VTOVFS(dvp), fid, &no.fattr, dvp, &vp);
465                 if (!error) {
466                         np = VTONW(vp);
467                         np->opened = 0;
468                         *vpp = vp;
469                 }
470                 if (cnp->cn_flags & CNP_MAKEENTRY)
471                         cache_enter(dvp, NCPNULL, vp, cnp);
472         }
473         return (error);
474 }
475
476 /*
477  * nwfs_remove call. It isn't possible to emulate UFS behaivour because
478  * NetWare doesn't allow delete/rename operations on an opened file.
479  */
480 static int
481 nwfs_remove(ap)
482         struct vop_remove_args /* {
483                 struct vnodeop_desc *a_desc;
484                 struct vnode * a_dvp;
485                 struct vnode * a_vp;
486                 struct componentname * a_cnp;
487         } */ *ap;
488 {
489         struct vnode *vp = ap->a_vp;
490         struct vnode *dvp = ap->a_dvp;
491         struct componentname *cnp = ap->a_cnp;
492         struct nwnode *np = VTONW(vp);
493         struct nwmount *nmp = VTONWFS(vp);
494         int error;
495
496         if (vp->v_type == VDIR || np->opened || vp->v_usecount != 1) {
497                 error = EPERM;
498         } else if (!ncp_conn_valid(NWFSTOCONN(nmp))) {
499                 error = EIO;
500         } else {
501                 cache_purge(vp);
502                 error = ncp_DeleteNSEntry(nmp, VTONW(dvp)->n_fid.f_id,
503                     cnp->cn_namelen,cnp->cn_nameptr,cnp->cn_td,cnp->cn_cred);
504                 if (error == 0)
505                         np->n_flag |= NSHOULDFREE;
506                 else if (error == 0x899c)
507                         error = EACCES;
508         }
509         return (error);
510 }
511
512 /*
513  * nwfs_file rename call
514  */
515 static int
516 nwfs_rename(ap)
517         struct vop_rename_args  /* {
518                 struct vnode *a_fdvp;
519                 struct vnode *a_fvp;
520                 struct componentname *a_fcnp;
521                 struct vnode *a_tdvp;
522                 struct vnode *a_tvp;
523                 struct componentname *a_tcnp;
524         } */ *ap;
525 {
526         struct vnode *fvp = ap->a_fvp;
527         struct vnode *tvp = ap->a_tvp;
528         struct vnode *fdvp = ap->a_fdvp;
529         struct vnode *tdvp = ap->a_tdvp;
530         struct componentname *tcnp = ap->a_tcnp;
531         struct componentname *fcnp = ap->a_fcnp;
532         struct nwmount *nmp=VTONWFS(fvp);
533         u_int16_t oldtype = 6;
534         int error=0;
535
536         /* Check for cross-device rename */
537         if ((fvp->v_mount != tdvp->v_mount) ||
538             (tvp && (fvp->v_mount != tvp->v_mount))) {
539                 error = EXDEV;
540                 goto out;
541         }
542
543         if (tvp && tvp->v_usecount > 1) {
544                 error = EBUSY;
545                 goto out;
546         }
547         if (tvp && tvp != fvp) {
548                 error = ncp_DeleteNSEntry(nmp, VTONW(tdvp)->n_fid.f_id,
549                     tcnp->cn_namelen, tcnp->cn_nameptr, 
550                     tcnp->cn_td, tcnp->cn_cred);
551                 if (error == 0x899c) error = EACCES;
552                 if (error)
553                         goto out;
554         }
555         if (fvp->v_type == VDIR) {
556                 oldtype |= NW_TYPE_SUBDIR;
557         } else if (fvp->v_type == VREG) {
558                 oldtype |= NW_TYPE_FILE;
559         } else
560                 return EINVAL;
561         error = ncp_nsrename(NWFSTOCONN(nmp), nmp->n_volume, nmp->name_space, 
562                 oldtype, &nmp->m.nls,
563                 VTONW(fdvp)->n_fid.f_id, fcnp->cn_nameptr, fcnp->cn_namelen,
564                 VTONW(tdvp)->n_fid.f_id, tcnp->cn_nameptr, tcnp->cn_namelen,
565                 tcnp->cn_td,tcnp->cn_cred);
566
567         if (error == 0x8992)
568                 error = EEXIST;
569         if (fvp->v_type == VDIR) {
570                 if (tvp != NULL && tvp->v_type == VDIR)
571                         cache_purge(tdvp);
572                 cache_purge(fdvp);
573         }
574 out:
575         if (tdvp == tvp)
576                 vrele(tdvp);
577         else
578                 vput(tdvp);
579         if (tvp)
580                 vput(tvp);
581         vrele(fdvp);
582         vrele(fvp);
583         nwfs_attr_cacheremove(fdvp);
584         nwfs_attr_cacheremove(tdvp);
585         /*
586          * Need to get rid of old vnodes, because netware will change
587          * file id on rename
588          */
589         vgone(fvp);
590         if (tvp)
591                 vgone(tvp);
592         /*
593          * Kludge: Map ENOENT => 0 assuming that it is a reply to a retry.
594          */
595         if (error == ENOENT)
596                 error = 0;
597         return (error);
598 }
599
600 /*
601  * nwfs hard link create call
602  * Netware filesystems don't know what links are.
603  */
604 static int
605 nwfs_link(ap)
606         struct vop_link_args /* {
607                 struct vnode *a_tdvp;
608                 struct vnode *a_vp;
609                 struct componentname *a_cnp;
610         } */ *ap;
611 {
612         return EOPNOTSUPP;
613 }
614
615 /*
616  * nwfs_symlink link create call
617  * Netware filesystems don't know what symlinks are.
618  */
619 static int
620 nwfs_symlink(ap)
621         struct vop_symlink_args /* {
622                 struct vnode *a_dvp;
623                 struct vnode **a_vpp;
624                 struct componentname *a_cnp;
625                 struct vattr *a_vap;
626                 char *a_target;
627         } */ *ap;
628 {
629         return (EOPNOTSUPP);
630 }
631
632 static int nwfs_mknod(ap) 
633         struct vop_mknod_args /* {
634         } */ *ap;
635 {
636         return (EOPNOTSUPP);
637 }
638
639 /*
640  * nwfs_mkdir call
641  */
642 static int
643 nwfs_mkdir(ap)
644         struct vop_mkdir_args /* {
645                 struct vnode *a_dvp;
646                 struct vnode **a_vpp;
647                 struct componentname *a_cnp;
648                 struct vattr *a_vap;
649         } */ *ap;
650 {
651         struct vnode *dvp = ap->a_dvp;
652 /*      struct vattr *vap = ap->a_vap;*/
653         struct componentname *cnp = ap->a_cnp;
654         int len=cnp->cn_namelen;
655         struct ncp_open_info no;
656         struct nwnode *np;
657         struct vnode *newvp = (struct vnode *)0;
658         ncpfid fid;
659         int error = 0;
660         struct vattr vattr;
661         char *name=cnp->cn_nameptr;
662
663         if ((error = VOP_GETATTR(dvp, &vattr, cnp->cn_td))) {
664                 return (error);
665         }       
666         if ((name[0] == '.') && ((len == 1) || ((len == 2) && (name[1] == '.')))) {
667                 return EEXIST;
668         }
669         if (ncp_open_create_file_or_subdir(VTONWFS(dvp),dvp, cnp->cn_namelen,
670                         cnp->cn_nameptr,OC_MODE_CREATE, aDIR, 0xffff,
671                         &no, cnp->cn_td, cnp->cn_cred) != 0) {
672                 error = EACCES;
673         } else {
674                 error = 0;
675         }
676         if (!error) {
677                 fid.f_parent = VTONW(dvp)->n_fid.f_id;
678                 fid.f_id = no.fattr.dirEntNum;
679                 error = nwfs_nget(VTOVFS(dvp), fid, &no.fattr, dvp, &newvp);
680                 if (!error) {
681                         np = VTONW(newvp);
682                         newvp->v_type = VDIR;
683                         *ap->a_vpp = newvp;
684                 }
685         }
686         return (error);
687 }
688
689 /*
690  * nwfs_remove directory call
691  */
692 static int
693 nwfs_rmdir(ap)
694         struct vop_rmdir_args /* {
695                 struct vnode *a_dvp;
696                 struct vnode *a_vp;
697                 struct componentname *a_cnp;
698         } */ *ap;
699 {
700         struct vnode *vp = ap->a_vp;
701         struct vnode *dvp = ap->a_dvp;
702         struct componentname *cnp = ap->a_cnp;
703         struct nwnode *np = VTONW(vp);
704         struct nwmount *nmp = VTONWFS(vp);
705         struct nwnode *dnp = VTONW(dvp);
706         int error = EIO;
707
708         if (dvp == vp)
709                 return EINVAL;
710
711         error = ncp_DeleteNSEntry(nmp, dnp->n_fid.f_id, 
712                 cnp->cn_namelen, cnp->cn_nameptr,cnp->cn_td,cnp->cn_cred);
713         if (error == 0)
714                 np->n_flag |= NSHOULDFREE;
715         else if (error == NWE_DIR_NOT_EMPTY)
716                 error = ENOTEMPTY;
717         dnp->n_flag |= NMODIFIED;
718         nwfs_attr_cacheremove(dvp);
719         cache_purge(dvp);
720         cache_purge(vp);
721         return (error);
722 }
723
724 /*
725  * nwfs_readdir call
726  */
727 static int
728 nwfs_readdir(ap)
729         struct vop_readdir_args /* {
730                 struct vnode *a_vp;
731                 struct uio *a_uio;
732                 struct ucred *a_cred;
733                 int *a_eofflag;
734                 u_long *a_cookies;
735                 int a_ncookies;
736         } */ *ap;
737 {
738         struct vnode *vp = ap->a_vp;
739         struct uio *uio = ap->a_uio;
740         int error;
741
742         if (vp->v_type != VDIR)
743                 return (EPERM);
744         if (ap->a_ncookies) {
745                 printf("nwfs_readdir: no support for cookies now...");
746                 return (EOPNOTSUPP);
747         }
748
749         error = nwfs_readvnode(vp, uio, ap->a_cred);
750         return error;
751 }
752 /* ARGSUSED */
753 static int
754 nwfs_fsync(ap)
755         struct vop_fsync_args /* {
756                 struct vnodeop_desc *a_desc;
757                 struct vnode * a_vp;
758                 struct ucred * a_cred;
759                 int  a_waitfor;
760                 struct thread *a_td;
761         } */ *ap;
762 {
763 /*      return (nfs_flush(ap->a_vp, ap->a_cred, ap->a_waitfor, ap->a_td, 1));*/
764     return (0);
765 }
766
767 /* ARGSUSED */
768 static 
769 int nwfs_print (ap) 
770         struct vop_print_args /* {
771                 struct vnode *a_vp;
772         } */ *ap;
773 {
774         struct vnode *vp = ap->a_vp;
775         struct nwnode *np = VTONW(vp);
776
777         printf("nwfs node: name = '%s', fid = %d, pfid = %d\n",
778             np->n_name, np->n_fid.f_id, np->n_fid.f_parent);
779         return (0);
780 }
781
782 static int nwfs_pathconf (ap)
783         struct vop_pathconf_args  /* {
784         struct vnode *vp;
785         int name;
786         register_t *retval;
787         } */ *ap;
788 {
789         int name=ap->a_name, error=0;
790         register_t *retval=ap->a_retval;
791         
792         switch(name){
793                 case _PC_LINK_MAX:
794                         *retval=0;
795                         break;
796                 case _PC_NAME_MAX:
797                         *retval=NCP_MAX_FILENAME; /* XXX from nwfsnode */
798                         break;
799                 case _PC_PATH_MAX:
800                         *retval=NCP_MAXPATHLEN; /* XXX from nwfsnode */
801                         break;
802                 default:
803                         error=EINVAL;
804         }
805         return(error);
806 }
807
808 static int nwfs_strategy (ap) 
809         struct vop_strategy_args /* {
810         struct buf *a_bp
811         } */ *ap;
812 {
813         struct buf *bp=ap->a_bp;
814         int error = 0;
815         struct thread *td = NULL;
816
817         NCPVNDEBUG("\n");
818         if (bp->b_flags & B_PHYS)
819                 panic("nwfs physio");
820         if ((bp->b_flags & B_ASYNC) == 0)
821                 td = curthread;         /* YYY dunno if this is legal */
822         /*
823          * If the op is asynchronous and an i/o daemon is waiting
824          * queue the request, wake it up and wait for completion
825          * otherwise just do it ourselves.
826          */
827         if ((bp->b_flags & B_ASYNC) == 0 )
828                 error = nwfs_doio(bp, proc0.p_ucred, td);
829         return (error);
830 }
831
832 static int
833 nwfs_bmap(ap)
834         struct vop_bmap_args /* {
835                 struct vnode *a_vp;
836                 daddr_t  a_bn;
837                 struct vnode **a_vpp;
838                 daddr_t *a_bnp;
839                 int *a_runp;
840                 int *a_runb;
841         } */ *ap;
842 {
843         struct vnode *vp = ap->a_vp;
844
845         if (ap->a_vpp != NULL)
846                 *ap->a_vpp = vp;
847         if (ap->a_bnp != NULL)
848                 *ap->a_bnp = ap->a_bn * btodb(vp->v_mount->mnt_stat.f_iosize);
849         if (ap->a_runp != NULL)
850                 *ap->a_runp = 0;
851         if (ap->a_runb != NULL)
852                 *ap->a_runb = 0;
853         return (0);
854 }
855
856 int
857 nwfs_nget(struct mount *mp, ncpfid fid, struct nw_entry_info *fap,
858           struct vnode *dvp, struct vnode **vpp)
859 {
860         int error;
861         struct nwnode *newnp;
862         struct vnode *vp;
863
864         *vpp = NULL;
865         error = nwfs_allocvp(mp, fid, &vp);
866         if (error)
867                 return error;
868         newnp = VTONW(vp);
869         if (fap) {
870                 newnp->n_attr = fap->attributes;
871                 vp->v_type = newnp->n_attr & aDIR ? VDIR : VREG;
872                 nwfs_attr_cacheenter(vp, fap);
873         }
874         if (dvp) {
875                 newnp->n_parent = VTONW(dvp)->n_fid;
876                 if ((newnp->n_flag & NNEW) && vp->v_type == VDIR) {
877                         if ((dvp->v_flag & VROOT) == 0) {
878                                 newnp->n_refparent = 1;
879                                 vref(dvp);      /* vhold */
880                         }
881                 }
882         } else {
883                 if ((newnp->n_flag & NNEW) && vp->v_type == VREG)
884                         printf("new vnode '%s' borned without parent ?\n",newnp->n_name);
885         }
886         newnp->n_flag &= ~NNEW;
887         *vpp = vp;
888         return 0;
889 }
890
891 /*
892  * How to keep the brain busy ...
893  * Currently lookup routine can make two lookup for vnode. This can be
894  * avoided by reorg the code.
895  */
896 int
897 nwfs_lookup(ap)
898         struct vop_lookup_args /* {
899                 struct vnodeop_desc *a_desc;
900                 struct vnode *a_dvp;
901                 struct vnode **a_vpp;
902                 struct componentname *a_cnp;
903         } */ *ap;
904 {
905         struct componentname *cnp = ap->a_cnp;
906         struct vnode *dvp = ap->a_dvp;
907         struct vnode **vpp = ap->a_vpp;
908         int flags = cnp->cn_flags;
909         struct vnode *vp;
910         struct nwmount *nmp;
911         struct mount *mp = dvp->v_mount;
912         struct nwnode *dnp, *npp;
913         struct nw_entry_info fattr, *fap;
914         ncpfid fid;
915         int nameiop=cnp->cn_nameiop, islastcn;
916         int lockparent, wantparent, error = 0, notfound;
917         struct thread *td = cnp->cn_td;
918         char _name[cnp->cn_namelen+1];
919         bcopy(cnp->cn_nameptr,_name,cnp->cn_namelen);
920         _name[cnp->cn_namelen]=0;
921         
922         if (dvp->v_type != VDIR)
923                 return (ENOTDIR);
924         if ((flags & CNP_ISDOTDOT) && (dvp->v_flag & VROOT)) {
925                 printf("nwfs_lookup: invalid '..'\n");
926                 return EIO;
927         }
928
929         NCPVNDEBUG("%d '%s' in '%s' id=d\n", nameiop, _name, 
930                 VTONW(dvp)->n_name/*, VTONW(dvp)->n_name*/);
931
932         islastcn = flags & CNP_ISLASTCN;
933         if (islastcn && (mp->mnt_flag & MNT_RDONLY) && (nameiop != NAMEI_LOOKUP))
934                 return (EROFS);
935         if ((error = VOP_ACCESS(dvp, VEXEC, cnp->cn_cred, td)))
936                 return (error);
937         lockparent = flags & CNP_LOCKPARENT;
938         wantparent = flags & (CNP_LOCKPARENT | CNP_WANTPARENT);
939         nmp = VFSTONWFS(mp);
940         dnp = VTONW(dvp);
941 /*
942 printf("dvp %d:%d:%d\n", (int)mp, (int)dvp->v_flag & VROOT, (int)flags & CNP_ISDOTDOT);
943 */
944         error = ncp_pathcheck(cnp->cn_nameptr, cnp->cn_namelen, &nmp->m.nls, 
945             (nameiop == NAMEI_CREATE || nameiop == NAMEI_RENAME) && (nmp->m.nls.opt & NWHP_NOSTRICT) == 0);
946         if (error) 
947             return ENOENT;
948
949         error = cache_lookup(dvp, NCPNULL, vpp, NCPPNULL, cnp);
950         NCPVNDEBUG("cache_lookup returned %d\n",error);
951         if (error > 0)
952                 return error;
953         if (error) {            /* name was found */
954                 struct vattr vattr;
955                 int vpid;
956
957                 vp = *vpp;
958                 vpid = vp->v_id;
959                 if (dvp == vp) {        /* lookup on current */
960                         vref(vp);
961                         error = 0;
962                         NCPVNDEBUG("cached '.'");
963                 } else if (flags & CNP_ISDOTDOT) {
964                         VOP_UNLOCK(dvp, NULL, 0, td);   /* unlock parent */
965                         error = vget(vp, NULL, LK_EXCLUSIVE, td);
966                         if (!error && lockparent && islastcn)
967                                 error = vn_lock(dvp, NULL, LK_EXCLUSIVE, td);
968                 } else {
969                         error = vget(vp, NULL, LK_EXCLUSIVE, td);
970                         if (!lockparent || error || !islastcn)
971                                 VOP_UNLOCK(dvp, NULL, 0, td);
972                 }
973                 if (!error) {
974                         if (vpid == vp->v_id) {
975                            if (!VOP_GETATTR(vp, &vattr, td)
976                             && vattr.va_ctime.tv_sec == VTONW(vp)->n_ctime) {
977                                 if (nameiop != NAMEI_LOOKUP && islastcn)
978                                         cnp->cn_flags |= CNP_SAVENAME;
979                                 NCPVNDEBUG("use cached vnode");
980                                 return (0);
981                            }
982                            cache_purge(vp);
983                         }
984                         vput(vp);
985                         if (lockparent && dvp != vp && islastcn)
986                                 VOP_UNLOCK(dvp, NULL, 0, td);
987                 }
988                 error = vn_lock(dvp, NULL, LK_EXCLUSIVE, td);
989                 *vpp = NULLVP;
990                 if (error)
991                         return (error);
992         }
993         /* not in cache, so ...  */
994         error = 0;
995         *vpp = NULLVP;
996         fap = NULL;
997         if (flags & CNP_ISDOTDOT) {
998                 if (NWCMPF(&dnp->n_parent, &nmp->n_rootent)) {
999                         fid = nmp->n_rootent;
1000                         fap = NULL;
1001                         notfound = 0;
1002                 } else {
1003                         error = nwfs_lookupnp(nmp, dnp->n_parent, td, &npp);
1004                         if (error) {
1005                                 return error;
1006                         }
1007                         fid = dnp->n_parent;
1008                         fap = &fattr;
1009                         /*np = *npp;*/
1010                         notfound = ncp_obtain_info(nmp, npp->n_dosfid,
1011                             0, NULL, fap, td, cnp->cn_cred);
1012                 }
1013         } else {
1014                 fap = &fattr;
1015                 notfound = ncp_lookup(dvp, cnp->cn_namelen, cnp->cn_nameptr,
1016                         fap, td, cnp->cn_cred);
1017                 fid.f_id = fap->dirEntNum;
1018                 if (cnp->cn_namelen == 1 && cnp->cn_nameptr[0] == '.') {
1019                         fid.f_parent = dnp->n_fid.f_parent;
1020                 } else
1021                         fid.f_parent = dnp->n_fid.f_id;
1022                 NCPVNDEBUG("call to ncp_lookup returned=%d\n",notfound);
1023         }
1024         if (notfound && notfound < 0x80 )
1025                 return (notfound);      /* hard error */
1026         if (notfound) { /* entry not found */
1027                 /* Handle RENAME or CREATE case... */
1028                 if ((nameiop == NAMEI_CREATE || nameiop == NAMEI_RENAME) && wantparent && islastcn) {
1029                         cnp->cn_flags |= CNP_SAVENAME;
1030                         if (!lockparent)
1031                                 VOP_UNLOCK(dvp, NULL, 0, td);
1032                         return (EJUSTRETURN);
1033                 }
1034                 return ENOENT;
1035         }/* else {
1036                 NCPVNDEBUG("Found entry %s with id=%d\n", fap->entryName, fap->dirEntNum);
1037         }*/
1038         /* handle DELETE case ... */
1039         if (nameiop == NAMEI_DELETE && islastcn) {      /* delete last component */
1040                 error = VOP_ACCESS(dvp, VWRITE, cnp->cn_cred, cnp->cn_td);
1041                 if (error) return (error);
1042                 if (NWCMPF(&dnp->n_fid, &fid)) {        /* we found ourselfs */
1043                         VREF(dvp);
1044                         *vpp = dvp;
1045                         return 0;
1046                 }
1047                 error = nwfs_nget(mp, fid, fap, dvp, &vp);
1048                 if (error) return (error);
1049                 *vpp = vp;
1050                 cnp->cn_flags |= CNP_SAVENAME;  /* I free it later */
1051                 if (!lockparent) VOP_UNLOCK(dvp, NULL, 0, td);
1052                 return (0);
1053         }
1054         if (nameiop == NAMEI_RENAME && islastcn && wantparent) {
1055                 error = VOP_ACCESS(dvp, VWRITE, cnp->cn_cred, cnp->cn_td);
1056                 if (error) return (error);
1057                 if (NWCMPF(&dnp->n_fid, &fid)) return EISDIR;
1058                 error = nwfs_nget(mp, fid, fap, dvp, &vp);
1059                 if (error) return (error);
1060                 *vpp = vp;
1061                 cnp->cn_flags |= CNP_SAVENAME;
1062                 if (!lockparent)
1063                         VOP_UNLOCK(dvp, NULL, 0, td);
1064                 return (0);
1065         }
1066         if (flags & CNP_ISDOTDOT) {
1067                 VOP_UNLOCK(dvp, NULL, 0, td);   /* race to get the inode */
1068                 error = nwfs_nget(mp, fid, NULL, NULL, &vp);
1069                 if (error) {
1070                         vn_lock(dvp, NULL, LK_EXCLUSIVE | LK_RETRY, td);
1071                         return (error);
1072                 }
1073                 if (lockparent && islastcn &&
1074                     (error = vn_lock(dvp, NULL, LK_EXCLUSIVE, td))) {
1075                         vput(vp);
1076                         return (error);
1077                 }
1078                 *vpp = vp;
1079         } else if (NWCMPF(&dnp->n_fid, &fid)) {
1080                 vref(dvp);
1081                 *vpp = dvp;
1082         } else {
1083                 error = nwfs_nget(mp, fid, fap, dvp, &vp);
1084                 if (error) return (error);
1085                 *vpp = vp;
1086                 NCPVNDEBUG("lookup: getnewvp!\n");
1087                 if (!lockparent || !islastcn)
1088                         VOP_UNLOCK(dvp, NULL, 0, td);
1089         }
1090         if ((cnp->cn_flags & CNP_MAKEENTRY)/* && !islastcn*/) {
1091                 VTONW(*vpp)->n_ctime = VTONW(*vpp)->n_vattr.va_ctime.tv_sec;
1092                 cache_enter(dvp, NCPNULL, *vpp, cnp);
1093         }
1094         return (0);
1095 }