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