xref: /dragonfly/sys/vfs/dirfs/dirfs_vfsops.c (revision cfd1aba3)
1 /*
2  * Copyright (c) 2013 The DragonFly Project.  All rights reserved.
3  *
4  * This code is derived from software contributed to The DragonFly Project
5  * by Antonio Huete Jimenez <tuxillo@quantumachine.net>
6  * by Matthew Dillon <dillon@dragonflybsd.org>
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in
16  *    the documentation and/or other materials provided with the
17  *    distribution.
18  * 3. Neither the name of The DragonFly Project nor the names of its
19  *    contributors may be used to endorse or promote products derived
20  *    from this software without specific, prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
25  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
26  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
27  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
28  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
29  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
30  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
31  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
32  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  *
35  */
36 
37 #include <sys/vfsops.h>
38 #include <sys/mount.h>
39 #include <sys/types.h>
40 #include <sys/systm.h>
41 #include <sys/param.h>
42 #include <sys/module.h>
43 #include <sys/kernel.h>
44 #include <sys/malloc.h>
45 #include <sys/sysctl.h>
46 #include <sys/queue.h>
47 #include <sys/spinlock2.h>
48 #include <sys/sysref2.h>
49 #include <sys/ktr.h>
50 
51 #include <string.h>
52 
53 #include "dirfs.h"
54 
55 MALLOC_DEFINE(M_DIRFS, "dirfs", "dirfs mount allocation");
56 MALLOC_DEFINE(M_DIRFS_NODE, "dirfs nodes", "dirfs nodes memory allocation");
57 MALLOC_DEFINE(M_DIRFS_MISC, "dirfs misc", "dirfs miscellaneous allocation");
58 
59 /*
60  * Kernel tracing facilities
61  */
62 KTR_INFO_MASTER(dirfs);
63 
64 KTR_INFO(KTR_DIRFS, dirfs, root, 20,
65     "DIRFS(root dnp=%p vnode=%p hostdir=%s fd=%d error=%d)",
66     dirfs_node_t dnp, struct vnode *vp, char *hostdir, int fd, int error);
67 
68 KTR_INFO(KTR_DIRFS, dirfs, mount, 21,
69     "DIRFS(mount path=%s dmp=%p mp=%p error=%d)",
70     char *path, dirfs_mount_t dmp, struct mount *mp, int error);
71 
72 KTR_INFO(KTR_DIRFS, dirfs, unmount, 22,
73     "DIRFS(unmount dmp=%p mp=%p error=%d)",
74     dirfs_mount_t dmp, struct mount *mp, int error);
75 
76 /* System wide sysctl stuff */
77 int debuglvl = 0;
78 int dirfs_fd_limit = 100;
79 int dirfs_fd_used = 0;
80 long passive_fd_list_miss = 0;
81 long passive_fd_list_hits = 0;
82 
83 SYSCTL_NODE(_vfs, OID_AUTO, dirfs, CTLFLAG_RW, 0,
84     "dirfs filesystem for vkernels");
85 SYSCTL_INT(_vfs_dirfs, OID_AUTO, debug, CTLFLAG_RW,
86     &debuglvl, 0, "dirfs debug level");
87 SYSCTL_INT(_vfs_dirfs, OID_AUTO, fd_limit, CTLFLAG_RW,
88     &dirfs_fd_limit, 0, "Maximum number of passive nodes to cache");
89 SYSCTL_INT(_vfs_dirfs, OID_AUTO, fd_used, CTLFLAG_RD,
90     &dirfs_fd_used, 0, "Current number of passive nodes cached");
91 SYSCTL_LONG(_vfs_dirfs, OID_AUTO, passive_fd_list_miss, CTLFLAG_RD,
92     &passive_fd_list_miss, 0, "Passive fd list cache misses");
93 SYSCTL_LONG(_vfs_dirfs, OID_AUTO, passive_fd_list_hits, CTLFLAG_RD,
94     &passive_fd_list_hits, 0, "Passive fd list cache misses");
95 
96 static int dirfs_statfs(struct mount *, struct statfs *, struct ucred *);
97 
98 static int
99 dirfs_mount(struct mount *mp, char *path, caddr_t data, struct ucred *cred)
100 {
101 	dirfs_mount_t dmp;
102 	struct stat st;
103 	size_t done, nlen;
104 	int error;
105 
106 	debug_called();
107 
108 	if (mp->mnt_flag & MNT_UPDATE) {
109 		dmp = VFS_TO_DIRFS(mp);
110 		if (dmp->dm_rdonly == 0 && (mp->mnt_flag & MNT_RDONLY)) {
111 			/* XXX We should make sure all writes are synced */
112 			dmp->dm_rdonly = 1;
113 			debug(2, "dirfs read-write -> read-only\n");
114 		}
115 
116 		if (dmp->dm_rdonly && (mp->mnt_kern_flag & MNTK_WANTRDWR)) {
117 			debug(2, "dirfs read-only -> read-write\n");
118 			dmp->dm_rdonly = 0;
119 		}
120 		return 0;
121 	}
122 
123 	dmp = kmalloc(sizeof(*dmp), M_DIRFS, M_WAITOK | M_ZERO);
124 	mp->mnt_data = (qaddr_t)dmp;
125 	dmp->dm_mount = mp;
126 
127 	error = copyinstr(data, &dmp->dm_path, MAXPATHLEN, &done);
128 	if (error) {
129 		/* Attempt to copy from kernel address */
130 		error = copystr(data, &dmp->dm_path, MAXPATHLEN, &done);
131 		if (error) {
132 			kfree(dmp, M_DIRFS);
133 			goto failure;
134 		}
135 	}
136 
137 	/* Strip / character at the end to avoid problems */
138 	nlen = strnlen(dmp->dm_path, MAXPATHLEN);
139 	if (dmp->dm_path[nlen-1] == '/')
140 		dmp->dm_path[nlen-1] = 0;
141 
142 	/* Make sure host directory exists and it is indeed a directory. */
143 	if ((stat(dmp->dm_path, &st)) == 0) {
144 		if (!S_ISDIR(st.st_mode)) {
145 			kfree(dmp, M_DIRFS);
146 			error = EINVAL;
147 			goto failure;
148 		}
149 	} else {
150 		error = errno;
151 		goto failure;
152 	}
153 
154 	lockinit(&dmp->dm_lock, "dfsmnt", 0, LK_CANRECURSE);
155 
156 	vfs_add_vnodeops(mp, &dirfs_vnode_vops, &mp->mnt_vn_norm_ops);
157 	vfs_getnewfsid(mp);
158 
159 	/* Who is running the vkernel */
160 	dmp->dm_uid = getuid();
161 	dmp->dm_gid = getgid();
162 
163 	TAILQ_INIT(&dmp->dm_fdlist);
164 	RB_INIT(&dmp->dm_inotree);
165 
166 	kmalloc_raise_limit(M_DIRFS_NODE, 0);
167 
168 	dirfs_statfs(mp, &mp->mnt_stat, cred);
169 
170 failure:
171 	KTR_LOG(dirfs_mount, (dmp->dm_path) ? dmp->dm_path : "NULL",
172 	    dmp, mp, error);
173 
174 	return error;
175 }
176 
177 static int
178 dirfs_unmount(struct mount *mp, int mntflags)
179 {
180 	dirfs_mount_t dmp;
181 	dirfs_node_t dnp;
182 	int cnt;
183 	int error;
184 
185 	debug_called();
186 	cnt = 0;
187 	dmp = VFS_TO_DIRFS(mp);
188 
189 	error = vflush(mp, 0, 0);
190 	if (error)
191 		goto failure;
192 
193 	/*
194 	 * Clean up dm_fdlist.  There should be no vnodes left so the
195 	 * only ref should be from the fdlist.
196 	 */
197 	while ((dnp = TAILQ_FIRST(&dmp->dm_fdlist)) != NULL) {
198 		dirfs_node_setpassive(dmp, dnp, 0);
199 	}
200 
201 	/*
202 	 * Cleanup root node. In the case the filesystem is mounted
203 	 * but no operation is done on it, there will be no call to
204 	 * VFS_ROOT() so better check dnp is not NULL before attempting
205 	 * to release it.
206 	 */
207 	dnp = dmp->dm_root;
208 	if (dnp != NULL) {
209 		dirfs_close_helper(dnp);
210 		debug_node2(dnp);
211 		dirfs_node_drop(dmp, dnp); /* last ref should free structure */
212 	}
213 	kfree(dmp, M_DIRFS);
214 	mp->mnt_data = (qaddr_t) 0;
215 
216 failure:
217 	KTR_LOG(dirfs_unmount, dmp, mp, error);
218 
219 	return error;
220 }
221 
222 static int
223 dirfs_root(struct mount *mp, struct vnode **vpp)
224 {
225 	dirfs_mount_t dmp;
226 	dirfs_node_t dnp;
227 	int fd;
228 	int error;
229 
230 	debug_called();
231 
232 	dmp = VFS_TO_DIRFS(mp);
233 	KKASSERT(dmp != NULL);
234 
235 	if (dmp->dm_root == NULL) {
236 		/*
237 		 * dm_root holds the root dirfs node. Allocate a new one since
238 		 * there is none. Also attempt to lstat(2) it, in order to set
239 		 * data for VOP_ACCESS()
240 		 */
241 		dnp = dirfs_node_alloc(mp);
242 		error = dirfs_node_stat(DIRFS_NOFD, dmp->dm_path, dnp);
243 		if (error != 0) {
244 			dirfs_node_free(dmp, dnp);
245 			return error;
246 		}
247 		dirfs_node_ref(dnp);	/* leave inact for life of mount */
248 
249 		/* Root inode's parent is NULL, used for verification */
250 		dnp->dn_parent = NULL;
251 		dmp->dm_root = dnp;
252 		dirfs_node_setflags(dnp, DIRFS_ROOT);
253 
254 		/*
255 		 * Maintain an open descriptor on the root dnp.  The
256 		 * normal open/close/cache does not apply for the root
257 		 * so the descriptor is ALWAYS available.
258 		 */
259 		fd = open(dmp->dm_path, O_DIRECTORY);
260 		if (fd == -1) {
261 			dbg(5, "failed to open ROOT node\n");
262 			dirfs_free_vp(dmp, dnp);
263 			dirfs_node_free(dmp, dnp);
264 			return errno;
265 		}
266 		dnp->dn_fd = fd;
267 		dnp->dn_type = VDIR;
268 	} else {
269 		dnp = dmp->dm_root;
270 	}
271 
272 	/*
273 	 * Acquire the root vnode (dn_type already set above).  This
274 	 * call will handle any races and return a locked vnode.
275 	 */
276 	dirfs_alloc_vp(mp, vpp, LK_CANRECURSE, dnp);
277 	KTR_LOG(dirfs_root, dnp, *vpp, dmp->dm_path, dnp->dn_fd, error);
278 
279 	return 0;
280 }
281 
282 static int
283 dirfs_fhtovp(struct mount *mp, struct vnode *rootvp, struct fid *fhp, struct vnode **vpp)
284 {
285 	debug_called();
286 
287 	return EOPNOTSUPP;
288 }
289 
290 static int
291 dirfs_statfs(struct mount *mp, struct statfs *sbp, struct ucred *cred)
292 {
293 	dirfs_mount_t dmp = VFS_TO_DIRFS(mp);
294 	struct statfs st;
295 
296 	debug_called();
297 
298 	if((statfs(dmp->dm_path, &st)) == -1)
299 		return errno;
300 
301 	ksnprintf(st.f_mntfromname, MNAMELEN - 1, "dirfs@%s", dmp->dm_path);
302 	bcopy(&st, sbp, sizeof(st));
303 	strlcpy(sbp->f_fstypename, mp->mnt_vfc->vfc_name, MFSNAMELEN);
304 	dbg(5, "iosize = %zd\n", sbp->f_iosize);
305 
306 	return 0;
307 }
308 
309 static int
310 dirfs_statvfs(struct mount *mp, struct statvfs *sbp, struct ucred *cred)
311 {
312 	dirfs_mount_t dmp = VFS_TO_DIRFS(mp);
313 	struct statvfs st;
314 
315 	debug_called();
316 
317 	if ((statvfs(dmp->dm_path, &st)) == -1)
318 		return errno;
319 
320 	bcopy(&st, sbp, sizeof(st));
321 
322 	return 0;
323 }
324 
325 static int
326 dirfs_vptofh(struct vnode *vp, struct fid *fhp)
327 {
328 	dirfs_node_t dnp;
329 
330 	dnp = VP_TO_NODE(vp);
331 	debug_node2(dnp);
332 	debug_called();
333 
334 	return EOPNOTSUPP;
335 }
336 
337 static int
338 dirfs_checkexp(struct mount *mp, struct sockaddr *nam, int *exflagsp,
339 	       struct ucred **credanonp)
340 {
341 	debug_called();
342 
343 	return EOPNOTSUPP;
344 }
345 
346 static struct vfsops dirfs_vfsops = {
347 	.vfs_mount =			dirfs_mount,
348 	.vfs_unmount =			dirfs_unmount,
349 	.vfs_root =			dirfs_root,
350 	.vfs_vget =			vfs_stdvget,
351 	.vfs_statfs =			dirfs_statfs,
352 	.vfs_statvfs =			dirfs_statvfs,
353 	.vfs_fhtovp =			dirfs_fhtovp,
354 	.vfs_vptofh =			dirfs_vptofh,
355 	.vfs_sync =			vfs_stdsync,
356 	.vfs_checkexp =			dirfs_checkexp
357 };
358 
359 VFS_SET(dirfs_vfsops, dirfs, 0);
360 MODULE_VERSION(dirfs, 1);
361