1/*-
2 * Copyright (c) 1999, 2000, 2001 Boris Popov
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 *
26 * $FreeBSD$
27 */
28#include <sys/param.h>
29#include <sys/systm.h>
30#include <sys/namei.h>
31#include <sys/kernel.h>
32#include <sys/bio.h>
33#include <sys/buf.h>
34#include <sys/fcntl.h>
35#include <sys/mount.h>
36#include <sys/unistd.h>
37#include <sys/vnode.h>
38
39#include <vm/vm.h>
40#include <vm/vm_extern.h>
41
42#include <netncp/ncp.h>
43#include <netncp/ncp_conn.h>
44#include <netncp/ncp_subr.h>
45#include <netncp/nwerror.h>
46#include <netncp/ncp_nls.h>
47
48#include <fs/nwfs/nwfs.h>
49#include <fs/nwfs/nwfs_node.h>
50#include <fs/nwfs/nwfs_subr.h>
51
52/*
53 * Prototypes for NWFS vnode operations
54 */
55static vop_create_t	nwfs_create;
56static vop_mknod_t	nwfs_mknod;
57static vop_open_t	nwfs_open;
58static vop_close_t	nwfs_close;
59static vop_access_t	nwfs_access;
60static vop_getattr_t	nwfs_getattr;
61static vop_setattr_t	nwfs_setattr;
62static vop_read_t	nwfs_read;
63static vop_write_t	nwfs_write;
64static vop_fsync_t	nwfs_fsync;
65static vop_remove_t	nwfs_remove;
66static vop_link_t	nwfs_link;
67static vop_lookup_t	nwfs_lookup;
68static vop_rename_t	nwfs_rename;
69static vop_mkdir_t	nwfs_mkdir;
70static vop_rmdir_t	nwfs_rmdir;
71static vop_symlink_t	nwfs_symlink;
72static vop_readdir_t	nwfs_readdir;
73static vop_strategy_t	nwfs_strategy;
74static vop_print_t	nwfs_print;
75static vop_pathconf_t	nwfs_pathconf;
76
77/* Global vfs data structures for nwfs */
78struct vop_vector nwfs_vnodeops = {
79	.vop_default =		&default_vnodeops,
80
81	.vop_access =		nwfs_access,
82	.vop_close =		nwfs_close,
83	.vop_create =		nwfs_create,
84	.vop_fsync =		nwfs_fsync,
85	.vop_getattr =		nwfs_getattr,
86	.vop_getpages =		nwfs_getpages,
87	.vop_inactive =		nwfs_inactive,
88	.vop_ioctl =		nwfs_ioctl,
89	.vop_link =		nwfs_link,
90	.vop_lookup =		nwfs_lookup,
91	.vop_mkdir =		nwfs_mkdir,
92	.vop_mknod =		nwfs_mknod,
93	.vop_open =		nwfs_open,
94	.vop_pathconf =		nwfs_pathconf,
95	.vop_print =		nwfs_print,
96	.vop_putpages =		nwfs_putpages,
97	.vop_read =		nwfs_read,
98	.vop_readdir =		nwfs_readdir,
99	.vop_reclaim =		nwfs_reclaim,
100	.vop_remove =		nwfs_remove,
101	.vop_rename =		nwfs_rename,
102	.vop_rmdir =		nwfs_rmdir,
103	.vop_setattr =		nwfs_setattr,
104	.vop_strategy =		nwfs_strategy,
105	.vop_symlink =		nwfs_symlink,
106	.vop_write =		nwfs_write,
107};
108
109/*
110 * nwfs_access vnode op
111 */
112static int
113nwfs_access(ap)
114	struct vop_access_args /* {
115		struct vnode *a_vp;
116		accmode_t a_accmode;
117		struct ucred *a_cred;
118		struct thread *td;
119	} */ *ap;
120{
121	struct vnode *vp = ap->a_vp;
122	mode_t mpmode;
123	struct nwmount *nmp = VTONWFS(vp);
124
125	NCPVNDEBUG("\n");
126	if ((ap->a_accmode & VWRITE) && (vp->v_mount->mnt_flag & MNT_RDONLY)) {
127		switch (vp->v_type) {
128		    case VREG: case VDIR: case VLNK:
129			return (EROFS);
130		    default:
131			break;
132		}
133	}
134	mpmode = vp->v_type == VREG ? nmp->m.file_mode :
135	    nmp->m.dir_mode;
136        return (vaccess(vp->v_type, mpmode, nmp->m.uid,
137            nmp->m.gid, ap->a_accmode, ap->a_cred, NULL));
138}
139/*
140 * nwfs_open vnode op
141 */
142/* ARGSUSED */
143static int
144nwfs_open(ap)
145	struct vop_open_args /* {
146		struct vnode *a_vp;
147		int  a_mode;
148		struct ucred *a_cred;
149		struct thread *td;
150	} */ *ap;
151{
152	struct vnode *vp = ap->a_vp;
153	int mode = ap->a_mode;
154	struct nwnode *np = VTONW(vp);
155	struct ncp_open_info no;
156	struct nwmount *nmp = VTONWFS(vp);
157	struct vattr vattr;
158	int error, nwm;
159
160	NCPVNDEBUG("%s,%d\n", np->n_name, np->opened);
161	if (vp->v_type != VREG && vp->v_type != VDIR) {
162		NCPFATAL("open vtype = %d\n", vp->v_type);
163		return (EACCES);
164	}
165	if (vp->v_type == VDIR) return 0;	/* nothing to do now */
166	if (np->n_flag & NMODIFIED) {
167		if ((error = nwfs_vinvalbuf(vp, ap->a_td)) == EINTR)
168			return (error);
169		np->n_atime = 0;
170		error = VOP_GETATTR(vp, &vattr, ap->a_cred);
171		if (error) return (error);
172		np->n_mtime = vattr.va_mtime.tv_sec;
173	} else {
174		error = VOP_GETATTR(vp, &vattr, ap->a_cred);
175		if (error) return (error);
176		if (np->n_mtime != vattr.va_mtime.tv_sec) {
177			if ((error = nwfs_vinvalbuf(vp, ap->a_td)) == EINTR)
178				return (error);
179			np->n_mtime = vattr.va_mtime.tv_sec;
180		}
181	}
182	if (np->opened) {
183		np->opened++;
184		return 0;
185	}
186	nwm = AR_READ;
187	if ((vp->v_mount->mnt_flag & MNT_RDONLY) == 0)
188		nwm |= AR_WRITE;
189	error = ncp_open_create_file_or_subdir(nmp, vp, 0, NULL, OC_MODE_OPEN,
190					       0, nwm, &no, ap->a_td, ap->a_cred);
191	if (error) {
192		if (mode & FWRITE)
193			return EACCES;
194		nwm = AR_READ;
195		error = ncp_open_create_file_or_subdir(nmp, vp, 0, NULL, OC_MODE_OPEN, 0,
196						   nwm, &no, ap->a_td, ap->a_cred);
197	}
198	if (!error) {
199		np->opened++;
200		np->n_fh = no.fh;
201		np->n_origfh = no.origfh;
202	}
203	np->n_atime = 0;
204	return (error);
205}
206
207static int
208nwfs_close(ap)
209	struct vop_close_args /* {
210		struct vnodeop_desc *a_desc;
211		struct vnode *a_vp;
212		int  a_fflag;
213		struct ucred *a_cred;
214		struct thread *td;
215	} */ *ap;
216{
217	struct vnode *vp = ap->a_vp;
218	struct nwnode *np = VTONW(vp);
219	int error;
220
221	NCPVNDEBUG("name=%s,pid=%d,c=%d\n", np->n_name, ap->a_td->td_proc->p_pid,
222			np->opened);
223
224	if (vp->v_type == VDIR) return 0;	/* nothing to do now */
225	error = 0;
226	VI_LOCK(vp);
227	if (np->opened == 0) {
228		VI_UNLOCK(vp);
229		return 0;
230	}
231	VI_UNLOCK(vp);
232	error = nwfs_vinvalbuf(vp, ap->a_td);
233	VI_LOCK(vp);
234	if (np->opened == 0) {
235		VI_UNLOCK(vp);
236		return 0;
237	}
238	if (--np->opened == 0) {
239		VI_UNLOCK(vp);
240		error = ncp_close_file(NWFSTOCONN(VTONWFS(vp)), &np->n_fh,
241		   ap->a_td, ap->a_cred);
242	} else
243		VI_UNLOCK(vp);
244	np->n_atime = 0;
245	return (error);
246}
247
248/*
249 * nwfs_getattr call from vfs.
250 */
251static int
252nwfs_getattr(ap)
253	struct vop_getattr_args /* {
254		struct vnode *a_vp;
255		struct vattr *a_vap;
256		struct ucred *a_cred;
257	} */ *ap;
258{
259	struct vnode *vp = ap->a_vp;
260	struct nwnode *np = VTONW(vp);
261	struct vattr *va=ap->a_vap;
262	struct nwmount *nmp = VTONWFS(vp);
263	struct thread *td = curthread;
264	struct nw_entry_info fattr;
265	int error;
266	u_int32_t oldsize;
267
268	NCPVNDEBUG("%lx:%d: '%s' %d\n", (long)vp, nmp->n_volume, np->n_name, (vp->v_vflag & VV_ROOT) != 0);
269	error = nwfs_attr_cachelookup(vp, va);
270	if (!error) return 0;
271	NCPVNDEBUG("not in cache\n");
272	oldsize = np->n_size;
273	if (np->n_flag & NVOLUME) {
274		error = ncp_obtain_info(nmp, np->n_fid.f_id, 0, NULL, &fattr,
275		    td, ap->a_cred);
276	} else {
277		error = ncp_obtain_info(nmp, np->n_fid.f_parent, np->n_nmlen,
278		    np->n_name, &fattr, td, ap->a_cred);
279	}
280	if (error) {
281		NCPVNDEBUG("error %d\n", error);
282		return error;
283	}
284	nwfs_attr_cacheenter(vp, &fattr);
285	*va = np->n_vattr;
286	if (np->opened)
287		np->n_size = oldsize;
288	return (0);
289}
290/*
291 * nwfs_setattr call from vfs.
292 */
293static int
294nwfs_setattr(ap)
295	struct vop_setattr_args /* {
296		struct vnode *a_vp;
297		struct vattr *a_vap;
298		struct ucred *a_cred;
299	} */ *ap;
300{
301	struct vnode *vp = ap->a_vp;
302	struct nwnode *np = VTONW(vp);
303	struct vattr *vap = ap->a_vap;
304	u_quad_t tsize=0;
305	int error = 0;
306
307	NCPVNDEBUG("\n");
308	if (vap->va_flags != VNOVAL)
309		return (EOPNOTSUPP);
310	/*
311	 * Disallow write attempts if the filesystem is mounted read-only.
312	 */
313  	if ((vap->va_uid != (uid_t)VNOVAL || vap->va_gid != (gid_t)VNOVAL ||
314	     vap->va_atime.tv_sec != VNOVAL || vap->va_mtime.tv_sec != VNOVAL ||
315	     vap->va_mode != (mode_t)VNOVAL) &&(vp->v_mount->mnt_flag & MNT_RDONLY))
316		return (EROFS);
317	if (vap->va_size != VNOVAL) {
318 		switch (vp->v_type) {
319 		case VDIR:
320 			return (EISDIR);
321 		case VREG:
322			/*
323			 * Disallow write attempts if the filesystem is
324			 * mounted read-only.
325			 */
326			if (vp->v_mount->mnt_flag & MNT_RDONLY)
327				return (EROFS);
328			vnode_pager_setsize(vp, (u_long)vap->va_size);
329 			tsize = np->n_size;
330 			np->n_size = vap->va_size;
331			break;
332 		default:
333			return EINVAL;
334  		};
335  	}
336	error = ncp_setattr(vp, vap, ap->a_cred, curthread);
337	if (error && vap->va_size != VNOVAL) {
338		np->n_size = tsize;
339		vnode_pager_setsize(vp, (u_long)tsize);
340	}
341	np->n_atime = 0;	/* invalidate cache */
342	VOP_GETATTR(vp, vap, ap->a_cred);
343	np->n_mtime = vap->va_mtime.tv_sec;
344	return (0);
345}
346/*
347 * nwfs_read call.
348 */
349static int
350nwfs_read(ap)
351	struct vop_read_args /* {
352		struct vnode *a_vp;
353		struct uio *a_uio;
354		int  a_ioflag;
355		struct ucred *a_cred;
356	} */ *ap;
357{
358	struct vnode *vp = ap->a_vp;
359	struct uio *uio=ap->a_uio;
360	int error;
361	NCPVNDEBUG("nwfs_read:\n");
362
363	if (vp->v_type != VREG && vp->v_type != VDIR)
364		return (EPERM);
365	error = nwfs_readvnode(vp, uio, ap->a_cred);
366	return error;
367}
368
369static int
370nwfs_write(ap)
371	struct vop_write_args /* {
372		struct vnode *a_vp;
373		struct uio *a_uio;
374		int  a_ioflag;
375		struct ucred *a_cred;
376	} */ *ap;
377{
378	struct vnode *vp = ap->a_vp;
379	struct uio *uio = ap->a_uio;
380	int error;
381
382	NCPVNDEBUG("%d,ofs=%d,sz=%d\n", vp->v_type, (int)uio->uio_offset, uio->uio_resid);
383
384	if (vp->v_type != VREG)
385		return (EPERM);
386	error = nwfs_writevnode(vp, uio, ap->a_cred, ap->a_ioflag);
387	return(error);
388}
389/*
390 * nwfs_create call
391 * Create a regular file. On entry the directory to contain the file being
392 * created is locked.  We must release before we return. We must also free
393 * the pathname buffer pointed at by cnp->cn_pnbuf, always on error, or
394 * only if the SAVESTART bit in cn_flags is clear on success.
395 */
396static int
397nwfs_create(ap)
398	struct vop_create_args /* {
399		struct vnode *a_dvp;
400		struct vnode **a_vpp;
401		struct componentname *a_cnp;
402		struct vattr *a_vap;
403	} */ *ap;
404{
405	struct vnode *dvp = ap->a_dvp;
406	struct vattr *vap = ap->a_vap;
407	struct vnode **vpp=ap->a_vpp;
408	struct componentname *cnp = ap->a_cnp;
409	struct vnode *vp = (struct vnode *)0;
410	int error = 0, fmode;
411	struct vattr vattr;
412	struct nwnode *np;
413	struct ncp_open_info no;
414	struct nwmount *nmp=VTONWFS(dvp);
415	ncpfid fid;
416
417
418	NCPVNDEBUG("\n");
419	*vpp = NULL;
420	if (vap->va_type == VSOCK)
421		return (EOPNOTSUPP);
422	if ((error = VOP_GETATTR(dvp, &vattr, cnp->cn_cred)))
423		return (error);
424	fmode = AR_READ | AR_WRITE;
425/*	if (vap->va_vaflags & VA_EXCLUSIVE)
426		fmode |= AR_DENY_READ | AR_DENY_WRITE;*/
427
428	error = ncp_open_create_file_or_subdir(nmp, dvp, cnp->cn_namelen, cnp->cn_nameptr,
429			   OC_MODE_CREATE | OC_MODE_OPEN | OC_MODE_REPLACE,
430			   0, fmode, &no, cnp->cn_thread, cnp->cn_cred);
431	if (!error) {
432		error = ncp_close_file(NWFSTOCONN(nmp), &no.fh, cnp->cn_thread, cnp->cn_cred);
433		fid.f_parent = VTONW(dvp)->n_fid.f_id;
434		fid.f_id = no.fattr.dirEntNum;
435		error = nwfs_nget(VTOVFS(dvp), fid, &no.fattr, dvp, &vp);
436		if (!error) {
437			np = VTONW(vp);
438			np->opened = 0;
439			*vpp = vp;
440		}
441		if (cnp->cn_flags & MAKEENTRY)
442			cache_enter(dvp, vp, cnp);
443	}
444	return (error);
445}
446
447/*
448 * nwfs_remove call. It isn't possible to emulate UFS behaivour because
449 * NetWare doesn't allow delete/rename operations on an opened file.
450 */
451static int
452nwfs_remove(ap)
453	struct vop_remove_args /* {
454		struct vnodeop_desc *a_desc;
455		struct vnode * a_dvp;
456		struct vnode * a_vp;
457		struct componentname * a_cnp;
458	} */ *ap;
459{
460	struct vnode *vp = ap->a_vp;
461	struct vnode *dvp = ap->a_dvp;
462	struct componentname *cnp = ap->a_cnp;
463	struct nwnode *np = VTONW(vp);
464	struct nwmount *nmp = VTONWFS(vp);
465	int error;
466
467	if (vp->v_type == VDIR || np->opened || vrefcnt(vp) != 1)
468		return EPERM;
469	cache_purge(vp);
470	error = ncp_DeleteNSEntry(nmp, VTONW(dvp)->n_fid.f_id,
471	    cnp->cn_namelen, cnp->cn_nameptr, cnp->cn_thread, cnp->cn_cred);
472	if (error == 0)
473		np->n_flag |= NSHOULDFREE;
474	else if (error == 0x899c)
475		error = EACCES;
476	return (error);
477}
478
479/*
480 * nwfs_file rename call
481 */
482static int
483nwfs_rename(ap)
484	struct vop_rename_args  /* {
485		struct vnode *a_fdvp;
486		struct vnode *a_fvp;
487		struct componentname *a_fcnp;
488		struct vnode *a_tdvp;
489		struct vnode *a_tvp;
490		struct componentname *a_tcnp;
491	} */ *ap;
492{
493	struct vnode *fvp = ap->a_fvp;
494	struct vnode *tvp = ap->a_tvp;
495	struct vnode *fdvp = ap->a_fdvp;
496	struct vnode *tdvp = ap->a_tdvp;
497	struct componentname *tcnp = ap->a_tcnp;
498	struct componentname *fcnp = ap->a_fcnp;
499	struct nwmount *nmp=VTONWFS(fvp);
500	u_int16_t oldtype = 6;
501	int error=0;
502
503	/* Check for cross-device rename */
504	if ((fvp->v_mount != tdvp->v_mount) ||
505	    (tvp && (fvp->v_mount != tvp->v_mount))) {
506		error = EXDEV;
507		goto out;
508	}
509
510	if (tvp && vrefcnt(tvp) > 1) {
511		error = EBUSY;
512		goto out;
513	}
514	if (fvp->v_type == VDIR) {
515		oldtype |= NW_TYPE_SUBDIR;
516	} else if (fvp->v_type == VREG) {
517		oldtype |= NW_TYPE_FILE;
518	} else {
519		error = EINVAL;
520		goto out;
521	}
522	if (tvp && tvp != fvp) {
523		error = ncp_DeleteNSEntry(nmp, VTONW(tdvp)->n_fid.f_id,
524		    tcnp->cn_namelen, tcnp->cn_nameptr,
525		    tcnp->cn_thread, tcnp->cn_cred);
526		if (error == 0x899c) error = EACCES;
527		if (error)
528			goto out_cacherem;
529	}
530	error = ncp_nsrename(NWFSTOCONN(nmp), nmp->n_volume, nmp->name_space,
531		oldtype, &nmp->m.nls,
532		VTONW(fdvp)->n_fid.f_id, fcnp->cn_nameptr, fcnp->cn_namelen,
533		VTONW(tdvp)->n_fid.f_id, tcnp->cn_nameptr, tcnp->cn_namelen,
534		tcnp->cn_thread, tcnp->cn_cred);
535
536	if (error == 0x8992)
537		error = EEXIST;
538	if (fvp->v_type == VDIR) {
539		if (tvp != NULL && tvp->v_type == VDIR)
540			cache_purge(tdvp);
541		cache_purge(fdvp);
542	}
543out_cacherem:
544	nwfs_attr_cacheremove(fdvp);
545	nwfs_attr_cacheremove(tdvp);
546	nwfs_attr_cacheremove(fvp);
547out:
548	if (tdvp == tvp)
549		vrele(tdvp);
550	else
551		vput(tdvp);
552	if (tvp)
553		vput(tvp);
554	vrele(fdvp);
555	vrele(fvp);
556	if (tvp)
557		nwfs_attr_cacheremove(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 */
570static int
571nwfs_link(ap)
572	struct vop_link_args /* {
573		struct vnode *a_tdvp;
574		struct vnode *a_vp;
575		struct componentname *a_cnp;
576	} */ *ap;
577{
578	return EOPNOTSUPP;
579}
580
581/*
582 * nwfs_symlink link create call
583 * Netware filesystems don't know what symlinks are.
584 */
585static int
586nwfs_symlink(ap)
587	struct vop_symlink_args /* {
588		struct vnode *a_dvp;
589		struct vnode **a_vpp;
590		struct componentname *a_cnp;
591		struct vattr *a_vap;
592		char *a_target;
593	} */ *ap;
594{
595	return (EOPNOTSUPP);
596}
597
598static int nwfs_mknod(ap)
599	struct vop_mknod_args /* {
600	} */ *ap;
601{
602	return (EOPNOTSUPP);
603}
604
605/*
606 * nwfs_mkdir call
607 */
608static int
609nwfs_mkdir(ap)
610	struct vop_mkdir_args /* {
611		struct vnode *a_dvp;
612		struct vnode **a_vpp;
613		struct componentname *a_cnp;
614		struct vattr *a_vap;
615	} */ *ap;
616{
617	struct vnode *dvp = ap->a_dvp;
618/*	struct vattr *vap = ap->a_vap;*/
619	struct componentname *cnp = ap->a_cnp;
620	int len=cnp->cn_namelen;
621	struct ncp_open_info no;
622	struct vnode *newvp = (struct vnode *)0;
623	ncpfid fid;
624	int error = 0;
625	struct vattr vattr;
626	char *name=cnp->cn_nameptr;
627
628	if ((error = VOP_GETATTR(dvp, &vattr, cnp->cn_cred)))
629		return (error);
630	if ((name[0] == '.') && ((len == 1) || ((len == 2) && (name[1] == '.')))) {
631		return EEXIST;
632	}
633	if (ncp_open_create_file_or_subdir(VTONWFS(dvp), dvp, cnp->cn_namelen,
634			cnp->cn_nameptr, OC_MODE_CREATE, aDIR, 0xffff,
635			&no, cnp->cn_thread, cnp->cn_cred) != 0) {
636		error = EACCES;
637	} else {
638		error = 0;
639        }
640	if (!error) {
641		fid.f_parent = VTONW(dvp)->n_fid.f_id;
642		fid.f_id = no.fattr.dirEntNum;
643		error = nwfs_nget(VTOVFS(dvp), fid, &no.fattr, dvp, &newvp);
644		if (!error) {
645			newvp->v_type = VDIR;
646			*ap->a_vpp = newvp;
647		}
648	}
649	return (error);
650}
651
652/*
653 * nwfs_remove directory call
654 */
655static int
656nwfs_rmdir(ap)
657	struct vop_rmdir_args /* {
658		struct vnode *a_dvp;
659		struct vnode *a_vp;
660		struct componentname *a_cnp;
661	} */ *ap;
662{
663	struct vnode *vp = ap->a_vp;
664	struct vnode *dvp = ap->a_dvp;
665	struct componentname *cnp = ap->a_cnp;
666	struct nwnode *np = VTONW(vp);
667	struct nwmount *nmp = VTONWFS(vp);
668	struct nwnode *dnp = VTONW(dvp);
669	int error = EIO;
670
671	if (dvp == vp)
672		return EINVAL;
673
674	error = ncp_DeleteNSEntry(nmp, dnp->n_fid.f_id,
675		cnp->cn_namelen, cnp->cn_nameptr, cnp->cn_thread, cnp->cn_cred);
676	if (error == 0)
677		np->n_flag |= NSHOULDFREE;
678	else if (error == NWE_DIR_NOT_EMPTY)
679		error = ENOTEMPTY;
680	dnp->n_flag |= NMODIFIED;
681	nwfs_attr_cacheremove(dvp);
682	cache_purge(dvp);
683	cache_purge(vp);
684	return (error);
685}
686
687/*
688 * nwfs_readdir call
689 */
690static int
691nwfs_readdir(ap)
692	struct vop_readdir_args /* {
693		struct vnode *a_vp;
694		struct uio *a_uio;
695		struct ucred *a_cred;
696		int *a_eofflag;
697		u_long *a_cookies;
698		int a_ncookies;
699	} */ *ap;
700{
701	struct vnode *vp = ap->a_vp;
702	struct uio *uio = ap->a_uio;
703	int error;
704
705	if (vp->v_type != VDIR)
706		return (EPERM);
707	if (ap->a_ncookies) {
708		printf("nwfs_readdir: no support for cookies now...");
709		return (EOPNOTSUPP);
710	}
711
712	error = nwfs_readvnode(vp, uio, ap->a_cred);
713	return error;
714}
715/* ARGSUSED */
716static int
717nwfs_fsync(ap)
718	struct vop_fsync_args /* {
719		struct vnodeop_desc *a_desc;
720		struct vnode * a_vp;
721		struct ucred * a_cred;
722		int  a_waitfor;
723		struct thread *a_td;
724	} */ *ap;
725{
726/*	return (nfs_flush(ap->a_vp, ap->a_cred, ap->a_waitfor, ap->a_td, 1));*/
727    return (0);
728}
729
730/* ARGSUSED */
731static
732int nwfs_print (ap)
733	struct vop_print_args /* {
734		struct vnode *a_vp;
735	} */ *ap;
736{
737	struct vnode *vp = ap->a_vp;
738	struct nwnode *np = VTONW(vp);
739
740	printf("\tnwfs node: name = '%s', fid = %d, pfid = %d\n",
741	    np->n_name, np->n_fid.f_id, np->n_fid.f_parent);
742	return (0);
743}
744
745static int nwfs_pathconf (ap)
746	struct vop_pathconf_args  /* {
747	struct vnode *vp;
748	int name;
749	register_t *retval;
750	} */ *ap;
751{
752	int name=ap->a_name, error=0;
753	register_t *retval=ap->a_retval;
754
755	switch(name){
756		case _PC_LINK_MAX:
757		        *retval=0;
758			break;
759		case _PC_NAME_MAX:
760			*retval=NCP_MAX_FILENAME; /* XXX from nwfsnode */
761			break;
762		case _PC_PATH_MAX:
763			*retval=NCP_MAXPATHLEN; /* XXX from nwfsnode */
764			break;
765		default:
766			error=EINVAL;
767	}
768	return(error);
769}
770
771static int nwfs_strategy (ap)
772	struct vop_strategy_args /* {
773	struct buf *a_bp
774	} */ *ap;
775{
776	struct buf *bp=ap->a_bp;
777	struct ucred *cr;
778	struct thread *td;
779
780	NCPVNDEBUG("\n");
781	if (bp->b_flags & B_ASYNC)
782		td = (struct thread *)0;
783	else
784		td = curthread;	/* XXX */
785	if (bp->b_iocmd == BIO_READ)
786		cr = bp->b_rcred;
787	else
788		cr = bp->b_wcred;
789	/*
790	 * If the op is asynchronous and an i/o daemon is waiting
791	 * queue the request, wake it up and wait for completion
792	 * otherwise just do it ourselves.
793	 */
794	if ((bp->b_flags & B_ASYNC) == 0 )
795		(void)nwfs_doio(ap->a_vp, bp, cr, td);
796	return (0);
797}
798
799
800/*
801 * How to keep the brain busy ...
802 * Currently lookup routine can make two lookup for vnode. This can be
803 * avoided by reorg the code.
804 */
805int
806nwfs_lookup(ap)
807	struct vop_lookup_args /* {
808		struct vnodeop_desc *a_desc;
809		struct vnode *a_dvp;
810		struct vnode **a_vpp;
811		struct componentname *a_cnp;
812	} */ *ap;
813{
814	struct componentname *cnp = ap->a_cnp;
815	struct vnode *dvp = ap->a_dvp;
816	struct vnode **vpp = ap->a_vpp;
817	int flags = cnp->cn_flags;
818	struct vnode *vp;
819	struct nwmount *nmp;
820	struct mount *mp = dvp->v_mount;
821	struct nwnode *dnp, *npp;
822	struct nw_entry_info fattr, *fap;
823	ncpfid fid;
824	int nameiop=cnp->cn_nameiop, islastcn;
825	int error = 0, notfound;
826	struct thread *td = cnp->cn_thread;
827	char _name[cnp->cn_namelen+1];
828	bcopy(cnp->cn_nameptr, _name, cnp->cn_namelen);
829	_name[cnp->cn_namelen]=0;
830
831	if (dvp->v_type != VDIR)
832		return (ENOTDIR);
833	if ((flags & ISDOTDOT) && (dvp->v_vflag & VV_ROOT)) {
834		printf("nwfs_lookup: invalid '..'\n");
835		return EIO;
836	}
837
838	NCPVNDEBUG("%d '%s' in '%s' id=d\n", nameiop, _name,
839		VTONW(dvp)->n_name/*, VTONW(dvp)->n_name*/);
840
841	islastcn = flags & ISLASTCN;
842	if (islastcn && (mp->mnt_flag & MNT_RDONLY) && (nameiop != LOOKUP))
843		return (EROFS);
844	if ((error = VOP_ACCESS(dvp, VEXEC, cnp->cn_cred, td)))
845		return (error);
846	nmp = VFSTONWFS(mp);
847	dnp = VTONW(dvp);
848/*
849printf("dvp %d:%d:%d\n", (int)mp, (int)dvp->v_vflag & VV_ROOT, (int)flags & ISDOTDOT);
850*/
851	error = ncp_pathcheck(cnp->cn_nameptr, cnp->cn_namelen, &nmp->m.nls,
852	    (nameiop == CREATE || nameiop == RENAME) && (nmp->m.nls.opt & NWHP_NOSTRICT) == 0);
853	if (error)
854	    return ENOENT;
855
856	error = cache_lookup(dvp, vpp, cnp);
857	NCPVNDEBUG("cache_lookup returned %d\n", error);
858	if (error > 0)
859		return error;
860	if (error) {		/* name was found */
861		struct vattr vattr;
862
863		vp = *vpp;
864		if (!VOP_GETATTR(vp, &vattr, cnp->cn_cred) &&
865		    vattr.va_ctime.tv_sec == VTONW(vp)->n_ctime) {
866			if (nameiop != LOOKUP && islastcn)
867				cnp->cn_flags |= SAVENAME;
868			NCPVNDEBUG("use cached vnode");
869			return (0);
870		}
871		cache_purge(vp);
872		if (vp != dvp)
873			vput(vp);
874		else
875			vrele(vp);
876		*vpp = NULLVP;
877	}
878	/* not in cache, so ...  */
879	error = 0;
880	*vpp = NULLVP;
881	fap = NULL;
882	if (flags & ISDOTDOT) {
883		if (NWCMPF(&dnp->n_parent, &nmp->n_rootent)) {
884			fid = nmp->n_rootent;
885			fap = NULL;
886			notfound = 0;
887		} else {
888			error = nwfs_lookupnp(nmp, dnp->n_parent, td, &npp);
889			if (error) {
890				return error;
891			}
892			fid = dnp->n_parent;
893			fap = &fattr;
894			/*np = *npp;*/
895			notfound = ncp_obtain_info(nmp, npp->n_dosfid,
896			    0, NULL, fap, td, cnp->cn_cred);
897		}
898	} else {
899		fap = &fattr;
900		notfound = ncp_lookup(dvp, cnp->cn_namelen, cnp->cn_nameptr,
901			fap, td, cnp->cn_cred);
902		fid.f_id = fap->dirEntNum;
903		if (cnp->cn_namelen == 1 && cnp->cn_nameptr[0] == '.') {
904			fid.f_parent = dnp->n_fid.f_parent;
905		} else
906			fid.f_parent = dnp->n_fid.f_id;
907		NCPVNDEBUG("call to ncp_lookup returned=%d\n", notfound);
908	}
909	if (notfound && notfound < 0x80 )
910		return (notfound);	/* hard error */
911	if (notfound) { /* entry not found */
912		/* Handle RENAME or CREATE case... */
913		if ((nameiop == CREATE || nameiop == RENAME) && islastcn) {
914			cnp->cn_flags |= SAVENAME;
915			return (EJUSTRETURN);
916		}
917		return ENOENT;
918	}/* else {
919		NCPVNDEBUG("Found entry %s with id=%d\n", fap->entryName, fap->dirEntNum);
920	}*/
921	/* handle DELETE case ... */
922	if (nameiop == DELETE && islastcn) { 	/* delete last component */
923		error = VOP_ACCESS(dvp, VWRITE, cnp->cn_cred, cnp->cn_thread);
924		if (error) return (error);
925		if (NWCMPF(&dnp->n_fid, &fid)) {	/* we found ourselfs */
926			VREF(dvp);
927			*vpp = dvp;
928			return 0;
929		}
930		error = nwfs_nget(mp, fid, fap, dvp, &vp);
931		if (error) return (error);
932		*vpp = vp;
933		cnp->cn_flags |= SAVENAME;	/* I free it later */
934		return (0);
935	}
936	if (nameiop == RENAME && islastcn) {
937		error = VOP_ACCESS(dvp, VWRITE, cnp->cn_cred, cnp->cn_thread);
938		if (error) return (error);
939		if (NWCMPF(&dnp->n_fid, &fid)) return EISDIR;
940		error = nwfs_nget(mp, fid, fap, dvp, &vp);
941		if (error) return (error);
942		*vpp = vp;
943		cnp->cn_flags |= SAVENAME;
944		return (0);
945	}
946	if (flags & ISDOTDOT) {
947		VOP_UNLOCK(dvp, 0);		/* race to get the inode */
948		error = nwfs_nget(mp, fid, NULL, NULL, &vp);
949		vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY);
950		if (error)
951			return (error);
952		*vpp = vp;
953	} else if (NWCMPF(&dnp->n_fid, &fid)) {
954		vref(dvp);
955		*vpp = dvp;
956	} else {
957		error = nwfs_nget(mp, fid, fap, dvp, &vp);
958		if (error) return (error);
959		*vpp = vp;
960		NCPVNDEBUG("lookup: getnewvp!\n");
961	}
962	if ((cnp->cn_flags & MAKEENTRY)/* && !islastcn*/) {
963		VTONW(*vpp)->n_ctime = VTONW(*vpp)->n_vattr.va_ctime.tv_sec;
964		cache_enter(dvp, *vpp, cnp);
965	}
966	return (0);
967}
968