Line | Count | Source |
1 | | /* |
2 | | * ramfs.c - a malloc based filesystem |
3 | | * |
4 | | * Copyright (c) 2007 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix |
5 | | * |
6 | | * This program is free software; you can redistribute it and/or modify |
7 | | * it under the terms of the GNU General Public License version 2 |
8 | | * as published by the Free Software Foundation. |
9 | | * |
10 | | * This program is distributed in the hope that it will be useful, |
11 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
13 | | * GNU General Public License for more details. |
14 | | * |
15 | | */ |
16 | | |
17 | | #include <common.h> |
18 | | #include <driver.h> |
19 | | #include <init.h> |
20 | | #include <malloc.h> |
21 | | #include <fs.h> |
22 | | #include <command.h> |
23 | | #include <errno.h> |
24 | | #include <linux/stat.h> |
25 | | #include <xfuncs.h> |
26 | | #include <linux/sizes.h> |
27 | | |
28 | | #define CHUNK_SIZE (4096 * 2) |
29 | | |
30 | | struct ramfs_chunk { |
31 | | unsigned long ofs; |
32 | | int size; |
33 | | struct list_head list; |
34 | | char data[]; |
35 | | }; |
36 | | |
37 | | struct ramfs_inode { |
38 | | struct inode inode; |
39 | | char *name; |
40 | | char *symlink; |
41 | | ulong mode; |
42 | | |
43 | | /* bytes used in this inode */ |
44 | | unsigned long size; |
45 | | /* bytes currently allocated for this inode */ |
46 | | unsigned long alloc_size; |
47 | | |
48 | | struct list_head data; |
49 | | |
50 | | struct ramfs_chunk *current_chunk; |
51 | | }; |
52 | | |
53 | | static inline struct ramfs_inode *to_ramfs_inode(struct inode *inode) |
54 | 70.0k | { |
55 | 70.0k | return container_of(inode, struct ramfs_inode, inode); |
56 | 70.0k | } |
57 | | |
58 | | /* ---------------------------------------------------------------*/ |
59 | | |
60 | | static const struct super_operations ramfs_ops; |
61 | | static const struct inode_operations ramfs_dir_inode_operations; |
62 | | static const struct inode_operations ramfs_file_inode_operations; |
63 | | static const struct inode_operations ramfs_symlink_inode_operations; |
64 | | static const struct file_operations ramfs_file_operations; |
65 | | |
66 | | static struct inode *ramfs_get_inode(struct super_block *sb, const struct inode *dir, |
67 | | umode_t mode) |
68 | 48.8k | { |
69 | 48.8k | struct inode *inode = new_inode(sb); |
70 | | |
71 | 48.8k | if (!inode) |
72 | 0 | return NULL; |
73 | | |
74 | 48.8k | inode->i_ino = get_next_ino(); |
75 | 48.8k | inode->i_mode = mode; |
76 | | |
77 | 48.8k | switch (mode & S_IFMT) { |
78 | 0 | default: |
79 | 0 | return NULL; |
80 | 9.66k | case S_IFREG: |
81 | 9.66k | inode->i_op = &ramfs_file_inode_operations; |
82 | 9.66k | inode->i_fop = &ramfs_file_operations; |
83 | 9.66k | break; |
84 | 16.2k | case S_IFDIR: |
85 | 16.2k | inode->i_op = &ramfs_dir_inode_operations; |
86 | 16.2k | inode->i_fop = &simple_dir_operations; |
87 | 16.2k | inc_nlink(inode); |
88 | 16.2k | break; |
89 | 7.24k | case S_IFLNK: |
90 | 7.24k | inode->i_op = &ramfs_symlink_inode_operations; |
91 | 7.24k | break; |
92 | 0 | case S_IFBLK: |
93 | 15.7k | case S_IFCHR: |
94 | 15.7k | break; |
95 | 48.8k | } |
96 | | |
97 | 48.8k | return inode; |
98 | 48.8k | } |
99 | | |
100 | 12.3k | #define MIN_SIZE SZ_8K |
101 | | |
102 | | static struct ramfs_chunk *ramfs_get_chunk(unsigned long size) |
103 | 10.1k | { |
104 | 10.1k | struct ramfs_chunk *data; |
105 | | |
106 | 10.1k | if (size < MIN_SIZE) |
107 | 2.17k | size = MIN_SIZE; |
108 | | |
109 | 10.1k | data = calloc(struct_size(data, data, size), 1); |
110 | 10.1k | if (!data) |
111 | 0 | return NULL; |
112 | | |
113 | 10.1k | data->size = size; |
114 | | |
115 | 10.1k | return data; |
116 | 10.1k | } |
117 | | |
118 | | static void ramfs_put_chunk(struct ramfs_chunk *data) |
119 | 9.12k | { |
120 | 9.12k | free(data); |
121 | 9.12k | } |
122 | | |
123 | | /* ---------------------------------------------------------------*/ |
124 | | |
125 | | static int |
126 | | ramfs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode) |
127 | 33.5k | { |
128 | 33.5k | struct inode *inode = ramfs_get_inode(dir->i_sb, dir, mode); |
129 | | |
130 | 33.5k | if (!inode) |
131 | 0 | return -ENOSPC; |
132 | | |
133 | 33.5k | d_instantiate(dentry, inode); |
134 | 33.5k | dget(dentry); /* Extra count - pin the dentry in core */ |
135 | | |
136 | 33.5k | return 0; |
137 | 33.5k | } |
138 | | |
139 | | static int ramfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) |
140 | 16.1k | { |
141 | 16.1k | int ret; |
142 | | |
143 | 16.1k | ret = ramfs_mknod(dir, dentry, mode | S_IFDIR); |
144 | 16.1k | if (!ret) |
145 | 16.1k | inc_nlink(dir); |
146 | | |
147 | 16.1k | return ret; |
148 | 16.1k | } |
149 | | |
150 | | static int ramfs_create(struct inode *dir, struct dentry *dentry, umode_t mode) |
151 | 1.67k | { |
152 | 1.67k | return ramfs_mknod(dir, dentry, mode | S_IFREG); |
153 | 1.67k | } |
154 | | |
155 | | static int __ramfs_mknod(struct inode *dir,struct dentry *dentry, umode_t mode, |
156 | | const char *cdevname) |
157 | 15.7k | { |
158 | 15.7k | int ret; |
159 | | |
160 | 15.7k | ret = ramfs_mknod(dir, dentry, mode); |
161 | 15.7k | if (ret) |
162 | 0 | return ret; |
163 | | |
164 | 15.7k | init_special_inode(dentry->d_inode, mode, cdevname); |
165 | | |
166 | 15.7k | return 0; |
167 | 15.7k | } |
168 | | |
169 | | static int ramfs_symlink(struct inode *dir, struct dentry *dentry, |
170 | | const char *symname) |
171 | 7.24k | { |
172 | 7.24k | struct inode *inode; |
173 | | |
174 | 7.24k | inode = ramfs_get_inode(dir->i_sb, dir, S_IFLNK | S_IRWXG); |
175 | 7.24k | if (!inode) |
176 | 0 | return -ENOSPC; |
177 | | |
178 | 7.24k | inode->i_link = xstrdup(symname); |
179 | 7.24k | d_instantiate(dentry, inode); |
180 | | |
181 | 7.24k | return 0; |
182 | 7.24k | } |
183 | | |
184 | | static const char *ramfs_get_link(struct dentry *dentry, struct inode *inode) |
185 | 0 | { |
186 | 0 | return inode->i_link; |
187 | 0 | } |
188 | | |
189 | | static const struct inode_operations ramfs_symlink_inode_operations = |
190 | | { |
191 | | .get_link = ramfs_get_link, |
192 | | }; |
193 | | |
194 | | static int ramfs_tmpfile(struct inode *dir, struct file *file, umode_t mode) |
195 | 7.99k | { |
196 | 7.99k | struct inode *inode; |
197 | | |
198 | 7.99k | inode = ramfs_get_inode(dir->i_sb, dir, mode); |
199 | 7.99k | if (!inode) |
200 | 0 | return -ENOSPC; |
201 | | |
202 | 7.99k | d_tmpfile(file, inode); |
203 | | |
204 | 7.99k | return finish_open_simple(file, 0); |
205 | 7.99k | } |
206 | | |
207 | | static const struct inode_operations ramfs_dir_inode_operations = |
208 | | { |
209 | | .lookup = simple_lookup, |
210 | | .symlink = ramfs_symlink, |
211 | | .mkdir = ramfs_mkdir, |
212 | | .rmdir = simple_rmdir, |
213 | | .unlink = simple_unlink, |
214 | | .create = ramfs_create, |
215 | | .tmpfile = ramfs_tmpfile, |
216 | | .mknod = __ramfs_mknod, |
217 | | }; |
218 | | |
219 | | static struct ramfs_chunk *ramfs_find_chunk(struct ramfs_inode *node, |
220 | | unsigned long pos, int *ofs, int *len) |
221 | 13.7k | { |
222 | 13.7k | struct ramfs_chunk *data, *cur = node->current_chunk; |
223 | | |
224 | 13.7k | if (cur && pos >= cur->ofs) |
225 | 11.3k | data = cur; |
226 | 2.45k | else |
227 | 2.45k | data = list_first_entry(&node->data, struct ramfs_chunk, list); |
228 | | |
229 | 23.8k | list_for_each_entry_from(data, &node->data, list) { |
230 | 23.8k | if (data->ofs + data->size > pos) { |
231 | 13.7k | *ofs = pos - data->ofs; |
232 | 13.7k | *len = data->size - *ofs; |
233 | | |
234 | 13.7k | node->current_chunk = data; |
235 | | |
236 | 13.7k | return data; |
237 | 13.7k | } |
238 | 23.8k | } |
239 | | |
240 | 0 | pr_err("%s: no chunk for pos %ld found\n", __func__, pos); |
241 | |
|
242 | 0 | return NULL; |
243 | 13.7k | } |
244 | | |
245 | | static int ramfs_read(struct file *f, void *buf, size_t insize) |
246 | 1.24k | { |
247 | 1.24k | struct inode *inode = f->f_inode; |
248 | 1.24k | struct ramfs_inode *node = to_ramfs_inode(inode); |
249 | 1.24k | struct ramfs_chunk *data; |
250 | 1.24k | int ofs, len, now; |
251 | 1.24k | unsigned long pos = f->f_pos; |
252 | 1.24k | int size = insize; |
253 | | |
254 | 1.24k | pr_vdebug("%s: %p %zu @ %lld\n", __func__, node, insize, f->f_pos); |
255 | | |
256 | 4.84k | while (size) { |
257 | 3.59k | data = ramfs_find_chunk(node, pos, &ofs, &len); |
258 | 3.59k | if (!data) |
259 | 0 | return -EINVAL; |
260 | | |
261 | 3.59k | pr_vdebug("%s: pos: %lu ofs: %d len: %d\n", __func__, pos, ofs, len); |
262 | | |
263 | 3.59k | now = min(size, len); |
264 | | |
265 | 3.59k | memcpy(buf, data->data + ofs, now); |
266 | | |
267 | 3.59k | size -= now; |
268 | 3.59k | buf += now; |
269 | 3.59k | pos += now; |
270 | 3.59k | } |
271 | | |
272 | 1.24k | return insize; |
273 | 1.24k | } |
274 | | |
275 | | static int ramfs_write(struct file *f, const void *buf, size_t insize) |
276 | 10.1k | { |
277 | 10.1k | struct inode *inode = f->f_inode; |
278 | 10.1k | struct ramfs_inode *node = to_ramfs_inode(inode); |
279 | 10.1k | struct ramfs_chunk *data; |
280 | 10.1k | int ofs, len, now; |
281 | 10.1k | unsigned long pos = f->f_pos; |
282 | 10.1k | int size = insize; |
283 | | |
284 | 10.1k | pr_vdebug("%s: %p %zu @ %lld\n", __func__, node, insize, f->f_pos); |
285 | | |
286 | 20.3k | while (size) { |
287 | 10.1k | data = ramfs_find_chunk(node, pos, &ofs, &len); |
288 | 10.1k | if (!data) |
289 | 0 | return -EINVAL; |
290 | | |
291 | 10.1k | pr_vdebug("%s: pos: %lu ofs: %d len: %d\n", __func__, pos, ofs, len); |
292 | | |
293 | 10.1k | now = min(size, len); |
294 | | |
295 | 10.1k | memcpy(data->data + ofs, buf, now); |
296 | | |
297 | 10.1k | size -= now; |
298 | 10.1k | buf += now; |
299 | 10.1k | pos += now; |
300 | 10.1k | } |
301 | | |
302 | 10.1k | return insize; |
303 | 10.1k | } |
304 | | |
305 | | static void ramfs_truncate_down(struct ramfs_inode *node, unsigned long size) |
306 | 46.7k | { |
307 | 46.7k | struct ramfs_chunk *data, *tmp; |
308 | | |
309 | 46.7k | list_for_each_entry_safe(data, tmp, &node->data, list) { |
310 | 9.12k | if (data->ofs >= size) { |
311 | 9.12k | list_del(&data->list); |
312 | 9.12k | node->alloc_size -= data->size; |
313 | 9.12k | ramfs_put_chunk(data); |
314 | 9.12k | } |
315 | 9.12k | } |
316 | | |
317 | 46.7k | node->current_chunk = NULL; |
318 | 46.7k | } |
319 | | |
320 | | static int ramfs_truncate_up(struct ramfs_inode *node, unsigned long size) |
321 | 10.1k | { |
322 | 10.1k | struct ramfs_chunk *data, *tmp; |
323 | 10.1k | LIST_HEAD(list); |
324 | 10.1k | unsigned long add = size - node->alloc_size; |
325 | 10.1k | unsigned long chunksize = add; |
326 | 10.1k | unsigned long alloc_size = 0; |
327 | | |
328 | 10.1k | if (node->alloc_size >= size) |
329 | 0 | return 0; |
330 | | |
331 | | /* |
332 | | * We first try to allocate all space we need in a single chunk. |
333 | | * This may fail because of fragmented memory, so in case we cannot |
334 | | * allocate memory we successively decrease the chunk size until |
335 | | * we have enough allocations made. |
336 | | */ |
337 | 10.1k | while (1) { |
338 | 10.1k | unsigned long now = min(chunksize, add); |
339 | | |
340 | 10.1k | data = ramfs_get_chunk(now); |
341 | 10.1k | if (!data) { |
342 | | /* No luck, try with smaller chunk size */ |
343 | 0 | chunksize >>= 1; |
344 | | |
345 | | /* If we do not have even 128KiB then go out */ |
346 | 0 | if (chunksize < SZ_128K) |
347 | 0 | goto out; |
348 | | |
349 | 0 | continue; |
350 | 0 | } |
351 | | |
352 | 10.1k | data->ofs = node->alloc_size + alloc_size; |
353 | | |
354 | 10.1k | alloc_size += data->size; |
355 | | |
356 | 10.1k | list_add_tail(&data->list, &list); |
357 | | |
358 | 10.1k | if (add <= data->size) |
359 | 10.1k | break; |
360 | | |
361 | 0 | add -= data->size; |
362 | 0 | } |
363 | | |
364 | 10.1k | list_splice_tail(&list, &node->data); |
365 | | |
366 | 10.1k | node->alloc_size += alloc_size; |
367 | | |
368 | 10.1k | return 0; |
369 | | |
370 | 0 | out: |
371 | 0 | list_for_each_entry_safe(data, tmp, &list, list) { |
372 | 0 | list_del(&data->list); |
373 | 0 | ramfs_put_chunk(data); |
374 | 0 | } |
375 | |
|
376 | 0 | return -ENOSPC; |
377 | 10.1k | } |
378 | | |
379 | | static int ramfs_truncate(struct file *f, loff_t size) |
380 | 11.8k | { |
381 | 11.8k | struct inode *inode = f->f_inode; |
382 | 11.8k | struct ramfs_inode *node = to_ramfs_inode(inode); |
383 | 11.8k | int ret; |
384 | | |
385 | | /* |
386 | | * This is a malloc based filesystem, no need to support more |
387 | | * memory than fits into unsigned long. |
388 | | */ |
389 | 11.8k | if (size > ULONG_MAX) |
390 | 0 | return -ENOSPC; |
391 | | |
392 | 11.8k | pr_vdebug("%s: %p cur: %ld new: %lld alloc: %ld\n", __func__, node, |
393 | 11.8k | node->size, size, node->alloc_size); |
394 | | |
395 | 11.8k | if (size == node->size) |
396 | 1.67k | return 0; |
397 | | |
398 | 10.2k | if (size < node->size) { |
399 | 52 | ramfs_truncate_down(node, size); |
400 | 10.1k | } else { |
401 | 10.1k | ret = ramfs_truncate_up(node, size); |
402 | 10.1k | if (ret) |
403 | 0 | return ret; |
404 | 10.1k | } |
405 | | |
406 | 10.2k | node->size = size; |
407 | | |
408 | 10.2k | return 0; |
409 | 10.2k | } |
410 | | |
411 | | static int ramfs_memmap(struct file *f, void **map, int flags) |
412 | 0 | { |
413 | 0 | struct inode *inode = f->f_inode; |
414 | 0 | struct ramfs_inode *node = to_ramfs_inode(inode); |
415 | 0 | struct ramfs_chunk *data; |
416 | |
|
417 | 0 | if (list_empty(&node->data)) |
418 | 0 | return -EINVAL; |
419 | | |
420 | 0 | if (!list_is_singular(&node->data)) |
421 | 0 | return -EINVAL; |
422 | | |
423 | 0 | data = list_first_entry(&node->data, struct ramfs_chunk, list); |
424 | |
|
425 | 0 | *map = data->data; |
426 | |
|
427 | 0 | return 0; |
428 | 0 | } |
429 | | |
430 | | static const struct file_operations ramfs_file_operations = { |
431 | | .read = ramfs_read, |
432 | | .write = ramfs_write, |
433 | | .memmap = ramfs_memmap, |
434 | | .truncate = ramfs_truncate, |
435 | | }; |
436 | | |
437 | | static struct inode *ramfs_alloc_inode(struct super_block *sb) |
438 | 48.8k | { |
439 | 48.8k | struct ramfs_inode *node; |
440 | | |
441 | 48.8k | node = xzalloc(sizeof(*node)); |
442 | | |
443 | 48.8k | INIT_LIST_HEAD(&node->data); |
444 | | |
445 | 48.8k | return &node->inode; |
446 | 48.8k | } |
447 | | |
448 | | static void ramfs_destroy_inode(struct inode *inode) |
449 | 46.7k | { |
450 | 46.7k | struct ramfs_inode *node = to_ramfs_inode(inode); |
451 | | |
452 | 46.7k | ramfs_truncate_down(node, 0); |
453 | | |
454 | 46.7k | free(node); |
455 | 46.7k | } |
456 | | |
457 | | static const struct super_operations ramfs_ops = { |
458 | | .alloc_inode = ramfs_alloc_inode, |
459 | | .destroy_inode = ramfs_destroy_inode, |
460 | | }; |
461 | | |
462 | | static int ramfs_probe(struct device *dev) |
463 | 26 | { |
464 | 26 | struct inode *inode; |
465 | 26 | struct fs_device *fsdev = dev_to_fs_device(dev); |
466 | 26 | struct super_block *sb = &fsdev->sb; |
467 | | |
468 | 26 | sb->s_op = &ramfs_ops; |
469 | | |
470 | 26 | inode = ramfs_get_inode(sb, NULL, S_IFDIR); |
471 | 26 | sb->s_root = d_make_root(inode); |
472 | | |
473 | 26 | return 0; |
474 | 26 | } |
475 | | |
476 | | static struct fs_driver ramfs_driver = { |
477 | | .drv = { |
478 | | .probe = ramfs_probe, |
479 | | .name = "ramfs", |
480 | | } |
481 | | }; |
482 | | |
483 | | static int ramfs_init(void) |
484 | 26 | { |
485 | 26 | return register_fs_driver(&ramfs_driver); |
486 | 26 | } |
487 | | |
488 | | coredevice_initcall(ramfs_init); |