opensolaris_vfs.c revision 168404
1/*-
2 * Copyright (c) 2006-2007 Pawel Jakub Dawidek <pjd@FreeBSD.org>
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 AUTHORS 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 AUTHORS 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
27#include <sys/cdefs.h>
28__FBSDID("$FreeBSD: head/sys/cddl/compat/opensolaris/kern/opensolaris_vfs.c 168404 2007-04-06 01:09:06Z pjd $");
29
30#include <sys/param.h>
31#include <sys/kernel.h>
32#include <sys/systm.h>
33#include <sys/mount.h>
34#include <sys/cred.h>
35#include <sys/vfs.h>
36#include <sys/priv.h>
37#include <sys/libkern.h>
38
39MALLOC_DECLARE(M_MOUNT);
40
41TAILQ_HEAD(vfsoptlist, vfsopt);
42struct vfsopt {
43	TAILQ_ENTRY(vfsopt) link;
44	char	*name;
45	void	*value;
46	int	len;
47};
48
49void
50vfs_setmntopt(vfs_t *vfsp, const char *name, const char *arg,
51    int flags __unused)
52{
53	struct vfsopt *opt;
54	size_t namesize;
55
56	if (vfsp->mnt_opt == NULL) {
57		vfsp->mnt_opt = malloc(sizeof(*vfsp->mnt_opt), M_MOUNT, M_WAITOK);
58		TAILQ_INIT(vfsp->mnt_opt);
59	}
60
61	opt = malloc(sizeof(*opt), M_MOUNT, M_WAITOK);
62
63	namesize = strlen(name) + 1;
64	opt->name = malloc(namesize, M_MOUNT, M_WAITOK);
65	strlcpy(opt->name, name, namesize);
66
67	if (arg == NULL) {
68		opt->value = NULL;
69		opt->len = 0;
70	} else {
71		opt->len = strlen(arg) + 1;
72		opt->value = malloc(opt->len, M_MOUNT, M_WAITOK);
73		bcopy(arg, opt->value, opt->len);
74	}
75	/* TODO: Locking. */
76	TAILQ_INSERT_TAIL(vfsp->mnt_opt, opt, link);
77}
78
79void
80vfs_clearmntopt(vfs_t *vfsp, const char *name)
81{
82	struct vfsopt *opt;
83
84	if (vfsp->mnt_opt == NULL)
85		return;
86	/* TODO: Locking. */
87	TAILQ_FOREACH(opt, vfsp->mnt_opt, link) {
88		if (strcmp(opt->name, name) == 0)
89			break;
90	}
91	if (opt != NULL) {
92		TAILQ_REMOVE(vfsp->mnt_opt, opt, link);
93		free(opt->name, M_MOUNT);
94		if (opt->value != NULL)
95			free(opt->value, M_MOUNT);
96		free(opt, M_MOUNT);
97	}
98}
99
100int
101vfs_optionisset(const vfs_t *vfsp, const char *opt, char **argp)
102{
103	struct vfsoptlist *opts = vfsp->mnt_opt;
104	int error;
105
106	if (opts == NULL)
107		return (0);
108	error = vfs_getopt(opts, opt, (void **)argp, NULL);
109	return (error != 0 ? 0 : 1);
110}
111
112int
113traverse(vnode_t **cvpp)
114{
115	kthread_t *td = curthread;
116	vnode_t *cvp;
117	vnode_t *tvp;
118	vfs_t *vfsp;
119	int error;
120
121	cvp = *cvpp;
122	error = 0;
123
124	/*
125	 * If this vnode is mounted on, then we transparently indirect
126	 * to the vnode which is the root of the mounted file system.
127	 * Before we do this we must check that an unmount is not in
128	 * progress on this vnode.
129	 */
130
131	for (;;) {
132		/*
133		 * Reached the end of the mount chain?
134		 */
135		vfsp = vn_mountedvfs(cvp);
136		if (vfsp == NULL)
137			break;
138		VN_RELE(cvp);
139
140		/*
141		 * The read lock must be held across the call to VFS_ROOT() to
142		 * prevent a concurrent unmount from destroying the vfs.
143		 */
144		error = VFS_ROOT(vfsp, 0, &tvp, td);
145		if (error)
146			break;
147		VOP_UNLOCK(tvp, 0, td);
148
149		cvp = tvp;
150	}
151
152	*cvpp = cvp;
153	return (error);
154}
155
156extern struct mount *vfs_mount_alloc(struct vnode *vp, struct vfsconf *vfsp,
157    const char *fspath, struct thread *td);
158
159int
160domount(kthread_t *td, vnode_t *vp, const char *fstype, char *fspath,
161    char *fspec, int fsflags)
162{
163	struct mount *mp;
164	struct vfsconf *vfsp;
165	int error;
166
167	/*
168	 * Be ultra-paranoid about making sure the type and fspath
169	 * variables will fit in our mp buffers, including the
170	 * terminating NUL.
171	 */
172	if (strlen(fstype) >= MFSNAMELEN || strlen(fspath) >= MNAMELEN)
173		return (ENAMETOOLONG);
174
175	if ((error = priv_check(td, PRIV_VFS_MOUNT)) != 0)
176		return (error);
177
178	vfsp = vfs_byname_kld(fstype, td, &error);
179	if (vfsp == NULL)
180		return (ENODEV);
181
182	if (vp->v_type != VDIR)
183		return (ENOTDIR);
184	VI_LOCK(vp);
185	if ((vp->v_iflag & VI_MOUNT) != 0 ||
186	    vp->v_mountedhere != NULL) {
187		VI_UNLOCK(vp);
188		return (EBUSY);
189	}
190	vp->v_iflag |= VI_MOUNT;
191	VI_UNLOCK(vp);
192
193	/*
194	 * Allocate and initialize the filesystem.
195	 */
196	vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td);
197	mp = vfs_mount_alloc(vp, vfsp, fspath, td);
198	VOP_UNLOCK(vp, 0, td);
199
200	mp->mnt_optnew = NULL;
201	vfs_setmntopt(mp, "from", fspec, 0);
202	mp->mnt_optnew = mp->mnt_opt;
203	mp->mnt_opt = NULL;
204
205	/*
206	 * Set the mount level flags.
207	 */
208	if (fsflags & MNT_RDONLY)
209		mp->mnt_flag |= MNT_RDONLY;
210	mp->mnt_flag &=~ MNT_UPDATEMASK;
211	mp->mnt_flag |= fsflags & (MNT_UPDATEMASK | MNT_FORCE | MNT_ROOTFS);
212	/*
213	 * Mount the filesystem.
214	 * XXX The final recipients of VFS_MOUNT just overwrite the ndp they
215	 * get.  No freeing of cn_pnbuf.
216	 */
217	error = VFS_MOUNT(mp, td);
218
219	if (!error) {
220		if (mp->mnt_opt != NULL)
221			vfs_freeopts(mp->mnt_opt);
222		mp->mnt_opt = mp->mnt_optnew;
223		(void)VFS_STATFS(mp, &mp->mnt_stat, td);
224	}
225	/*
226	 * Prevent external consumers of mount options from reading
227	 * mnt_optnew.
228	*/
229	mp->mnt_optnew = NULL;
230	vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td);
231	/*
232	 * Put the new filesystem on the mount list after root.
233	 */
234//	cache_purge(vp);
235	if (!error) {
236		vnode_t *mvp;
237
238		VI_LOCK(vp);
239		vp->v_iflag &= ~VI_MOUNT;
240		VI_UNLOCK(vp);
241		vp->v_mountedhere = mp;
242		mtx_lock(&mountlist_mtx);
243		TAILQ_INSERT_TAIL(&mountlist, mp, mnt_list);
244		mtx_unlock(&mountlist_mtx);
245		vfs_event_signal(NULL, VQ_MOUNT, 0);
246		if (VFS_ROOT(mp, LK_EXCLUSIVE, &mvp, td))
247			panic("mount: lost mount");
248		mountcheckdirs(vp, mvp);
249		vput(mvp);
250		VOP_UNLOCK(vp, 0, td);
251		if ((mp->mnt_flag & MNT_RDONLY) == 0)
252			error = vfs_allocate_syncvnode(mp);
253		vfs_unbusy(mp, td);
254		if (error)
255			vrele(vp);
256		else
257			vfs_mountedfrom(mp, fspec);
258	} else {
259		VI_LOCK(vp);
260		vp->v_iflag &= ~VI_MOUNT;
261		VI_UNLOCK(vp);
262		vfs_unbusy(mp, td);
263		vfs_mount_destroy(mp);
264		if (VOP_ISLOCKED(vp, td) != LK_EXCLUSIVE) {
265			printf("%s:%u: vnode vp=%p not locked\n", __func__, __LINE__, vp);
266			vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td);
267		}
268	}
269	return (error);
270}
271