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