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/ktr.h>
49
50 #include <string.h>
51
52 #include "dirfs.h"
53
54 MALLOC_DEFINE(M_DIRFS, "dirfs", "dirfs mount allocation");
55 MALLOC_DEFINE(M_DIRFS_NODE, "dirfs nodes", "dirfs nodes memory allocation");
56 MALLOC_DEFINE(M_DIRFS_MISC, "dirfs misc", "dirfs miscellaneous allocation");
57
58 /*
59 * Kernel tracing facilities
60 */
61 KTR_INFO_MASTER(dirfs);
62
63 KTR_INFO(KTR_DIRFS, dirfs, root, 20,
64 "DIRFS(root dnp=%p vnode=%p hostdir=%s fd=%d error=%d)",
65 dirfs_node_t dnp, struct vnode *vp, char *hostdir, int fd, int error);
66
67 KTR_INFO(KTR_DIRFS, dirfs, mount, 21,
68 "DIRFS(mount path=%s dmp=%p mp=%p error=%d)",
69 char *path, dirfs_mount_t dmp, struct mount *mp, int error);
70
71 KTR_INFO(KTR_DIRFS, dirfs, unmount, 22,
72 "DIRFS(unmount dmp=%p mp=%p error=%d)",
73 dirfs_mount_t dmp, struct mount *mp, int error);
74
75 /* System wide sysctl stuff */
76 int debuglvl = 0;
77 int dirfs_fd_limit = 100;
78 int dirfs_fd_used = 0;
79 long passive_fd_list_miss = 0;
80 long passive_fd_list_hits = 0;
81
82 SYSCTL_NODE(_vfs, OID_AUTO, dirfs, CTLFLAG_RW, 0,
83 "dirfs filesystem for vkernels");
84 SYSCTL_INT(_vfs_dirfs, OID_AUTO, debug, CTLFLAG_RW,
85 &debuglvl, 0, "dirfs debug level");
86 SYSCTL_INT(_vfs_dirfs, OID_AUTO, fd_limit, CTLFLAG_RW,
87 &dirfs_fd_limit, 0, "Maximum number of passive nodes to cache");
88 SYSCTL_INT(_vfs_dirfs, OID_AUTO, fd_used, CTLFLAG_RD,
89 &dirfs_fd_used, 0, "Current number of passive nodes cached");
90 SYSCTL_LONG(_vfs_dirfs, OID_AUTO, passive_fd_list_miss, CTLFLAG_RD,
91 &passive_fd_list_miss, 0, "Passive fd list cache misses");
92 SYSCTL_LONG(_vfs_dirfs, OID_AUTO, passive_fd_list_hits, CTLFLAG_RD,
93 &passive_fd_list_hits, 0, "Passive fd list cache misses");
94
95 static int dirfs_statfs(struct mount *, struct statfs *, struct ucred *);
96
97 static int
dirfs_mount(struct mount * mp,char * path,caddr_t data,struct ucred * cred)98 dirfs_mount(struct mount *mp, char *path, caddr_t data, struct ucred *cred)
99 {
100 dirfs_mount_t dmp;
101 struct stat st;
102 size_t done, nlen;
103 int error;
104
105 dbg(1, "called\n");
106
107 if (mp->mnt_flag & MNT_UPDATE) {
108 dmp = VFS_TO_DIRFS(mp);
109 if (dmp->dm_rdonly == 0 && (mp->mnt_flag & MNT_RDONLY)) {
110 /* XXX We should make sure all writes are synced */
111 dmp->dm_rdonly = 1;
112 debug(2, "dirfs read-write -> read-only\n");
113 }
114
115 if (dmp->dm_rdonly && (mp->mnt_kern_flag & MNTK_WANTRDWR)) {
116 debug(2, "dirfs read-only -> read-write\n");
117 dmp->dm_rdonly = 0;
118 }
119 return 0;
120 }
121
122 dmp = kmalloc(sizeof(*dmp), M_DIRFS, M_WAITOK | M_ZERO);
123 mp->mnt_data = (qaddr_t)dmp;
124 dmp->dm_mount = mp;
125
126 error = copyinstr(data, &dmp->dm_path, MAXPATHLEN, &done);
127 if (error) {
128 /* Attempt to copy from kernel address */
129 error = copystr(data, &dmp->dm_path, MAXPATHLEN, &done);
130 if (error) {
131 kfree(dmp, M_DIRFS);
132 goto failure;
133 }
134 }
135
136 /* Strip / character at the end to avoid problems */
137 nlen = strnlen(dmp->dm_path, MAXPATHLEN);
138 if (dmp->dm_path[nlen-1] == '/')
139 dmp->dm_path[nlen-1] = 0;
140
141 /* Make sure host directory exists and it is indeed a directory. */
142 if ((stat(dmp->dm_path, &st)) == 0) {
143 if (!S_ISDIR(st.st_mode)) {
144 kfree(dmp, M_DIRFS);
145 error = EINVAL;
146 goto failure;
147 }
148 } else {
149 error = errno;
150 goto failure;
151 }
152
153 lockinit(&dmp->dm_lock, "dfsmnt", 0, LK_CANRECURSE);
154
155 vfs_add_vnodeops(mp, &dirfs_vnode_vops, &mp->mnt_vn_norm_ops);
156 vfs_getnewfsid(mp);
157
158 /* Who is running the vkernel */
159 dmp->dm_uid = getuid();
160 dmp->dm_gid = getgid();
161
162 TAILQ_INIT(&dmp->dm_fdlist);
163 RB_INIT(&dmp->dm_inotree);
164
165 kmalloc_raise_limit(M_DIRFS_NODE, 0);
166
167 dirfs_statfs(mp, &mp->mnt_stat, cred);
168
169 failure:
170 KTR_LOG(dirfs_mount, (dmp->dm_path) ? dmp->dm_path : "NULL",
171 dmp, mp, error);
172
173 return error;
174 }
175
176 static int
dirfs_unmount(struct mount * mp,int mntflags)177 dirfs_unmount(struct mount *mp, int mntflags)
178 {
179 dirfs_mount_t dmp;
180 dirfs_node_t dnp;
181 int cnt;
182 int error;
183
184 dbg(1, "called\n");
185 cnt = 0;
186 dmp = VFS_TO_DIRFS(mp);
187
188 error = vflush(mp, 0, 0);
189 if (error)
190 goto failure;
191
192 /*
193 * Clean up dm_fdlist. There should be no vnodes left so the
194 * only ref should be from the fdlist.
195 */
196 while ((dnp = TAILQ_FIRST(&dmp->dm_fdlist)) != NULL) {
197 dirfs_node_setpassive(dmp, dnp, 0);
198 }
199
200 /*
201 * Cleanup root node. In the case the filesystem is mounted
202 * but no operation is done on it, there will be no call to
203 * VFS_ROOT() so better check dnp is not NULL before attempting
204 * to release it.
205 */
206 dnp = dmp->dm_root;
207 if (dnp != NULL) {
208 dirfs_close_helper(dnp);
209 debug_node2(dnp);
210 dirfs_node_drop(dmp, dnp); /* last ref should free structure */
211 }
212 kfree(dmp, M_DIRFS);
213 mp->mnt_data = (qaddr_t) 0;
214
215 failure:
216 KTR_LOG(dirfs_unmount, dmp, mp, error);
217
218 return error;
219 }
220
221 static int
dirfs_root(struct mount * mp,struct vnode ** vpp)222 dirfs_root(struct mount *mp, struct vnode **vpp)
223 {
224 dirfs_mount_t dmp;
225 dirfs_node_t dnp;
226 int fd;
227 int error;
228
229 dbg(1, "called\n");
230
231 dmp = VFS_TO_DIRFS(mp);
232 KKASSERT(dmp != NULL);
233
234 if (dmp->dm_root == NULL) {
235 /*
236 * dm_root holds the root dirfs node. Allocate a new one since
237 * there is none. Also attempt to lstat(2) it, in order to set
238 * data for VOP_ACCESS()
239 */
240 dnp = dirfs_node_alloc(mp);
241 error = dirfs_node_stat(DIRFS_NOFD, dmp->dm_path, dnp);
242 if (error != 0) {
243 dirfs_node_free(dmp, dnp);
244 return error;
245 }
246 dirfs_node_ref(dnp); /* leave inact for life of mount */
247
248 /* Root inode's parent is NULL, used for verification */
249 dnp->dn_parent = NULL;
250 dmp->dm_root = dnp;
251 dirfs_node_setflags(dnp, DIRFS_ROOT);
252
253 /*
254 * Maintain an open descriptor on the root dnp. The
255 * normal open/close/cache does not apply for the root
256 * so the descriptor is ALWAYS available.
257 */
258 fd = open(dmp->dm_path, O_DIRECTORY);
259 if (fd == -1) {
260 dbg(9, "failed to open ROOT node\n");
261 dirfs_free_vp(dmp, dnp);
262 dirfs_node_free(dmp, dnp);
263 return errno;
264 }
265 dnp->dn_fd = fd;
266 dnp->dn_type = VDIR;
267 } else {
268 dnp = dmp->dm_root;
269 }
270
271 /*
272 * Acquire the root vnode (dn_type already set above). This
273 * call will handle any races and return a locked vnode.
274 */
275 dirfs_alloc_vp(mp, vpp, LK_CANRECURSE, dnp);
276 KTR_LOG(dirfs_root, dnp, *vpp, dmp->dm_path, dnp->dn_fd, error);
277
278 return 0;
279 }
280
281 static int
dirfs_fhtovp(struct mount * mp,struct vnode * rootvp,struct fid * fhp,struct vnode ** vpp)282 dirfs_fhtovp(struct mount *mp, struct vnode *rootvp, struct fid *fhp, struct vnode **vpp)
283 {
284 dbg(1, "called\n");
285
286 return EOPNOTSUPP;
287 }
288
289 static int
dirfs_statfs(struct mount * mp,struct statfs * sbp,struct ucred * cred)290 dirfs_statfs(struct mount *mp, struct statfs *sbp, struct ucred *cred)
291 {
292 dirfs_mount_t dmp = VFS_TO_DIRFS(mp);
293 struct statfs st;
294
295 dbg(1, "called\n");
296
297 if((statfs(dmp->dm_path, &st)) == -1)
298 return errno;
299
300 ksnprintf(st.f_mntfromname, MNAMELEN - 1, "dirfs@%s", dmp->dm_path);
301 bcopy(&st, sbp, sizeof(st));
302 strlcpy(sbp->f_fstypename, mp->mnt_vfc->vfc_name, MFSNAMELEN);
303 dbg(5, "iosize = %zd\n", sbp->f_iosize);
304
305 return 0;
306 }
307
308 static int
dirfs_statvfs(struct mount * mp,struct statvfs * sbp,struct ucred * cred)309 dirfs_statvfs(struct mount *mp, struct statvfs *sbp, struct ucred *cred)
310 {
311 dirfs_mount_t dmp = VFS_TO_DIRFS(mp);
312 struct statvfs st;
313
314 dbg(1, "called\n");
315
316 if ((statvfs(dmp->dm_path, &st)) == -1)
317 return errno;
318
319 bcopy(&st, sbp, sizeof(st));
320
321 return 0;
322 }
323
324 static int
dirfs_vptofh(struct vnode * vp,struct fid * fhp)325 dirfs_vptofh(struct vnode *vp, struct fid *fhp)
326 {
327 dirfs_node_t dnp;
328
329 dnp = VP_TO_NODE(vp);
330 debug_node2(dnp);
331 dbg(1, "called\n");
332
333 return EOPNOTSUPP;
334 }
335
336 static int
dirfs_checkexp(struct mount * mp,struct sockaddr * nam,int * exflagsp,struct ucred ** credanonp)337 dirfs_checkexp(struct mount *mp, struct sockaddr *nam, int *exflagsp,
338 struct ucred **credanonp)
339 {
340 dbg(1, "called\n");
341
342 return EOPNOTSUPP;
343 }
344
345 static struct vfsops dirfs_vfsops = {
346 .vfs_flags = 0,
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_checkexp = dirfs_checkexp
356 };
357
358 VFS_SET(dirfs_vfsops, dirfs, 0);
359 MODULE_VERSION(dirfs, 1);
360