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