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