Initial import of binutils 2.22 on the new vendor branch
[dragonfly.git] / sys / vfs / tmpfs / tmpfs_vfsops.c
CommitLineData
86d7f5d3
JM
1/* $NetBSD: tmpfs_vfsops.c,v 1.10 2005/12/11 12:24:29 christos Exp $ */
2
3/*-
4 * Copyright (c) 2005 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Julio M. Merino Vidal, developed as part of Google's Summer of Code
9 * 2005 program.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
21 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
24 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 * POSSIBILITY OF SUCH DAMAGE.
31 */
32
33/*
34 * Efficient memory file system.
35 *
36 * tmpfs is a file system that uses NetBSD's virtual memory sub-system
37 * (the well-known UVM) to store file data and metadata in an efficient
38 * way. This means that it does not follow the structure of an on-disk
39 * file system because it simply does not need to. Instead, it uses
40 * memory-specific data structures and algorithms to automatically
41 * allocate and release resources.
42 */
43
44#include <sys/conf.h>
45#include <sys/param.h>
46#include <sys/limits.h>
47#include <sys/lock.h>
48#include <sys/mutex.h>
49#include <sys/kernel.h>
50#include <sys/stat.h>
51#include <sys/systm.h>
52#include <sys/sysctl.h>
53#include <sys/objcache.h>
54
55#include <vm/vm.h>
56#include <vm/vm_object.h>
57#include <vm/vm_param.h>
58
59#include <vfs/tmpfs/tmpfs.h>
60#include <vfs/tmpfs/tmpfs_vnops.h>
61#include <vfs/tmpfs/tmpfs_args.h>
62
63/*
64 * Default permission for root node
65 */
66#define TMPFS_DEFAULT_ROOT_MODE (S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH)
67
68MALLOC_DEFINE(M_TMPFSMNT, "tmpfs mount", "tmpfs mount structures");
69
70/* --------------------------------------------------------------------- */
71
72static int tmpfs_mount(struct mount *, char *, caddr_t, struct ucred *);
73static int tmpfs_unmount(struct mount *, int);
74static int tmpfs_root(struct mount *, struct vnode **);
75static int tmpfs_fhtovp(struct mount *, struct vnode *, struct fid *, struct vnode **);
76static int tmpfs_statfs(struct mount *, struct statfs *, struct ucred *cred);
77
78/* --------------------------------------------------------------------- */
79int
80tmpfs_node_ctor(void *obj, void *privdata, int flags)
81{
82 struct tmpfs_node *node = (struct tmpfs_node *)obj;
83
84 node->tn_gen++;
85 node->tn_size = 0;
86 node->tn_status = 0;
87 node->tn_flags = 0;
88 node->tn_links = 0;
89 node->tn_vnode = NULL;
90 node->tn_vpstate = TMPFS_VNODE_WANT;
91 bzero(&node->tn_spec, sizeof(node->tn_spec));
92
93 return (1);
94}
95
96static void
97tmpfs_node_dtor(void *obj, void *privdata)
98{
99 struct tmpfs_node *node = (struct tmpfs_node *)obj;
100 node->tn_type = VNON;
101 node->tn_vpstate = TMPFS_VNODE_DOOMED;
102}
103
104static void*
105tmpfs_node_init(void *args, int flags)
106{
107 struct tmpfs_node *node = (struct tmpfs_node *)objcache_malloc_alloc(args, flags);
108 if (node == NULL)
109 return (NULL);
110 node->tn_id = 0;
111
112 lockinit(&node->tn_interlock, "tmpfs node interlock", 0, LK_CANRECURSE);
113 node->tn_gen = karc4random();
114
115 return node;
116}
117
118static void
119tmpfs_node_fini(void *obj, void *args)
120{
121 struct tmpfs_node *node = (struct tmpfs_node *)obj;
122 lockuninit(&node->tn_interlock);
123 objcache_malloc_free(obj, args);
124}
125
126static int
127tmpfs_mount(struct mount *mp, char *path, caddr_t data, struct ucred *cred)
128{
129 struct tmpfs_mount *tmp;
130 struct tmpfs_node *root;
131 struct tmpfs_args args;
132 vm_pindex_t pages;
133 vm_pindex_t pages_limit;
134 ino_t nodes;
135 u_int64_t maxfsize;
136 int error;
137 /* Size counters. */
138 ino_t nodes_max;
139 off_t size_max;
140 size_t maxfsize_max;
141 size_t size;
142
143 /* Root node attributes. */
144 uid_t root_uid = cred->cr_uid;
145 gid_t root_gid = cred->cr_gid;
146 mode_t root_mode = (VREAD | VWRITE);
147
148 if (mp->mnt_flag & MNT_UPDATE) {
149 /* XXX: There is no support yet to update file system
150 * settings. Should be added. */
151
152 return EOPNOTSUPP;
153 }
154
155 /*
156 * mount info
157 */
158 bzero(&args, sizeof(args));
159 size_max = 0;
160 nodes_max = 0;
161 maxfsize_max = 0;
162
163 if (path) {
164 if (data) {
165 error = copyin(data, &args, sizeof(args));
166 if (error)
167 return (error);
168 }
169 size_max = args.ta_size_max;
170 nodes_max = args.ta_nodes_max;
171 maxfsize_max = args.ta_maxfsize_max;
172 root_uid = args.ta_root_uid;
173 root_gid = args.ta_root_gid;
174 root_mode = args.ta_root_mode;
175 }
176
177 /*
178 * If mount by non-root, then verify that user has necessary
179 * permissions on the device.
180 */
181 if (cred->cr_uid != 0) {
182 root_mode = VREAD;
183 if ((mp->mnt_flag & MNT_RDONLY) == 0)
184 root_mode |= VWRITE;
185 }
186
187 pages_limit = vm_swap_max + vmstats.v_page_count / 2;
188
189 if (size_max == 0)
190 pages = pages_limit / 2;
191 else if (size_max < PAGE_SIZE)
192 pages = 1;
193 else if (OFF_TO_IDX(size_max) > pages_limit)
194 pages = pages_limit;
195 else
196 pages = OFF_TO_IDX(size_max);
197
198 if (nodes_max == 0)
199 nodes = 3 + pages * PAGE_SIZE / 1024;
200 else if (nodes_max < 3)
201 nodes = 3;
202 else if (nodes_max > pages)
203 nodes = pages;
204 else
205 nodes = nodes_max;
206
207 maxfsize = IDX_TO_OFF(pages_limit);
208 if (maxfsize_max != 0 && maxfsize > maxfsize_max)
209 maxfsize = maxfsize_max;
210
211 /* Allocate the tmpfs mount structure and fill it. */
212 tmp = kmalloc(sizeof(*tmp), M_TMPFSMNT, M_WAITOK | M_ZERO);
213
214 lockinit(&(tmp->allnode_lock), "tmpfs allnode lock", 0, LK_CANRECURSE);
215 tmp->tm_nodes_max = nodes;
216 tmp->tm_nodes_inuse = 0;
217 tmp->tm_maxfilesize = maxfsize;
218 LIST_INIT(&tmp->tm_nodes_used);
219
220 tmp->tm_pages_max = pages;
221 tmp->tm_pages_used = 0;
222
223 kmalloc_create(&tmp->tm_node_zone, "tmpfs node");
224 kmalloc_create(&tmp->tm_dirent_zone, "tmpfs dirent");
225 kmalloc_create(&tmp->tm_name_zone, "tmpfs name zone");
226
227 kmalloc_raise_limit(tmp->tm_node_zone, sizeof(struct tmpfs_node) *
228 tmp->tm_nodes_max);
229
230 tmp->tm_node_zone_malloc_args.objsize = sizeof(struct tmpfs_node);
231 tmp->tm_node_zone_malloc_args.mtype = tmp->tm_node_zone;
232
233 tmp->tm_dirent_zone_malloc_args.objsize = sizeof(struct tmpfs_dirent);
234 tmp->tm_dirent_zone_malloc_args.mtype = tmp->tm_dirent_zone;
235
236 tmp->tm_dirent_pool = objcache_create( "tmpfs dirent cache",
237 0, 0,
238 NULL, NULL, NULL,
239 objcache_malloc_alloc, objcache_malloc_free,
240 &tmp->tm_dirent_zone_malloc_args);
241 tmp->tm_node_pool = objcache_create( "tmpfs node cache",
242 0, 0,
243 tmpfs_node_ctor, tmpfs_node_dtor, NULL,
244 tmpfs_node_init, tmpfs_node_fini,
245 &tmp->tm_node_zone_malloc_args);
246
247 /* Allocate the root node. */
248 error = tmpfs_alloc_node(tmp, VDIR, root_uid, root_gid,
249 root_mode & ALLPERMS, NULL, NULL,
250 VNOVAL, VNOVAL, &root);
251
252 /*
253 * We are backed by swap, set snocache chflags flag so we
254 * don't trip over swapcache.
255 */
256 root->tn_flags = SF_NOCACHE;
257
258 if (error != 0 || root == NULL) {
259 objcache_destroy(tmp->tm_node_pool);
260 objcache_destroy(tmp->tm_dirent_pool);
261 kfree(tmp, M_TMPFSMNT);
262 return error;
263 }
264 KASSERT(root->tn_id >= 0, ("tmpfs root with invalid ino: %d", (int)root->tn_id));
265 tmp->tm_root = root;
266
267 mp->mnt_flag |= MNT_LOCAL;
268#if 0
269 mp->mnt_kern_flag |= MNTK_RD_MPSAFE | MNTK_WR_MPSAFE | MNTK_GA_MPSAFE |
270 MNTK_IN_MPSAFE | MNTK_SG_MPSAFE;
271#endif
272 mp->mnt_kern_flag |= MNTK_RD_MPSAFE | MNTK_GA_MPSAFE | MNTK_SG_MPSAFE;
273 mp->mnt_kern_flag |= MNTK_WR_MPSAFE;
274 mp->mnt_kern_flag |= MNTK_NOMSYNC;
275 mp->mnt_data = (qaddr_t)tmp;
276 vfs_getnewfsid(mp);
277
278
279 vfs_add_vnodeops(mp, &tmpfs_vnode_vops, &mp->mnt_vn_norm_ops);
280 vfs_add_vnodeops(mp, &tmpfs_fifo_vops, &mp->mnt_vn_fifo_ops);
281
282 copystr("tmpfs", mp->mnt_stat.f_mntfromname, MNAMELEN - 1, &size);
283 bzero(mp->mnt_stat.f_mntfromname +size, MNAMELEN - size);
284 bzero(mp->mnt_stat.f_mntonname, sizeof(mp->mnt_stat.f_mntonname));
285 copyinstr(path, mp->mnt_stat.f_mntonname,
286 sizeof(mp->mnt_stat.f_mntonname) -1,
287 &size);
288
289 tmpfs_statfs(mp, &mp->mnt_stat, cred);
290
291 return 0;
292}
293
294/* --------------------------------------------------------------------- */
295
296/* ARGSUSED2 */
297static int
298tmpfs_unmount(struct mount *mp, int mntflags)
299{
300 int error;
301 int flags = 0;
302 int found;
303 struct tmpfs_mount *tmp;
304 struct tmpfs_node *node;
305
306 /* Handle forced unmounts. */
307 if (mntflags & MNT_FORCE)
308 flags |= FORCECLOSE;
309
310 tmp = VFS_TO_TMPFS(mp);
311
312 /*
313 * Finalize all pending I/O. In the case of tmpfs we want
314 * to throw all the data away so clean out the buffer cache
315 * and vm objects before calling vflush().
316 */
317 LIST_FOREACH(node, &tmp->tm_nodes_used, tn_entries) {
318 if (node->tn_type == VREG && node->tn_vnode) {
319 ++node->tn_links;
320 TMPFS_NODE_LOCK(node);
321 vx_get(node->tn_vnode);
322 tmpfs_truncate(node->tn_vnode, 0);
323 vx_put(node->tn_vnode);
324 TMPFS_NODE_UNLOCK(node);
325 --node->tn_links;
326 }
327 }
328 error = vflush(mp, 0, flags);
329 if (error != 0)
330 return error;
331
332 /*
333 * First pass get rid of all the directory entries and
334 * vnode associations. The directory structure will
335 * remain via the extra link count representing tn_dir.tn_parent.
336 *
337 * No vnodes should remain after the vflush above.
338 */
339 LIST_FOREACH(node, &tmp->tm_nodes_used, tn_entries) {
340 ++node->tn_links;
341 TMPFS_NODE_LOCK(node);
342 if (node->tn_type == VDIR) {
343 struct tmpfs_dirent *de;
344
345 while (!TAILQ_EMPTY(&node->tn_dir.tn_dirhead)) {
346 de = TAILQ_FIRST(&node->tn_dir.tn_dirhead);
347 tmpfs_dir_detach(node, de);
348 tmpfs_free_dirent(tmp, de);
349 node->tn_size -= sizeof(struct tmpfs_dirent);
350 }
351 }
352 KKASSERT(node->tn_vnode == NULL);
353#if 0
354 vp = node->tn_vnode;
355 if (vp != NULL) {
356 tmpfs_free_vp(vp);
357 vrecycle(vp);
358 node->tn_vnode = NULL;
359 }
360#endif
361 TMPFS_NODE_UNLOCK(node);
362 --node->tn_links;
363 }
364
365 /*
366 * Now get rid of all nodes. We can remove any node with a
367 * link count of 0 or any directory node with a link count of
368 * 1. The parents will not be destroyed until all their children
369 * have been destroyed.
370 *
371 * Recursion in tmpfs_free_node() can further modify the list so
372 * we cannot use a next pointer here.
373 *
374 * The root node will be destroyed by this loop (it will be last).
375 */
376 while (!LIST_EMPTY(&tmp->tm_nodes_used)) {
377 found = 0;
378 LIST_FOREACH(node, &tmp->tm_nodes_used, tn_entries) {
379 if (node->tn_links == 0 ||
380 (node->tn_links == 1 && node->tn_type == VDIR)) {
381 TMPFS_NODE_LOCK(node);
382 tmpfs_free_node(tmp, node);
383 /* eats lock */
384 found = 1;
385 break;
386 }
387 }
388 if (found == 0) {
389 kprintf("tmpfs: Cannot free entire node tree!");
390 break;
391 }
392 }
393
394 KKASSERT(tmp->tm_root == NULL);
395
396 objcache_destroy(tmp->tm_dirent_pool);
397 objcache_destroy(tmp->tm_node_pool);
398
399 kmalloc_destroy(&tmp->tm_name_zone);
400 kmalloc_destroy(&tmp->tm_dirent_zone);
401 kmalloc_destroy(&tmp->tm_node_zone);
402
403 tmp->tm_node_zone = tmp->tm_dirent_zone = NULL;
404
405 lockuninit(&tmp->allnode_lock);
406 KKASSERT(tmp->tm_pages_used == 0);
407 KKASSERT(tmp->tm_nodes_inuse == 0);
408
409 /* Throw away the tmpfs_mount structure. */
410 kfree(tmp, M_TMPFSMNT);
411 mp->mnt_data = NULL;
412
413 mp->mnt_flag &= ~MNT_LOCAL;
414 return 0;
415}
416
417/* --------------------------------------------------------------------- */
418
419static int
420tmpfs_root(struct mount *mp, struct vnode **vpp)
421{
422 struct tmpfs_mount *tmp;
423 int error;
424
425 tmp = VFS_TO_TMPFS(mp);
426 if (tmp->tm_root == NULL) {
427 kprintf("tmpfs_root: called without root node %p\n", mp);
428 print_backtrace(-1);
429 *vpp = NULL;
430 error = EINVAL;
431 } else {
432 error = tmpfs_alloc_vp(mp, tmp->tm_root, LK_EXCLUSIVE, vpp);
433 (*vpp)->v_flag |= VROOT;
434 (*vpp)->v_type = VDIR;
435 }
436 return error;
437}
438
439/* --------------------------------------------------------------------- */
440
441static int
442tmpfs_fhtovp(struct mount *mp, struct vnode *rootvp, struct fid *fhp, struct vnode **vpp)
443{
444 boolean_t found;
445 struct tmpfs_fid *tfhp;
446 struct tmpfs_mount *tmp;
447 struct tmpfs_node *node;
448
449 tmp = VFS_TO_TMPFS(mp);
450
451 tfhp = (struct tmpfs_fid *)fhp;
452 if (tfhp->tf_len != sizeof(struct tmpfs_fid))
453 return EINVAL;
454
455 if (tfhp->tf_id >= tmp->tm_nodes_max)
456 return EINVAL;
457
458 found = FALSE;
459
460 TMPFS_LOCK(tmp);
461 LIST_FOREACH(node, &tmp->tm_nodes_used, tn_entries) {
462 if (node->tn_id == tfhp->tf_id &&
463 node->tn_gen == tfhp->tf_gen) {
464 found = TRUE;
465 break;
466 }
467 }
468 TMPFS_UNLOCK(tmp);
469
470 if (found)
471 return (tmpfs_alloc_vp(mp, node, LK_EXCLUSIVE, vpp));
472
473 return (EINVAL);
474}
475
476/* --------------------------------------------------------------------- */
477
478/* ARGSUSED2 */
479static int
480tmpfs_statfs(struct mount *mp, struct statfs *sbp, struct ucred *cred)
481{
482 fsfilcnt_t freenodes;
483 struct tmpfs_mount *tmp;
484
485 tmp = VFS_TO_TMPFS(mp);
486
487 sbp->f_iosize = PAGE_SIZE;
488 sbp->f_bsize = PAGE_SIZE;
489
490 sbp->f_blocks = tmp->tm_pages_max;
491 sbp->f_bavail = tmp->tm_pages_max - tmp->tm_pages_used;
492 sbp->f_bfree = sbp->f_bavail;
493
494 freenodes = tmp->tm_nodes_max - tmp->tm_nodes_inuse;
495
496 sbp->f_files = freenodes + tmp->tm_nodes_inuse;
497 sbp->f_ffree = freenodes;
498 sbp->f_owner = tmp->tm_root->tn_uid;
499
500 return 0;
501}
502
503/* --------------------------------------------------------------------- */
504
505static int
506tmpfs_vptofh(struct vnode *vp, struct fid *fhp)
507{
508 struct tmpfs_node *node;
509 struct tmpfs_fid tfh;
510 node = VP_TO_TMPFS_NODE(vp);
511 memset(&tfh, 0, sizeof(tfh));
512 tfh.tf_len = sizeof(struct tmpfs_fid);
513 tfh.tf_gen = node->tn_gen;
514 tfh.tf_id = node->tn_id;
515 memcpy(fhp, &tfh, sizeof(tfh));
516 return (0);
517}
518
519/* --------------------------------------------------------------------- */
520
521/*
522 * tmpfs vfs operations.
523 */
524
525static struct vfsops tmpfs_vfsops = {
526 .vfs_mount = tmpfs_mount,
527 .vfs_unmount = tmpfs_unmount,
528 .vfs_root = tmpfs_root,
529 .vfs_statfs = tmpfs_statfs,
530 .vfs_fhtovp = tmpfs_fhtovp,
531 .vfs_vptofh = tmpfs_vptofh,
532 .vfs_sync = vfs_stdsync
533};
534
535VFS_SET(tmpfs_vfsops, tmpfs, 0);
536MODULE_VERSION(tmpfs, 1);