tmpfs_vfsops.c revision 222167
1170808Sdelphij/*	$NetBSD: tmpfs_vfsops.c,v 1.10 2005/12/11 12:24:29 christos Exp $	*/
2170808Sdelphij
3182739Sdelphij/*-
4170808Sdelphij * Copyright (c) 2005 The NetBSD Foundation, Inc.
5170808Sdelphij * All rights reserved.
6170808Sdelphij *
7170808Sdelphij * This code is derived from software contributed to The NetBSD Foundation
8170808Sdelphij * by Julio M. Merino Vidal, developed as part of Google's Summer of Code
9170808Sdelphij * 2005 program.
10170808Sdelphij *
11170808Sdelphij * Redistribution and use in source and binary forms, with or without
12170808Sdelphij * modification, are permitted provided that the following conditions
13170808Sdelphij * are met:
14170808Sdelphij * 1. Redistributions of source code must retain the above copyright
15170808Sdelphij *    notice, this list of conditions and the following disclaimer.
16170808Sdelphij * 2. Redistributions in binary form must reproduce the above copyright
17170808Sdelphij *    notice, this list of conditions and the following disclaimer in the
18170808Sdelphij *    documentation and/or other materials provided with the distribution.
19170808Sdelphij *
20170808Sdelphij * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
21170808Sdelphij * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22170808Sdelphij * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23170808Sdelphij * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
24170808Sdelphij * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25170808Sdelphij * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26170808Sdelphij * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27170808Sdelphij * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28170808Sdelphij * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29170808Sdelphij * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30170808Sdelphij * POSSIBILITY OF SUCH DAMAGE.
31170808Sdelphij */
32170808Sdelphij
33170808Sdelphij/*
34170808Sdelphij * Efficient memory file system.
35170808Sdelphij *
36170808Sdelphij * tmpfs is a file system that uses NetBSD's virtual memory sub-system
37170808Sdelphij * (the well-known UVM) to store file data and metadata in an efficient
38170808Sdelphij * way.  This means that it does not follow the structure of an on-disk
39170808Sdelphij * file system because it simply does not need to.  Instead, it uses
40170808Sdelphij * memory-specific data structures and algorithms to automatically
41170808Sdelphij * allocate and release resources.
42170808Sdelphij */
43170808Sdelphij#include <sys/cdefs.h>
44170808Sdelphij__FBSDID("$FreeBSD: head/sys/fs/tmpfs/tmpfs_vfsops.c 222167 2011-05-22 01:07:54Z rmacklem $");
45170808Sdelphij
46170808Sdelphij#include <sys/param.h>
47171308Sdelphij#include <sys/limits.h>
48170808Sdelphij#include <sys/lock.h>
49170808Sdelphij#include <sys/mutex.h>
50170808Sdelphij#include <sys/kernel.h>
51170808Sdelphij#include <sys/stat.h>
52170808Sdelphij#include <sys/systm.h>
53170808Sdelphij#include <sys/sysctl.h>
54170808Sdelphij
55170808Sdelphij#include <vm/vm.h>
56170808Sdelphij#include <vm/vm_object.h>
57170808Sdelphij#include <vm/vm_param.h>
58170808Sdelphij
59170808Sdelphij#include <fs/tmpfs/tmpfs.h>
60170808Sdelphij
61170808Sdelphij/*
62171070Sdelphij * Default permission for root node
63170808Sdelphij */
64170808Sdelphij#define TMPFS_DEFAULT_ROOT_MODE	(S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH)
65170808Sdelphij
66170808SdelphijMALLOC_DEFINE(M_TMPFSMNT, "tmpfs mount", "tmpfs mount structures");
67171087SdelphijMALLOC_DEFINE(M_TMPFSNAME, "tmpfs name", "tmpfs file names");
68170808Sdelphij
69170808Sdelphij/* --------------------------------------------------------------------- */
70170808Sdelphij
71191990Sattiliostatic int	tmpfs_mount(struct mount *);
72191990Sattiliostatic int	tmpfs_unmount(struct mount *, int);
73191990Sattiliostatic int	tmpfs_root(struct mount *, int flags, struct vnode **);
74222167Srmacklemstatic int	tmpfs_fhtovp(struct mount *, struct fid *, int,
75222167Srmacklem		    struct vnode **);
76191990Sattiliostatic int	tmpfs_statfs(struct mount *, struct statfs *);
77170808Sdelphij
78170808Sdelphij/* --------------------------------------------------------------------- */
79170808Sdelphij
80170808Sdelphijstatic const char *tmpfs_opts[] = {
81203164Sjh	"from", "size", "maxfilesize", "inodes", "uid", "gid", "mode", "export",
82170808Sdelphij	NULL
83170808Sdelphij};
84170808Sdelphij
85170808Sdelphij/* --------------------------------------------------------------------- */
86170808Sdelphij
87171029Sdelphijstatic int
88171029Sdelphijtmpfs_node_ctor(void *mem, int size, void *arg, int flags)
89171029Sdelphij{
90171029Sdelphij	struct tmpfs_node *node = (struct tmpfs_node *)mem;
91171070Sdelphij
92171362Sdelphij	node->tn_gen++;
93171029Sdelphij	node->tn_size = 0;
94171029Sdelphij	node->tn_status = 0;
95171029Sdelphij	node->tn_flags = 0;
96171029Sdelphij	node->tn_links = 0;
97171029Sdelphij	node->tn_vnode = NULL;
98171029Sdelphij	node->tn_vpstate = 0;
99170808Sdelphij
100171029Sdelphij	return (0);
101171029Sdelphij}
102171029Sdelphij
103171029Sdelphijstatic void
104171029Sdelphijtmpfs_node_dtor(void *mem, int size, void *arg)
105171029Sdelphij{
106171029Sdelphij	struct tmpfs_node *node = (struct tmpfs_node *)mem;
107171029Sdelphij	node->tn_type = VNON;
108171029Sdelphij}
109171029Sdelphij
110171070Sdelphijstatic int
111171029Sdelphijtmpfs_node_init(void *mem, int size, int flags)
112171029Sdelphij{
113171029Sdelphij	struct tmpfs_node *node = (struct tmpfs_node *)mem;
114171029Sdelphij	node->tn_id = 0;
115171029Sdelphij
116171029Sdelphij	mtx_init(&node->tn_interlock, "tmpfs node interlock", NULL, MTX_DEF);
117171362Sdelphij	node->tn_gen = arc4random();
118171070Sdelphij
119171029Sdelphij	return (0);
120171029Sdelphij}
121171029Sdelphij
122171029Sdelphijstatic void
123171029Sdelphijtmpfs_node_fini(void *mem, int size)
124171029Sdelphij{
125171029Sdelphij	struct tmpfs_node *node = (struct tmpfs_node *)mem;
126171070Sdelphij
127171029Sdelphij	mtx_destroy(&node->tn_interlock);
128171029Sdelphij}
129171029Sdelphij
130170808Sdelphijstatic int
131191990Sattiliotmpfs_mount(struct mount *mp)
132170808Sdelphij{
133170808Sdelphij	struct tmpfs_mount *tmp;
134170808Sdelphij	struct tmpfs_node *root;
135203164Sjh	size_t pages;
136202708Sjh	uint32_t nodes;
137170808Sdelphij	int error;
138171308Sdelphij	/* Size counters. */
139202708Sjh	u_int nodes_max;
140203164Sjh	u_quad_t size_max, maxfilesize;
141170808Sdelphij
142171308Sdelphij	/* Root node attributes. */
143202187Sjh	uid_t root_uid;
144202187Sjh	gid_t root_gid;
145202187Sjh	mode_t root_mode;
146171308Sdelphij
147202187Sjh	struct vattr va;
148171308Sdelphij
149170808Sdelphij	if (vfs_filteropt(mp->mnt_optnew, tmpfs_opts))
150170808Sdelphij		return (EINVAL);
151170808Sdelphij
152170808Sdelphij	if (mp->mnt_flag & MNT_UPDATE) {
153170808Sdelphij		/* XXX: There is no support yet to update file system
154170808Sdelphij		 * settings.  Should be added. */
155170808Sdelphij
156170808Sdelphij		return EOPNOTSUPP;
157170808Sdelphij	}
158170808Sdelphij
159172441Sdelphij	printf("WARNING: TMPFS is considered to be a highly experimental "
160173724Sdelphij	    "feature in FreeBSD.\n");
161172441Sdelphij
162175202Sattilio	vn_lock(mp->mnt_vnodecovered, LK_SHARED | LK_RETRY);
163182371Sattilio	error = VOP_GETATTR(mp->mnt_vnodecovered, &va, mp->mnt_cred);
164175294Sattilio	VOP_UNLOCK(mp->mnt_vnodecovered, 0);
165171308Sdelphij	if (error)
166171308Sdelphij		return (error);
167170808Sdelphij
168171308Sdelphij	if (mp->mnt_cred->cr_ruid != 0 ||
169171308Sdelphij	    vfs_scanopt(mp->mnt_optnew, "gid", "%d", &root_gid) != 1)
170171308Sdelphij		root_gid = va.va_gid;
171171308Sdelphij	if (mp->mnt_cred->cr_ruid != 0 ||
172171308Sdelphij	    vfs_scanopt(mp->mnt_optnew, "uid", "%d", &root_uid) != 1)
173171308Sdelphij		root_uid = va.va_uid;
174171308Sdelphij	if (mp->mnt_cred->cr_ruid != 0 ||
175173570Sdelphij	    vfs_scanopt(mp->mnt_optnew, "mode", "%ho", &root_mode) != 1)
176171308Sdelphij		root_mode = va.va_mode;
177202708Sjh	if (vfs_scanopt(mp->mnt_optnew, "inodes", "%u", &nodes_max) != 1)
178171308Sdelphij		nodes_max = 0;
179173724Sdelphij	if (vfs_scanopt(mp->mnt_optnew, "size", "%qu", &size_max) != 1)
180171308Sdelphij		size_max = 0;
181203164Sjh	if (vfs_scanopt(mp->mnt_optnew, "maxfilesize", "%qu",
182203164Sjh	    &maxfilesize) != 1)
183203164Sjh		maxfilesize = 0;
184170808Sdelphij
185170808Sdelphij	/* Do not allow mounts if we do not have enough memory to preserve
186170808Sdelphij	 * the minimum reserved pages. */
187203164Sjh	if (tmpfs_mem_info() < TMPFS_PAGES_RESERVED)
188170808Sdelphij		return ENOSPC;
189170808Sdelphij
190170808Sdelphij	/* Get the maximum number of memory pages this file system is
191170808Sdelphij	 * allowed to use, based on the maximum size the user passed in
192170808Sdelphij	 * the mount structure.  A value of zero is treated as if the
193170808Sdelphij	 * maximum available space was requested. */
194202187Sjh	if (size_max < PAGE_SIZE || size_max > SIZE_MAX - PAGE_SIZE)
195170808Sdelphij		pages = SIZE_MAX;
196170808Sdelphij	else
197171308Sdelphij		pages = howmany(size_max, PAGE_SIZE);
198170808Sdelphij	MPASS(pages > 0);
199170808Sdelphij
200202708Sjh	if (nodes_max <= 3) {
201202708Sjh		if (pages > UINT32_MAX - 3)
202202708Sjh			nodes = UINT32_MAX;
203202708Sjh		else
204202708Sjh			nodes = pages + 3;
205202708Sjh	} else
206171308Sdelphij		nodes = nodes_max;
207170808Sdelphij	MPASS(nodes >= 3);
208170808Sdelphij
209170808Sdelphij	/* Allocate the tmpfs mount structure and fill it. */
210170808Sdelphij	tmp = (struct tmpfs_mount *)malloc(sizeof(struct tmpfs_mount),
211170808Sdelphij	    M_TMPFSMNT, M_WAITOK | M_ZERO);
212171070Sdelphij
213170808Sdelphij	mtx_init(&tmp->allnode_lock, "tmpfs allnode lock", NULL, MTX_DEF);
214170808Sdelphij	tmp->tm_nodes_max = nodes;
215170808Sdelphij	tmp->tm_nodes_inuse = 0;
216203164Sjh	tmp->tm_maxfilesize = maxfilesize > 0 ? maxfilesize : UINT64_MAX;
217170808Sdelphij	LIST_INIT(&tmp->tm_nodes_used);
218171070Sdelphij
219170808Sdelphij	tmp->tm_pages_max = pages;
220170808Sdelphij	tmp->tm_pages_used = 0;
221171362Sdelphij	tmp->tm_ino_unr = new_unrhdr(2, INT_MAX, &tmp->allnode_lock);
222173724Sdelphij	tmp->tm_dirent_pool = uma_zcreate("TMPFS dirent",
223173724Sdelphij	    sizeof(struct tmpfs_dirent),
224173724Sdelphij	    NULL, NULL, NULL, NULL,
225173724Sdelphij	    UMA_ALIGN_PTR, 0);
226173724Sdelphij	tmp->tm_node_pool = uma_zcreate("TMPFS node",
227173724Sdelphij	    sizeof(struct tmpfs_node),
228173724Sdelphij	    tmpfs_node_ctor, tmpfs_node_dtor,
229173724Sdelphij	    tmpfs_node_init, tmpfs_node_fini,
230173724Sdelphij	    UMA_ALIGN_PTR, 0);
231170808Sdelphij
232170808Sdelphij	/* Allocate the root node. */
233171308Sdelphij	error = tmpfs_alloc_node(tmp, VDIR, root_uid,
234171308Sdelphij	    root_gid, root_mode & ALLPERMS, NULL, NULL,
235191990Sattilio	    VNOVAL, &root);
236170808Sdelphij
237170808Sdelphij	if (error != 0 || root == NULL) {
238171029Sdelphij	    uma_zdestroy(tmp->tm_node_pool);
239171029Sdelphij	    uma_zdestroy(tmp->tm_dirent_pool);
240171362Sdelphij	    delete_unrhdr(tmp->tm_ino_unr);
241170808Sdelphij	    free(tmp, M_TMPFSMNT);
242170808Sdelphij	    return error;
243170808Sdelphij	}
244171362Sdelphij	KASSERT(root->tn_id == 2, ("tmpfs root with invalid ino: %d", root->tn_id));
245170808Sdelphij	tmp->tm_root = root;
246170808Sdelphij
247170808Sdelphij	MNT_ILOCK(mp);
248170808Sdelphij	mp->mnt_flag |= MNT_LOCAL;
249170808Sdelphij	mp->mnt_kern_flag |= MNTK_MPSAFE;
250170808Sdelphij	MNT_IUNLOCK(mp);
251171070Sdelphij
252170808Sdelphij	mp->mnt_data = tmp;
253170808Sdelphij	mp->mnt_stat.f_namemax = MAXNAMLEN;
254170808Sdelphij	vfs_getnewfsid(mp);
255170808Sdelphij	vfs_mountedfrom(mp, "tmpfs");
256170808Sdelphij
257170808Sdelphij	return 0;
258170808Sdelphij}
259170808Sdelphij
260170808Sdelphij/* --------------------------------------------------------------------- */
261170808Sdelphij
262170808Sdelphij/* ARGSUSED2 */
263170808Sdelphijstatic int
264191990Sattiliotmpfs_unmount(struct mount *mp, int mntflags)
265170808Sdelphij{
266170808Sdelphij	int error;
267170808Sdelphij	int flags = 0;
268170808Sdelphij	struct tmpfs_mount *tmp;
269170808Sdelphij	struct tmpfs_node *node;
270170808Sdelphij
271170808Sdelphij	/* Handle forced unmounts. */
272170808Sdelphij	if (mntflags & MNT_FORCE)
273170808Sdelphij		flags |= FORCECLOSE;
274170808Sdelphij
275170808Sdelphij	/* Finalize all pending I/O. */
276191990Sattilio	error = vflush(mp, 0, flags, curthread);
277170808Sdelphij	if (error != 0)
278170808Sdelphij		return error;
279170808Sdelphij
280170808Sdelphij	tmp = VFS_TO_TMPFS(mp);
281171070Sdelphij
282170808Sdelphij	/* Free all associated data.  The loop iterates over the linked list
283170808Sdelphij	 * we have containing all used nodes.  For each of them that is
284170808Sdelphij	 * a directory, we free all its directory entries.  Note that after
285170808Sdelphij	 * freeing a node, it will automatically go to the available list,
286170808Sdelphij	 * so we will later have to iterate over it to release its items. */
287170808Sdelphij	node = LIST_FIRST(&tmp->tm_nodes_used);
288170808Sdelphij	while (node != NULL) {
289170808Sdelphij		struct tmpfs_node *next;
290170808Sdelphij
291170808Sdelphij		if (node->tn_type == VDIR) {
292170808Sdelphij			struct tmpfs_dirent *de;
293170808Sdelphij
294170808Sdelphij			de = TAILQ_FIRST(&node->tn_dir.tn_dirhead);
295170808Sdelphij			while (de != NULL) {
296170808Sdelphij				struct tmpfs_dirent *nde;
297170808Sdelphij
298170808Sdelphij				nde = TAILQ_NEXT(de, td_entries);
299170808Sdelphij				tmpfs_free_dirent(tmp, de, FALSE);
300170808Sdelphij				de = nde;
301170808Sdelphij				node->tn_size -= sizeof(struct tmpfs_dirent);
302170808Sdelphij			}
303170808Sdelphij		}
304170808Sdelphij
305170808Sdelphij		next = LIST_NEXT(node, tn_entries);
306170808Sdelphij		tmpfs_free_node(tmp, node);
307170808Sdelphij		node = next;
308170808Sdelphij	}
309170808Sdelphij
310171029Sdelphij	uma_zdestroy(tmp->tm_dirent_pool);
311171029Sdelphij	uma_zdestroy(tmp->tm_node_pool);
312171362Sdelphij	delete_unrhdr(tmp->tm_ino_unr);
313170808Sdelphij
314170808Sdelphij	mtx_destroy(&tmp->allnode_lock);
315170808Sdelphij	MPASS(tmp->tm_pages_used == 0);
316171308Sdelphij	MPASS(tmp->tm_nodes_inuse == 0);
317170808Sdelphij
318170808Sdelphij	/* Throw away the tmpfs_mount structure. */
319170808Sdelphij	free(mp->mnt_data, M_TMPFSMNT);
320170808Sdelphij	mp->mnt_data = NULL;
321171070Sdelphij
322170808Sdelphij	MNT_ILOCK(mp);
323170808Sdelphij	mp->mnt_flag &= ~MNT_LOCAL;
324170808Sdelphij	MNT_IUNLOCK(mp);
325170808Sdelphij	return 0;
326170808Sdelphij}
327170808Sdelphij
328170808Sdelphij/* --------------------------------------------------------------------- */
329170808Sdelphij
330170808Sdelphijstatic int
331191990Sattiliotmpfs_root(struct mount *mp, int flags, struct vnode **vpp)
332170808Sdelphij{
333170808Sdelphij	int error;
334191990Sattilio	error = tmpfs_alloc_vp(mp, VFS_TO_TMPFS(mp)->tm_root, flags, vpp);
335170808Sdelphij
336170808Sdelphij	if (!error)
337171029Sdelphij		(*vpp)->v_vflag |= VV_ROOT;
338170808Sdelphij
339170808Sdelphij	return error;
340170808Sdelphij}
341170808Sdelphij
342170808Sdelphij/* --------------------------------------------------------------------- */
343170808Sdelphij
344170808Sdelphijstatic int
345222167Srmacklemtmpfs_fhtovp(struct mount *mp, struct fid *fhp, int flags,
346222167Srmacklem    struct vnode **vpp)
347170808Sdelphij{
348170808Sdelphij	boolean_t found;
349170808Sdelphij	struct tmpfs_fid *tfhp;
350170808Sdelphij	struct tmpfs_mount *tmp;
351170808Sdelphij	struct tmpfs_node *node;
352170808Sdelphij
353170808Sdelphij	tmp = VFS_TO_TMPFS(mp);
354170808Sdelphij
355170808Sdelphij	tfhp = (struct tmpfs_fid *)fhp;
356170808Sdelphij	if (tfhp->tf_len != sizeof(struct tmpfs_fid))
357170808Sdelphij		return EINVAL;
358170808Sdelphij
359170808Sdelphij	if (tfhp->tf_id >= tmp->tm_nodes_max)
360170808Sdelphij		return EINVAL;
361170808Sdelphij
362170808Sdelphij	found = FALSE;
363170808Sdelphij
364170808Sdelphij	TMPFS_LOCK(tmp);
365170808Sdelphij	LIST_FOREACH(node, &tmp->tm_nodes_used, tn_entries) {
366170808Sdelphij		if (node->tn_id == tfhp->tf_id &&
367170808Sdelphij		    node->tn_gen == tfhp->tf_gen) {
368170808Sdelphij			found = TRUE;
369170808Sdelphij			break;
370170808Sdelphij		}
371170808Sdelphij	}
372170808Sdelphij	TMPFS_UNLOCK(tmp);
373170808Sdelphij
374171799Sdelphij	if (found)
375191990Sattilio		return (tmpfs_alloc_vp(mp, node, LK_EXCLUSIVE, vpp));
376171799Sdelphij
377171799Sdelphij	return (EINVAL);
378170808Sdelphij}
379170808Sdelphij
380170808Sdelphij/* --------------------------------------------------------------------- */
381170808Sdelphij
382170808Sdelphij/* ARGSUSED2 */
383170808Sdelphijstatic int
384191990Sattiliotmpfs_statfs(struct mount *mp, struct statfs *sbp)
385170808Sdelphij{
386170808Sdelphij	fsfilcnt_t freenodes;
387170808Sdelphij	struct tmpfs_mount *tmp;
388170808Sdelphij
389170808Sdelphij	tmp = VFS_TO_TMPFS(mp);
390170808Sdelphij
391171070Sdelphij	sbp->f_iosize = PAGE_SIZE;
392170808Sdelphij	sbp->f_bsize = PAGE_SIZE;
393170808Sdelphij
394170808Sdelphij	sbp->f_blocks = TMPFS_PAGES_MAX(tmp);
395170808Sdelphij	sbp->f_bavail = sbp->f_bfree = TMPFS_PAGES_AVAIL(tmp);
396170808Sdelphij
397170808Sdelphij	freenodes = MIN(tmp->tm_nodes_max - tmp->tm_nodes_inuse,
398170808Sdelphij	    TMPFS_PAGES_AVAIL(tmp) * PAGE_SIZE / sizeof(struct tmpfs_node));
399170808Sdelphij
400170808Sdelphij	sbp->f_files = freenodes + tmp->tm_nodes_inuse;
401170808Sdelphij	sbp->f_ffree = freenodes;
402170808Sdelphij	/* sbp->f_owner = tmp->tn_uid; */
403170808Sdelphij
404170808Sdelphij	return 0;
405170808Sdelphij}
406170808Sdelphij
407170808Sdelphij/* --------------------------------------------------------------------- */
408170808Sdelphij
409170808Sdelphij/*
410170808Sdelphij * tmpfs vfs operations.
411170808Sdelphij */
412170808Sdelphij
413170808Sdelphijstruct vfsops tmpfs_vfsops = {
414170808Sdelphij	.vfs_mount =			tmpfs_mount,
415170808Sdelphij	.vfs_unmount =			tmpfs_unmount,
416170808Sdelphij	.vfs_root =			tmpfs_root,
417170808Sdelphij	.vfs_statfs =			tmpfs_statfs,
418170808Sdelphij	.vfs_fhtovp =			tmpfs_fhtovp,
419170808Sdelphij};
420170808SdelphijVFS_SET(tmpfs_vfsops, tmpfs, 0);
421