tmpfs_vfsops.c revision 1.2 1 /* $NetBSD: tmpfs_vfsops.c,v 1.2 2005/09/10 22:28:57 jmmv 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.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. All advertising materials mentioning features or use of this software
19 * must display the following acknowledgement:
20 * This product includes software developed by the NetBSD
21 * Foundation, Inc. and its contributors.
22 * 4. Neither the name of The NetBSD Foundation nor the names of its
23 * contributors may be used to endorse or promote products derived
24 * from this software without specific prior written permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36 * POSSIBILITY OF SUCH DAMAGE.
37 */
38
39 /*
40 * Efficient memory file system.
41 */
42
43 #include <sys/cdefs.h>
44 __KERNEL_RCSID(0, "$NetBSD: tmpfs_vfsops.c,v 1.2 2005/09/10 22:28:57 jmmv Exp $");
45
46 #include <sys/param.h>
47 #include <sys/types.h>
48 #include <sys/malloc.h>
49 #include <sys/mount.h>
50 #include <sys/systm.h>
51 #include <sys/vnode.h>
52
53 #include <fs/tmpfs/tmpfs.h>
54
55 MALLOC_DEFINE(M_TMPFSMNT, "tmpfs mount", "tmpfs mount structures");
56
57 /* --------------------------------------------------------------------- */
58
59 static int tmpfs_mount(struct mount *, const char *, void *,
60 struct nameidata *, struct proc *);
61 static int tmpfs_start(struct mount *, int, struct proc *);
62 static int tmpfs_unmount(struct mount *, int, struct proc *);
63 static int tmpfs_root(struct mount *, struct vnode **);
64 static int tmpfs_quotactl(struct mount *, int, uid_t, void *,
65 struct proc *);
66 static int tmpfs_vget(struct mount *, ino_t, struct vnode **);
67 static int tmpfs_fhtovp(struct mount *, struct fid *, struct vnode **);
68 static int tmpfs_vptofh(struct vnode *, struct fid *);
69 static int tmpfs_statvfs(struct mount *, struct statvfs *, struct proc *);
70 static int tmpfs_sync(struct mount *, int, struct ucred *, struct proc *);
71 static void tmpfs_init(void);
72 static void tmpfs_done(void);
73 static int tmpfs_checkexp(struct mount *, struct mbuf *, int *,
74 struct ucred **);
75 static int tmpfs_snapshot(struct mount *, struct vnode *,
76 struct timespec *);
77
78 /* --------------------------------------------------------------------- */
79
80 static int
81 tmpfs_mount(struct mount *mp, const char *path, void *data,
82 struct nameidata *ndp, struct proc *p)
83 {
84 int error;
85 ino_t nodes;
86 size_t pages;
87 struct tmpfs_mount *tmp;
88 struct tmpfs_node *root;
89 struct tmpfs_args args;
90
91 /* Handle retrieval of mount point arguments. */
92 if (mp->mnt_flag & MNT_GETARGS) {
93 if (mp->mnt_data == NULL)
94 return EIO;
95 tmp = VFS_TO_TMPFS(mp);
96
97 args.ta_version = TMPFS_ARGS_VERSION;
98 args.ta_fspec = NULL;
99 args.ta_nodes_max = tmp->tm_nodes_max;
100 args.ta_size_max = tmp->tm_pages_max * PAGE_SIZE;
101
102 root = tmp->tm_root;
103 args.ta_root_uid = root->tn_uid;
104 args.ta_root_gid = root->tn_gid;
105 args.ta_root_mode = root->tn_mode;
106
107 vfs_showexport(mp, &args, &tmp->tm_export);
108
109 return copyout(&args, data, sizeof(args));
110 }
111
112 /* Verify that we have parameters, as they are required. */
113 if (data == NULL)
114 return EINVAL;
115
116 /* Get the mount parameters. */
117 error = copyin(data, &args, sizeof(args));
118 if (error != 0)
119 return error;
120
121 if (mp->mnt_flag & MNT_UPDATE) {
122 if (mp->mnt_data == NULL)
123 return EIO;
124 tmp = VFS_TO_TMPFS(mp);
125
126 if (args.ta_fspec == NULL) {
127 /* Update NFS export information. */
128 return vfs_export(mp, &tmp->tm_export, &args.ta_export);
129 }
130
131 /* XXX: There is no support yet to update file system
132 * settings. Should be added. */
133
134 return EOPNOTSUPP;
135 }
136
137 if (args.ta_version != TMPFS_ARGS_VERSION)
138 return EINVAL;
139
140 /* Do not allow mounts if we do not have enough memory to preserve
141 * the minimum reserved pages. */
142 if (tmpfs_mem_info(TRUE) < TMPFS_PAGES_RESERVED)
143 return EINVAL;
144
145 /* Get the maximum number of memory pages this file system is
146 * allowed to use, based on the maximum size the user passed in
147 * the mount structure. A value of zero is treated as if the
148 * maximum available space was requested. */
149 if (args.ta_size_max == 0)
150 pages = SIZE_MAX;
151 else
152 pages = args.ta_size_max / PAGE_SIZE +
153 (args.ta_size_max % PAGE_SIZE == 0 ? 0 : 1);
154
155 if (args.ta_nodes_max == 0)
156 nodes = pages * PAGE_SIZE / 1024;
157 else
158 nodes = args.ta_nodes_max;
159
160 /* Allocate the tmpfs mount structure and fill it. */
161 tmp = (struct tmpfs_mount *)malloc(sizeof(struct tmpfs_mount),
162 M_TMPFSMNT, M_WAITOK);
163 KASSERT(tmp != NULL);
164
165 tmp->tm_nodes_max = nodes;
166 tmp->tm_nodes_last = 0;
167 LIST_INIT(&tmp->tm_nodes_used);
168 LIST_INIT(&tmp->tm_nodes_avail);
169
170 tmp->tm_pages_max = pages;
171 tmp->tm_pages_used = 0;
172 tmpfs_pool_init(&tmp->tm_dirent_pool, sizeof(struct tmpfs_dirent),
173 "dirent", tmp);
174 tmpfs_pool_init(&tmp->tm_node_pool, sizeof(struct tmpfs_node),
175 "node", tmp);
176 tmpfs_str_pool_init(&tmp->tm_str_pool, tmp);
177 bzero(&tmp->tm_export, sizeof(struct netexport));
178
179 /* Allocate the root node. */
180 error = tmpfs_alloc_node(tmp, VDIR, args.ta_root_uid,
181 args.ta_root_gid, args.ta_root_mode, NULL, NULL, VNOVAL, p,
182 &root);
183 KASSERT(error == 0 && root != NULL);
184 tmp->tm_root = root;
185
186 mp->mnt_data = tmp;
187 mp->mnt_flag |= MNT_LOCAL;
188 mp->mnt_stat.f_namemax = MAXNAMLEN;
189 vfs_getnewfsid(mp);
190
191 return set_statvfs_info(path, UIO_USERSPACE, "tmpfs", UIO_SYSSPACE,
192 mp, p);
193 }
194
195 /* --------------------------------------------------------------------- */
196
197 static int
198 tmpfs_start(struct mount *mp, int flags, struct proc *p)
199 {
200
201 return 0;
202 }
203
204 /* --------------------------------------------------------------------- */
205
206 /* ARGSUSED2 */
207 static int
208 tmpfs_unmount(struct mount *mp, int mntflags, struct proc *p)
209 {
210 int error;
211 int flags = 0;
212 struct tmpfs_mount *tmp;
213 struct tmpfs_node *node;
214
215 /* Handle forced unmounts. */
216 if (mntflags & MNT_FORCE)
217 flags |= FORCECLOSE;
218
219 /* Finalize all pending I/O. */
220 error = vflush(mp, NULL, flags);
221 if (error != 0)
222 return error;
223
224 tmp = VFS_TO_TMPFS(mp);
225
226 /* Free all associated data. The loop iterates over the linked list
227 * we have containing all used nodes. For each of them that is
228 * a directory, we free all its directory entries. Note that after
229 * freeing a node, it will automatically go to the available list,
230 * so we will later have to iterate over it to release its items. */
231 node = LIST_FIRST(&tmp->tm_nodes_used);
232 while (node != NULL) {
233 struct tmpfs_node *next;
234
235 if (node->tn_type == VDIR) {
236 struct tmpfs_dirent *de;
237
238 de = TAILQ_FIRST(&node->tn_dir);
239 while (de != NULL) {
240 struct tmpfs_dirent *nde;
241
242 nde = TAILQ_NEXT(de, td_entries);
243 tmpfs_free_dirent(tmp, de, FALSE);
244 de = nde;
245 node->tn_size -= sizeof(struct tmpfs_dirent);
246 }
247 }
248
249 next = LIST_NEXT(node, tn_entries);
250 tmpfs_free_node(tmp, node);
251 node = next;
252 }
253 node = LIST_FIRST(&tmp->tm_nodes_avail);
254 while (node != NULL) {
255 struct tmpfs_node *next;
256
257 next = LIST_NEXT(node, tn_entries);
258 LIST_REMOVE(node, tn_entries);
259 TMPFS_POOL_PUT(&tmp->tm_node_pool, node);
260 node = next;
261 }
262
263 tmpfs_pool_destroy(&tmp->tm_dirent_pool);
264 tmpfs_pool_destroy(&tmp->tm_node_pool);
265 tmpfs_str_pool_destroy(&tmp->tm_str_pool);
266
267 KASSERT(tmp->tm_pages_used == 0);
268
269 /* Throw away the tmpfs_mount structure. */
270 free(mp->mnt_data, M_TMPFSMNT);
271 mp->mnt_data = NULL;
272
273 return 0;
274 }
275
276 /* --------------------------------------------------------------------- */
277
278 static int
279 tmpfs_root(struct mount *mp, struct vnode **vpp)
280 {
281
282 return tmpfs_alloc_vp(mp, VFS_TO_TMPFS(mp)->tm_root, vpp);
283 }
284
285 /* --------------------------------------------------------------------- */
286
287 static int
288 tmpfs_quotactl(struct mount *mp, int cmd, uid_t uid, void *arg,
289 struct proc *p)
290 {
291
292 printf("tmpfs_quotactl called; need for it unknown yet\n");
293 return EOPNOTSUPP;
294 }
295
296 /* --------------------------------------------------------------------- */
297
298 static int
299 tmpfs_vget(struct mount *mp, ino_t ino, struct vnode **vpp)
300 {
301
302 printf("tmpfs_vget called; need for it unknown yet\n");
303 return EOPNOTSUPP;
304 }
305
306 /* --------------------------------------------------------------------- */
307
308 static int
309 tmpfs_fhtovp(struct mount *mp, struct fid *fhp, struct vnode **vpp)
310 {
311 boolean_t found;
312 struct tmpfs_fid *tfhp;
313 struct tmpfs_mount *tmp;
314 struct tmpfs_node *node;
315
316 tmp = VFS_TO_TMPFS(mp);
317
318 tfhp = (struct tmpfs_fid *)fhp;
319 if (tfhp->tf_len != sizeof(struct tmpfs_fid))
320 return EINVAL;
321
322 if (tfhp->tf_id >= tmp->tm_nodes_max)
323 return EINVAL;
324
325 found = FALSE;
326 LIST_FOREACH(node, &tmp->tm_nodes_used, tn_entries) {
327 if (node->tn_id == tfhp->tf_id &&
328 node->tn_gen == tfhp->tf_gen) {
329 found = TRUE;
330 break;
331 }
332 }
333
334 return found ? tmpfs_alloc_vp(mp, node, vpp) : EINVAL;
335 }
336
337 /* --------------------------------------------------------------------- */
338
339 static int
340 tmpfs_vptofh(struct vnode *vp, struct fid *fhp)
341 {
342 struct tmpfs_fid *tfhp;
343 struct tmpfs_node *node;
344
345 tfhp = (struct tmpfs_fid *)fhp;
346 node = VP_TO_TMPFS_NODE(vp);
347
348 tfhp->tf_len = sizeof(struct tmpfs_fid);
349 tfhp->tf_id = node->tn_id;
350 tfhp->tf_gen = node->tn_gen;
351
352 return 0;
353 }
354
355 /* --------------------------------------------------------------------- */
356
357 /* ARGSUSED2 */
358 static int
359 tmpfs_statvfs(struct mount *mp, struct statvfs *sbp, struct proc *p)
360 {
361 fsfilcnt_t freenodes, usednodes;
362 struct tmpfs_mount *tmp;
363 struct tmpfs_node *dummy;
364
365 tmp = VFS_TO_TMPFS(mp);
366
367 sbp->f_iosize = sbp->f_frsize = sbp->f_bsize = PAGE_SIZE;
368
369 sbp->f_blocks = TMPFS_PAGES_MAX(tmp);
370 sbp->f_bavail = sbp->f_bfree = TMPFS_PAGES_AVAIL(tmp);
371 sbp->f_bresvd = 0;
372
373 freenodes = MIN(tmp->tm_nodes_max - tmp->tm_nodes_last,
374 TMPFS_PAGES_AVAIL(tmp) * PAGE_SIZE / sizeof(struct tmpfs_node));
375 LIST_FOREACH(dummy, &tmp->tm_nodes_avail, tn_entries)
376 freenodes++;
377
378 usednodes = 0;
379 LIST_FOREACH(dummy, &tmp->tm_nodes_used, tn_entries)
380 usednodes++;
381
382 sbp->f_files = freenodes + usednodes;
383 sbp->f_favail = sbp->f_ffree = freenodes;
384 sbp->f_fresvd = 0;
385
386 copy_statvfs_info(sbp, mp);
387
388 return 0;
389 }
390
391 /* --------------------------------------------------------------------- */
392
393 /* ARGSUSED0 */
394 static int
395 tmpfs_sync(struct mount *mp, int waitfor, struct ucred *uc, struct proc *p)
396 {
397
398 return 0;
399 }
400
401 /* --------------------------------------------------------------------- */
402
403 static void
404 tmpfs_init(void)
405 {
406
407 #ifdef _LKM
408 malloc_type_attach(M_TMPFSMNT);
409 #endif
410 }
411
412 /* --------------------------------------------------------------------- */
413
414 static void
415 tmpfs_done(void)
416 {
417
418 #ifdef _LKM
419 malloc_type_detach(M_TMPFSMNT);
420 #endif
421 }
422
423 /* --------------------------------------------------------------------- */
424
425 static int
426 tmpfs_checkexp(struct mount *mp, struct mbuf *mb, int *wh,
427 struct ucred **anon)
428 {
429 struct netcred *np;
430 struct tmpfs_mount *tmp;
431
432 tmp = VFS_TO_TMPFS(mp);
433
434 np = vfs_export_lookup(mp, &tmp->tm_export, mb);
435 if (np != NULL) {
436 *wh = np->netc_exflags;
437 *anon = &np->netc_anon;
438 }
439
440 return np == NULL ? EACCES : 0;
441 }
442
443 /* --------------------------------------------------------------------- */
444
445 static int
446 tmpfs_snapshot(struct mount *mp, struct vnode *vp, struct timespec *ctime)
447 {
448
449 return EOPNOTSUPP;
450 }
451
452 /* --------------------------------------------------------------------- */
453
454 /*
455 * tmpfs vfs operations.
456 */
457
458 extern const struct vnodeopv_desc tmpfs_fifoop_opv_desc;
459 extern const struct vnodeopv_desc tmpfs_specop_opv_desc;
460 extern const struct vnodeopv_desc tmpfs_vnodeop_opv_desc;
461
462 const struct vnodeopv_desc * const tmpfs_vnodeopv_descs[] = {
463 &tmpfs_fifoop_opv_desc,
464 &tmpfs_specop_opv_desc,
465 &tmpfs_vnodeop_opv_desc,
466 NULL,
467 };
468
469 struct vfsops tmpfs_vfsops = {
470 MOUNT_TMPFS, /* vfs_name */
471 tmpfs_mount, /* vfs_mount */
472 tmpfs_start, /* vfs_start */
473 tmpfs_unmount, /* vfs_unmount */
474 tmpfs_root, /* vfs_root */
475 tmpfs_quotactl, /* vfs_quotactl */
476 tmpfs_statvfs, /* vfs_statvfs */
477 tmpfs_sync, /* vfs_sync */
478 tmpfs_vget, /* vfs_vget */
479 tmpfs_fhtovp, /* vfs_fhtovp */
480 tmpfs_vptofh, /* vfs_vptofh */
481 tmpfs_init, /* vfs_init */
482 NULL, /* vfs_reinit */
483 tmpfs_done, /* vfs_done */
484 NULL, /* vfs_wassysctl: deprecated */
485 NULL, /* vfs_mountroot */
486 tmpfs_checkexp, /* vfs_checkexp */
487 tmpfs_snapshot, /* vfs_snapshot */
488 vfs_stdextattrctl, /* vfs_extattrctl */
489 tmpfs_vnodeopv_descs,
490 };
491 VFS_ATTACH(tmpfs_vfsops);
492