jcs's openbsd hax
openbsd
1/* $OpenBSD: tmpfs_vfsops.c,v 1.21 2025/11/21 09:49:33 mvs Exp $ */
2/* $NetBSD: tmpfs_vfsops.c,v 1.52 2011/09/27 01:10:43 christos Exp $ */
3
4/*
5 * Copyright (c) 2005, 2006, 2007 The NetBSD Foundation, Inc.
6 * All rights reserved.
7 *
8 * This code is derived from software contributed to The NetBSD Foundation
9 * by Julio M. Merino Vidal, developed as part of Google's Summer of Code
10 * 2005 program.
11 *
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
14 * are met:
15 * 1. Redistributions of source code must retain the above copyright
16 * notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 * notice, this list of conditions and the following disclaimer in the
19 * documentation and/or other materials provided with the distribution.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
22 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
23 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
24 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
25 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 * POSSIBILITY OF SUCH DAMAGE.
32 */
33
34/*
35 * Efficient memory file system.
36 *
37 * tmpfs is a file system that uses NetBSD's virtual memory sub-system
38 * (the well-known UVM) to store file data and metadata in an efficient
39 * way. This means that it does not follow the structure of an on-disk
40 * file system because it simply does not need to. Instead, it uses
41 * memory-specific data structures and algorithms to automatically
42 * allocate and release resources.
43 */
44
45#include <sys/param.h>
46#include <sys/mount.h>
47#include <sys/stat.h>
48#include <sys/systm.h>
49#include <sys/vnode.h>
50#include <sys/malloc.h>
51
52#include <tmpfs/tmpfs.h>
53
54/* MODULE(MODULE_CLASS_VFS, tmpfs, NULL); */
55
56extern uint64_t tmpfs_bytes_limit;
57extern uint64_t tmpfs_bytes_used;
58
59struct pool tmpfs_dirent_pool;
60struct pool tmpfs_node_pool;
61
62int tmpfs_mount(struct mount *, const char *, void *, struct nameidata *,
63 struct proc *);
64int tmpfs_start(struct mount *, int, struct proc *);
65int tmpfs_unmount(struct mount *, int, struct proc *);
66int tmpfs_root(struct mount *, struct vnode **);
67int tmpfs_vget(struct mount *, ino_t, struct vnode **);
68int tmpfs_fhtovp(struct mount *, struct fid *, struct vnode **);
69int tmpfs_vptofh(struct vnode *, struct fid *);
70int tmpfs_statfs(struct mount *, struct statfs *, struct proc *);
71int tmpfs_sync(struct mount *, int, int, struct ucred *, struct proc *);
72int tmpfs_init(struct vfsconf *);
73int tmpfs_mount_update(struct mount *);
74
75int
76tmpfs_init(struct vfsconf *vfsp)
77{
78 tmpfs_bytes_limit = ((uint64_t)(uvmexp.npages / 2) << PAGE_SHIFT);
79
80 pool_init(&tmpfs_dirent_pool, sizeof(tmpfs_dirent_t), 0, IPL_NONE,
81 PR_WAITOK, "tmpfs_dirent", NULL);
82 pool_init(&tmpfs_node_pool, sizeof(tmpfs_node_t), 0, IPL_NONE,
83 PR_WAITOK, "tmpfs_node", NULL);
84
85 return 0;
86}
87
88int
89tmpfs_mount_update(struct mount *mp)
90{
91 tmpfs_mount_t *tmp;
92 struct vnode *rootvp;
93 int error;
94
95 if ((mp->mnt_flag & MNT_RDONLY) == 0)
96 return EOPNOTSUPP;
97
98 /* ro->rw transition: nothing to do? */
99 if (mp->mnt_flag & MNT_WANTRDWR)
100 return 0;
101
102 tmp = mp->mnt_data;
103 rootvp = tmp->tm_root->tn_vnode;
104
105 /* Lock root to prevent lookups. */
106 error = vn_lock(rootvp, LK_EXCLUSIVE | LK_RETRY);
107 if (error)
108 return error;
109
110 /* Lock mount point to prevent nodes from being added/removed. */
111 rw_enter_write(&tmp->tm_lock);
112
113 /* Flush files opened for writing; skip rootvp. */
114 error = vflush(mp, rootvp, WRITECLOSE);
115
116 rw_exit_write(&tmp->tm_lock);
117 VOP_UNLOCK(rootvp);
118
119 return error;
120}
121
122int
123tmpfs_mount(struct mount *mp, const char *path, void *data,
124 struct nameidata *ndp, struct proc *p)
125{
126 struct tmpfs_args *args = data;
127 tmpfs_mount_t *tmp;
128 tmpfs_node_t *root;
129 uint64_t memlimit = 0;
130 uint64_t nodes;
131 int error;
132
133 if (mp->mnt_flag & MNT_UPDATE)
134 return (tmpfs_mount_update(mp));
135
136 if (args->ta_root_uid == VNOVAL || args->ta_root_gid == VNOVAL ||
137 args->ta_root_mode == VNOVAL)
138 return EINVAL;
139
140 /* Get the memory usage limit for this file-system. */
141 if (args->ta_size_max) {
142 memlimit = roundup(args->ta_size_max, PAGE_SIZE);
143
144 if ((tmpfs_bytes_limit - tmpfs_bytes_used) < memlimit)
145 return EINVAL; /* historic error */
146 tmpfs_bytes_used += memlimit;
147 }
148
149 if (args->ta_nodes_max <= 3) {
150 nodes = 3 + ((memlimit ? memlimit : UINT64_MAX) / 1024);
151 } else {
152 nodes = args->ta_nodes_max;
153 }
154 nodes = MIN(nodes, INT_MAX);
155 KASSERT(nodes >= 3);
156
157 /* Allocate the tmpfs mount structure and fill it. */
158 tmp = malloc(sizeof(tmpfs_mount_t), M_MISCFSMNT, M_WAITOK);
159
160 tmp->tm_nodes_max = (ino_t)nodes;
161 tmp->tm_nodes_cnt = 0;
162 tmp->tm_highest_inode = 1;
163 LIST_INIT(&tmp->tm_nodes);
164
165 rw_init(&tmp->tm_lock, "tmplk");
166 tmpfs_mntmem_init(tmp, memlimit);
167
168 /* Allocate the root node. */
169 error = tmpfs_alloc_node(tmp, VDIR, args->ta_root_uid,
170 args->ta_root_gid, args->ta_root_mode & ALLPERMS, NULL,
171 VNOVAL, &root);
172 KASSERT(error == 0 && root != NULL);
173
174 /*
175 * Parent of the root inode is itself. Also, root inode has no
176 * directory entry (i.e. is never attached), thus hold an extra
177 * reference (link) for it.
178 */
179 root->tn_links++;
180 root->tn_spec.tn_dir.tn_parent = root;
181 tmp->tm_root = root;
182
183 mp->mnt_data = tmp;
184 mp->mnt_flag |= MNT_LOCAL;
185 mp->mnt_stat.f_namemax = TMPFS_MAXNAMLEN;
186 vfs_getnewfsid(mp);
187
188 mp->mnt_stat.mount_info.tmpfs_args = *args;
189
190 bzero(&mp->mnt_stat.f_mntonname, sizeof(mp->mnt_stat.f_mntonname));
191 bzero(&mp->mnt_stat.f_mntfromname, sizeof(mp->mnt_stat.f_mntfromname));
192 bzero(&mp->mnt_stat.f_mntfromspec, sizeof(mp->mnt_stat.f_mntfromspec));
193
194 strlcpy(mp->mnt_stat.f_mntonname, path,
195 sizeof(mp->mnt_stat.f_mntonname) - 1);
196 strlcpy(mp->mnt_stat.f_mntfromname, "tmpfs",
197 sizeof(mp->mnt_stat.f_mntfromname) - 1);
198 strlcpy(mp->mnt_stat.f_mntfromspec, "tmpfs",
199 sizeof(mp->mnt_stat.f_mntfromspec) - 1);
200
201 return error;
202}
203
204int
205tmpfs_start(struct mount *mp, int flags, struct proc *p)
206{
207 return 0;
208}
209
210int
211tmpfs_unmount(struct mount *mp, int mntflags, struct proc *p)
212{
213 tmpfs_mount_t *tmp = VFS_TO_TMPFS(mp);
214 tmpfs_node_t *node, *cnode;
215 int error, flags = 0;
216
217 /* Handle forced unmounts. */
218 if (mntflags & MNT_FORCE)
219 flags |= FORCECLOSE;
220
221 /* Finalize all pending I/O. */
222 error = vflush(mp, NULL, flags);
223 if (error != 0)
224 return error;
225
226 /*
227 * First round, detach and destroy all directory entries.
228 * Also, clear the pointers to the vnodes - they are gone.
229 */
230 LIST_FOREACH(node, &tmp->tm_nodes, tn_entries) {
231 tmpfs_dirent_t *de;
232
233 node->tn_vnode = NULL;
234 if (node->tn_type != VDIR) {
235 continue;
236 }
237 while ((de = TAILQ_FIRST(&node->tn_spec.tn_dir.tn_dir)) != NULL) {
238 cnode = de->td_node;
239 if (cnode)
240 cnode->tn_vnode = NULL;
241 tmpfs_dir_detach(node, de);
242 tmpfs_free_dirent(tmp, de);
243 }
244 }
245
246 /* Second round, destroy all inodes. */
247 while ((node = LIST_FIRST(&tmp->tm_nodes)) != NULL) {
248 tmpfs_free_node(tmp, node);
249 }
250
251 if (tmp->tm_mem_limit)
252 tmpfs_bytes_used -= tmp->tm_mem_limit;
253
254 /* Throw away the tmpfs_mount structure. */
255 tmpfs_mntmem_destroy(tmp);
256 /* mutex_destroy(&tmp->tm_lock); */
257 /* kmem_free(tmp, sizeof(*tmp)); */
258 free(tmp, M_MISCFSMNT, sizeof(tmpfs_mount_t));
259 mp->mnt_data = NULL;
260
261 return 0;
262}
263
264int
265tmpfs_root(struct mount *mp, struct vnode **vpp)
266{
267 tmpfs_node_t *node = VFS_TO_TMPFS(mp)->tm_root;
268
269 rw_enter_write(&node->tn_nlock);
270 return tmpfs_vnode_get(mp, node, vpp);
271}
272
273int
274tmpfs_vget(struct mount *mp, ino_t ino, struct vnode **vpp)
275{
276
277 printf("tmpfs_vget called; need for it unknown yet\n");
278 return EOPNOTSUPP;
279}
280
281int
282tmpfs_fhtovp(struct mount *mp, struct fid *fhp, struct vnode **vpp)
283{
284 tmpfs_mount_t *tmp = VFS_TO_TMPFS(mp);
285 tmpfs_node_t *node;
286 tmpfs_fid_t tfh;
287
288 if (fhp->fid_len != sizeof(tmpfs_fid_t)) {
289 return EINVAL;
290 }
291 memcpy(&tfh, fhp, sizeof(tmpfs_fid_t));
292
293 rw_enter_write(&tmp->tm_lock);
294 LIST_FOREACH(node, &tmp->tm_nodes, tn_entries) {
295 if (node->tn_id != tfh.tf_id) {
296 continue;
297 }
298 if (TMPFS_NODE_GEN(node) != tfh.tf_gen) {
299 continue;
300 }
301 rw_enter_write(&node->tn_nlock);
302 break;
303 }
304 rw_exit_write(&tmp->tm_lock);
305
306 /* Will release the tn_nlock. */
307 return node ? tmpfs_vnode_get(mp, node, vpp) : ESTALE;
308}
309
310int
311tmpfs_vptofh(struct vnode *vp, struct fid *fhp)
312{
313 tmpfs_fid_t tfh;
314 tmpfs_node_t *node;
315
316 node = VP_TO_TMPFS_NODE(vp);
317
318 memset(&tfh, 0, sizeof(tfh));
319 tfh.tf_len = sizeof(tmpfs_fid_t);
320 tfh.tf_gen = TMPFS_NODE_GEN(node);
321 tfh.tf_id = node->tn_id;
322 memcpy(fhp, &tfh, sizeof(tfh));
323
324 return 0;
325}
326
327int
328tmpfs_statfs(struct mount *mp, struct statfs *sbp, struct proc *p)
329{
330 tmpfs_mount_t *tmp;
331 fsfilcnt_t freenodes;
332 uint64_t avail;
333
334 tmp = VFS_TO_TMPFS(mp);
335
336 sbp->f_iosize = sbp->f_bsize = PAGE_SIZE;
337
338 rw_enter_read(&tmp->tm_acc_lock);
339 avail = tmpfs_pages_avail(tmp);
340 sbp->f_blocks = tmpfs_pages_total(tmp);
341 sbp->f_bfree = avail;
342 sbp->f_bavail = avail & INT64_MAX; /* f_bavail is int64_t */
343
344 freenodes = MIN(tmp->tm_nodes_max - tmp->tm_nodes_cnt,
345 avail * PAGE_SIZE / sizeof(tmpfs_node_t));
346
347 sbp->f_files = tmp->tm_nodes_cnt + freenodes;
348 sbp->f_ffree = freenodes;
349 sbp->f_favail = freenodes & INT64_MAX; /* f_favail is int64_t */
350 rw_exit_read(&tmp->tm_acc_lock);
351
352 copy_statfs_info(sbp, mp);
353
354 return 0;
355}
356
357int
358tmpfs_sync(struct mount *mp, int waitfor, int stall, struct ucred *cred,
359 struct proc *p)
360{
361
362 return 0;
363}
364
365/*
366 * tmpfs vfs operations.
367 */
368
369const struct vfsops tmpfs_vfsops = {
370 .vfs_mount = tmpfs_mount,
371 .vfs_start = tmpfs_start,
372 .vfs_unmount = tmpfs_unmount,
373 .vfs_root = tmpfs_root,
374 .vfs_quotactl = (void *)eopnotsupp,
375 .vfs_statfs = tmpfs_statfs,
376 .vfs_sync = tmpfs_sync,
377 .vfs_vget = tmpfs_vget,
378 .vfs_fhtovp = tmpfs_fhtovp,
379 .vfs_vptofh = tmpfs_vptofh,
380 .vfs_init = tmpfs_init,
381 .vfs_sysctl = (void *)eopnotsupp,
382 .vfs_checkexp = (void *)eopnotsupp,
383};