1/*-
2 *             Coda: an Experimental Distributed File System
3 *                              Release 3.1
4 *
5 *           Copyright (c) 1987-1998 Carnegie Mellon University
6 *                          All Rights Reserved
7 *
8 * Permission  to  use, copy, modify and distribute this software and its
9 * documentation is hereby granted,  provided  that  both  the  copyright
10 * notice  and  this  permission  notice  appear  in  all  copies  of the
11 * software, derivative works or  modified  versions,  and  any  portions
12 * thereof, and that both notices appear in supporting documentation, and
13 * that credit is given to Carnegie Mellon University  in  all  documents
14 * and publicity pertaining to direct or indirect use of this code or its
15 * derivatives.
16 *
17 * CODA IS AN EXPERIMENTAL SOFTWARE SYSTEM AND IS  KNOWN  TO  HAVE  BUGS,
18 * SOME  OF  WHICH MAY HAVE SERIOUS CONSEQUENCES.  CARNEGIE MELLON ALLOWS
19 * FREE USE OF THIS SOFTWARE IN ITS "AS IS" CONDITION.   CARNEGIE  MELLON
20 * DISCLAIMS  ANY  LIABILITY  OF  ANY  KIND  FOR  ANY  DAMAGES WHATSOEVER
21 * RESULTING DIRECTLY OR INDIRECTLY FROM THE USE OF THIS SOFTWARE  OR  OF
22 * ANY DERIVATIVE WORK.
23 *
24 * Carnegie  Mellon  encourages  users  of  this  software  to return any
25 * improvements or extensions that  they  make,  and  to  grant  Carnegie
26 * Mellon the rights to redistribute these changes without encumbrance.
27 *
28 *  	@(#) src/sys/cfs/coda_vfsops.c,v 1.1.1.1 1998/08/29 21:14:52 rvb Exp $
29 */
30
31/*-
32 * Mach Operating System
33 * Copyright (c) 1989 Carnegie-Mellon University
34 * All rights reserved.  The CMU software License Agreement specifies
35 * the terms and conditions for use and redistribution.
36 */
37
38/*
39 * This code was written for the Coda filesystem at Carnegie Mellon
40 * University.  Contributers include David Steere, James Kistler, and
41 * M. Satyanarayanan.
42 */
43
44#include <sys/cdefs.h>
45__FBSDID("$FreeBSD$");
46
47#include <sys/param.h>
48#include <sys/systm.h>
49#include <sys/conf.h>
50#include <sys/fcntl.h>
51#include <sys/kernel.h>
52#include <sys/lock.h>
53#include <sys/malloc.h>
54#include <sys/mount.h>
55#include <sys/namei.h>
56#include <sys/proc.h>
57
58#include <fs/coda/coda.h>
59#include <fs/coda/cnode.h>
60#include <fs/coda/coda_vfsops.h>
61#include <fs/coda/coda_venus.h>
62#include <fs/coda/coda_subr.h>
63#include <fs/coda/coda_opstats.h>
64
65MALLOC_DEFINE(M_CODA, "coda", "Various Coda Structures");
66
67int codadebug = 0;
68int coda_vfsop_print_entry = 0;
69#define	ENTRY do {							\
70	if (coda_vfsop_print_entry)					\
71		myprintf(("Entered %s\n", __func__));			\
72} while (0)
73
74struct vnode *coda_ctlvp;
75
76/*
77 * Structure to keep statistics of internally generated/satisfied calls.
78 */
79static struct coda_op_stats coda_vfsopstats[CODA_VFSOPS_SIZE];
80
81#define	MARK_ENTRY(op)		(coda_vfsopstats[op].entries++)
82#define	MARK_INT_SAT(op)	(coda_vfsopstats[op].sat_intrn++)
83#define	MARK_INT_FAIL(op)	(coda_vfsopstats[op].unsat_intrn++)
84#define	MARK_INT_GEN(op)	(coda_vfsopstats[op].gen_intrn++)
85
86int
87coda_vfsopstats_init(void)
88{
89	int i;
90
91	for (i=0; i<CODA_VFSOPS_SIZE;i++) {
92		coda_vfsopstats[i].opcode = i;
93		coda_vfsopstats[i].entries = 0;
94		coda_vfsopstats[i].sat_intrn = 0;
95		coda_vfsopstats[i].unsat_intrn = 0;
96		coda_vfsopstats[i].gen_intrn = 0;
97	}
98	return (0);
99}
100
101static const char *coda_opts[] = { "from", NULL };
102/*
103 * cfs mount vfsop
104 *
105 * Set up mount info record and attach it to vfs struct.
106 */
107/*ARGSUSED*/
108int
109coda_mount(struct mount *vfsp)
110{
111	struct vnode *dvp;
112	struct cnode *cp;
113	struct cdev *dev;
114	struct coda_mntinfo *mi;
115	struct vnode *rootvp;
116	struct CodaFid rootfid = INVAL_FID;
117	struct CodaFid ctlfid = CTL_FID;
118	int error;
119	struct nameidata ndp;
120	ENTRY;
121	char *from;
122
123	if (vfs_filteropt(vfsp->mnt_optnew, coda_opts))
124		return (EINVAL);
125	from = vfs_getopts(vfsp->mnt_optnew, "from", &error);
126	if (error)
127		return (error);
128	coda_vfsopstats_init();
129	coda_vnodeopstats_init();
130	MARK_ENTRY(CODA_MOUNT_STATS);
131	if (CODA_MOUNTED(vfsp)) {
132		MARK_INT_FAIL(CODA_MOUNT_STATS);
133		return (EBUSY);
134	}
135
136	/*
137	 * Validate mount device.  Similar to getmdev().
138	 */
139	NDINIT(&ndp, LOOKUP, FOLLOW, UIO_SYSSPACE, from, curthread);
140	error = namei(&ndp);
141	dvp = ndp.ni_vp;
142	if (error) {
143		MARK_INT_FAIL(CODA_MOUNT_STATS);
144		return (error);
145	}
146	if (dvp->v_type != VCHR) {
147		MARK_INT_FAIL(CODA_MOUNT_STATS);
148		vrele(dvp);
149		NDFREE(&ndp, NDF_ONLY_PNBUF);
150		return (ENXIO);
151	}
152	dev = dvp->v_rdev;
153	vrele(dvp);
154	NDFREE(&ndp, NDF_ONLY_PNBUF);
155
156	/*
157	 * Initialize the mount record and link it to the vfs struct.
158	 */
159	mi = dev2coda_mntinfo(dev);
160	if (!mi) {
161		MARK_INT_FAIL(CODA_MOUNT_STATS);
162		printf("Coda mount: %s is not a cfs device\n", from);
163		return (ENXIO);
164	}
165	if (!VC_OPEN(&mi->mi_vcomm)) {
166		MARK_INT_FAIL(CODA_MOUNT_STATS);
167		return (ENODEV);
168	}
169
170	/*
171	 * No initialization (here) of mi_vcomm!
172	 */
173	vfsp->mnt_data = mi;
174	vfs_getnewfsid (vfsp);
175	mi->mi_vfsp = vfsp;
176	mi->mi_started = 0;			/* XXX See coda_root() */
177
178	/*
179	 * Make a root vnode to placate the Vnode interface, but don't
180	 * actually make the CODA_ROOT call to venus until the first call to
181	 * coda_root in case a server is down while venus is starting.
182	 */
183	cp = make_coda_node(&rootfid, vfsp, VDIR);
184	rootvp = CTOV(cp);
185	rootvp->v_vflag |= VV_ROOT;
186	cp = make_coda_node(&ctlfid, vfsp, VREG);
187	coda_ctlvp = CTOV(cp);
188
189	/*
190	 * Add vfs and rootvp to chain of vfs hanging off mntinfo.
191	 */
192	mi->mi_vfsp = vfsp;
193	mi->mi_rootvp = rootvp;
194	vfs_mountedfrom(vfsp, from);
195
196	/*
197	 * Error is currently guaranteed to be zero, but in case some code
198	 * changes...
199	 */
200	CODADEBUG(1, myprintf(("coda_mount returned %d\n", error)););
201	if (error)
202		MARK_INT_FAIL(CODA_MOUNT_STATS);
203	else
204		MARK_INT_SAT(CODA_MOUNT_STATS);
205	return (error);
206}
207
208int
209coda_unmount(struct mount *vfsp, int mntflags)
210{
211	struct coda_mntinfo *mi = vftomi(vfsp);
212	int active, error = 0;
213
214	ENTRY;
215	MARK_ENTRY(CODA_UMOUNT_STATS);
216	if (!CODA_MOUNTED(vfsp)) {
217		MARK_INT_FAIL(CODA_UMOUNT_STATS);
218		return (EINVAL);
219	}
220	if (mi->mi_vfsp == vfsp) {
221		/*
222		 * We found the victim.
223		 */
224		if (!IS_UNMOUNTING(VTOC(mi->mi_rootvp)))
225			return (EBUSY); 	/* Venus is still running */
226#ifdef DEBUG
227		printf("coda_unmount: ROOT: vp %p, cp %p\n", mi->mi_rootvp,
228		    VTOC(mi->mi_rootvp));
229#endif
230		vrele(mi->mi_rootvp);
231		mi->mi_rootvp = NULL;
232		vrele(coda_ctlvp);
233		coda_ctlvp = NULL;
234		active = coda_kill(vfsp, NOT_DOWNCALL);
235		error = vflush(mi->mi_vfsp, 0, FORCECLOSE, curthread);
236#ifdef CODA_VERBOSE
237		printf("coda_unmount: active = %d, vflush active %d\n",
238		    active, error);
239#endif
240		error = 0;
241		/*
242		 * I'm going to take this out to allow lookups to go through.
243		 * I'm not sure it's important anyway. -- DCS 2/2/94
244		 */
245		/* vfsp->VFS_DATA = NULL; */
246
247		/*
248		 * No more vfsp's to hold onto.
249		 */
250		mi->mi_vfsp = NULL;
251
252		if (error)
253			MARK_INT_FAIL(CODA_UMOUNT_STATS);
254		else
255			MARK_INT_SAT(CODA_UMOUNT_STATS);
256		return (error);
257	}
258	return (EINVAL);
259}
260
261/*
262 * Find root of cfs.
263 */
264int
265coda_root(struct mount *vfsp, int flags, struct vnode **vpp)
266{
267	struct coda_mntinfo *mi = vftomi(vfsp);
268	int error;
269	struct proc *p;
270	struct thread *td;
271	struct CodaFid VFid;
272	static const struct CodaFid invalfid = INVAL_FID;
273
274	td = curthread;
275	p = td->td_proc;
276	ENTRY;
277	MARK_ENTRY(CODA_ROOT_STATS);
278	if (vfsp == mi->mi_vfsp) {
279		/*
280		 * Cache the root across calls.  We only need to pass the
281		 * request on to Venus if the root vnode is the dummy we
282		 * installed in coda_mount() with all c_fid members zeroed.
283		 *
284		 * XXX In addition, we assume that the first call to
285		 * coda_root() is from vfs_mount() (before the call to
286		 * checkdirs()) and return the dummy root node to avoid a
287		 * deadlock.  This bug is fixed in the Coda CVS repository
288		 * but not in any released versions as of 6 Mar 2003.
289		 */
290		if (memcmp(&VTOC(mi->mi_rootvp)->c_fid, &invalfid,
291		    sizeof(struct CodaFid)) != 0 || mi->mi_started == 0) {
292			/*
293			 * Found valid root.
294			 */
295			*vpp = mi->mi_rootvp;
296			mi->mi_started = 1;
297
298			/*
299			 * On Mach, this is vref.  On FreeBSD, vref +
300			 * vn_lock.
301			 */
302			vref(*vpp);
303			vn_lock(*vpp, LK_EXCLUSIVE | LK_RETRY);
304			MARK_INT_SAT(CODA_ROOT_STATS);
305			return (0);
306		}
307	}
308
309	error = venus_root(vftomi(vfsp), td->td_ucred, p, &VFid);
310	if (!error) {
311		/*
312		 * Save the new rootfid in the cnode, and rehash the cnode
313		 * into the cnode hash with the new fid key.
314		 */
315		coda_unsave(VTOC(mi->mi_rootvp));
316		VTOC(mi->mi_rootvp)->c_fid = VFid;
317		coda_save(VTOC(mi->mi_rootvp));
318		*vpp = mi->mi_rootvp;
319		vref(*vpp);
320		vn_lock(*vpp, LK_EXCLUSIVE | LK_RETRY);
321		MARK_INT_SAT(CODA_ROOT_STATS);
322	} else if (error == ENODEV || error == EINTR) {
323		/*
324		 * Gross hack here!
325		 *
326		 * If Venus fails to respond to the CODA_ROOT call, coda_call
327		 * returns ENODEV. Return the uninitialized root vnode to
328		 * allow vfs operations such as unmount to continue.  Without
329		 * this hack, there is no way to do an unmount if Venus dies
330		 * before a successful CODA_ROOT call is done.  All vnode
331		 * operations will fail.
332		 */
333		*vpp = mi->mi_rootvp;
334		vref(*vpp);
335		vn_lock(*vpp, LK_EXCLUSIVE | LK_RETRY);
336		MARK_INT_FAIL(CODA_ROOT_STATS);
337		error = 0;
338	} else {
339		CODADEBUG(CODA_ROOT, myprintf(("error %d in CODA_ROOT\n",
340		    error)););
341		MARK_INT_FAIL(CODA_ROOT_STATS);
342	}
343	return (error);
344}
345
346/*
347 * Get filesystem statistics.
348 */
349int
350coda_statfs(struct mount *vfsp, struct statfs *sbp)
351{
352
353	ENTRY;
354	MARK_ENTRY(CODA_STATFS_STATS);
355	if (!CODA_MOUNTED(vfsp)) {
356		MARK_INT_FAIL(CODA_STATFS_STATS);
357		return (EINVAL);
358	}
359
360	/*
361	 * XXX - what to do about f_flags, others? --bnoble
362	 *
363	 * We just make up free space counts that are sufficiently large.
364	 */
365	sbp->f_flags = 0;
366	sbp->f_bsize = 8192; /* XXX */
367	sbp->f_iosize = 8192; /* XXX */
368#define	CODA_SFS_SIZ	0x8AB75D
369	sbp->f_blocks = CODA_SFS_SIZ;
370	sbp->f_bfree = CODA_SFS_SIZ;
371	sbp->f_bavail = CODA_SFS_SIZ;
372	sbp->f_files = CODA_SFS_SIZ;
373	sbp->f_ffree = CODA_SFS_SIZ;
374	MARK_INT_SAT(CODA_STATFS_STATS);
375	return (0);
376}
377
378/*
379 * Flush any pending I/O.
380 */
381int
382coda_sync(struct mount *vfsp, int waitfor)
383{
384
385	ENTRY;
386	MARK_ENTRY(CODA_SYNC_STATS);
387	MARK_INT_SAT(CODA_SYNC_STATS);
388	return (0);
389}
390
391/*
392 * fhtovp is now what vget used to be in 4.3-derived systems.  For some silly
393 * reason, vget is now keyed by a 32 bit ino_t, rather than a type-specific
394 * fid.
395 *
396 * XXX: coda_fhtovp is currently not hooked up, so no NFS export for Coda.
397 * We leave it here in the hopes that someone will find it someday and hook
398 * it up.  Among other things, it will need some reworking to match the
399 * vfs_fhtovp_t prototype.
400 */
401int
402coda_fhtovp(struct mount *vfsp, struct fid *fhp, struct mbuf *nam,
403    struct vnode **vpp, int *exflagsp, struct ucred **creadanonp)
404{
405	struct cfid *cfid = (struct cfid *)fhp;
406	struct cnode *cp = NULL;
407	int error;
408	struct thread *td = curthread; /* XXX -mach */
409	struct proc *p = td->td_proc;
410	struct CodaFid VFid;
411	int vtype;
412
413	ENTRY;
414	MARK_ENTRY(CODA_VGET_STATS);
415
416	/*
417	 * Check for vget of control object.
418	 */
419	if (IS_CTL_FID(&cfid->cfid_fid)) {
420		*vpp = coda_ctlvp;
421		vref(coda_ctlvp);
422		MARK_INT_SAT(CODA_VGET_STATS);
423		return (0);
424	}
425	error = venus_fhtovp(vftomi(vfsp), &cfid->cfid_fid, td->td_ucred, p,
426	    &VFid, &vtype);
427	if (error) {
428		CODADEBUG(CODA_VGET, myprintf(("vget error %d\n",error)););
429		*vpp = NULL;
430	} else {
431		CODADEBUG(CODA_VGET, myprintf(("vget: %s type %d result "
432		    "%d\n", coda_f2s(&VFid), vtype, error)););
433		cp = make_coda_node(&VFid, vfsp, vtype);
434		*vpp = CTOV(cp);
435	}
436	return (error);
437}
438
439struct vfsops coda_vfsops = {
440	.vfs_mount =		coda_mount,
441	.vfs_root = 		coda_root,
442	.vfs_statfs =		coda_statfs,
443	.vfs_sync = 		coda_sync,
444	.vfs_unmount =		coda_unmount,
445};
446VFS_SET(coda_vfsops, coda, VFCF_NETWORK);
447