source: mainline/uspace/srv/fs/tmpfs/tmpfs_ops.c@ 7b6d98b

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 7b6d98b was 7b6d98b, checked in by Jakub Jermar <jakub@…>, 17 years ago

No need to keep the parent pointer in the TMPFS node. Moreover, other file
systems won't have it either. Finally, if TMPFS is to support hardlinks, there
can be multiple parents.

  • Property mode set to 100644
File size: 11.7 KB
Line 
1/*
2 * Copyright (c) 2008 Jakub Jermar
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup fs
30 * @{
31 */
32
33/**
34 * @file tmpfs_ops.c
35 * @brief Implementation of VFS operations for the TMPFS file system
36 * server.
37 */
38
39#include "tmpfs.h"
40#include "../../vfs/vfs.h"
41#include <ipc/ipc.h>
42#include <async.h>
43#include <errno.h>
44#include <atomic.h>
45#include <stdlib.h>
46#include <string.h>
47#include <stdio.h>
48#include <assert.h>
49#include <sys/types.h>
50#include <libadt/hash_table.h>
51#include <as.h>
52#include <libfs.h>
53
54#define min(a, b) ((a) < (b) ? (a) : (b))
55#define max(a, b) ((a) > (b) ? (a) : (b))
56
57#define DENTRIES_BUCKETS 256
58
59/*
60 * For now, we don't distinguish between different dev_handles/instances. All
61 * requests resolve to the only instance, rooted in the following variable.
62 */
63static tmpfs_dentry_t *root;
64
65/*
66 * Implementation of the libfs interface.
67 */
68
69/* Forward declarations of static functions. */
70static bool tmpfs_match(void *, const char *);
71static void *tmpfs_create_node(int);
72static bool tmpfs_link_node(void *, void *, const char *);
73static int tmpfs_unlink_node(void *, void *);
74static void tmpfs_destroy_node(void *);
75
76/* Implementation of helper functions. */
77static unsigned long tmpfs_index_get(void *nodep)
78{
79 return ((tmpfs_dentry_t *) nodep)->index;
80}
81
82static unsigned long tmpfs_size_get(void *nodep)
83{
84 return ((tmpfs_dentry_t *) nodep)->size;
85}
86
87static unsigned tmpfs_lnkcnt_get(void *nodep)
88{
89 return ((tmpfs_dentry_t *) nodep)->lnkcnt;
90}
91
92static void *tmpfs_child_get(void *nodep)
93{
94 return ((tmpfs_dentry_t *) nodep)->child;
95}
96
97static void *tmpfs_sibling_get(void *nodep)
98{
99 return ((tmpfs_dentry_t *) nodep)->sibling;
100}
101
102static void *tmpfs_root_get(void)
103{
104 return root;
105}
106
107static char tmpfs_plb_get_char(unsigned pos)
108{
109 return tmpfs_reg.plb_ro[pos % PLB_SIZE];
110}
111
112static bool tmpfs_is_directory(void *nodep)
113{
114 return ((tmpfs_dentry_t *) nodep)->type == TMPFS_DIRECTORY;
115}
116
117static bool tmpfs_is_file(void *nodep)
118{
119 return ((tmpfs_dentry_t *) nodep)->type == TMPFS_FILE;
120}
121
122/** libfs operations */
123libfs_ops_t tmpfs_libfs_ops = {
124 .match = tmpfs_match,
125 .create = tmpfs_create_node,
126 .destroy = tmpfs_destroy_node,
127 .link = tmpfs_link_node,
128 .unlink = tmpfs_unlink_node,
129 .index_get = tmpfs_index_get,
130 .size_get = tmpfs_size_get,
131 .lnkcnt_get = tmpfs_lnkcnt_get,
132 .child_get = tmpfs_child_get,
133 .sibling_get = tmpfs_sibling_get,
134 .root_get = tmpfs_root_get,
135 .plb_get_char = tmpfs_plb_get_char,
136 .is_directory = tmpfs_is_directory,
137 .is_file = tmpfs_is_file
138};
139
140/** Hash table of all directory entries. */
141hash_table_t dentries;
142
143/* Implementation of hash table interface. */
144static hash_index_t dentries_hash(unsigned long *key)
145{
146 return *key % DENTRIES_BUCKETS;
147}
148
149static int dentries_compare(unsigned long *key, hash_count_t keys,
150 link_t *item)
151{
152 tmpfs_dentry_t *dentry = hash_table_get_instance(item, tmpfs_dentry_t,
153 dh_link);
154 return dentry->index == *key;
155}
156
157static void dentries_remove_callback(link_t *item)
158{
159}
160
161/** TMPFS dentries hash table operations. */
162hash_table_operations_t dentries_ops = {
163 .hash = dentries_hash,
164 .compare = dentries_compare,
165 .remove_callback = dentries_remove_callback
166};
167
168unsigned tmpfs_next_index = 1;
169
170static void tmpfs_dentry_initialize(tmpfs_dentry_t *dentry)
171{
172 dentry->index = 0;
173 dentry->sibling = NULL;
174 dentry->child = NULL;
175 dentry->name = NULL;
176 dentry->type = TMPFS_NONE;
177 dentry->lnkcnt = 0;
178 dentry->size = 0;
179 dentry->data = NULL;
180 link_initialize(&dentry->dh_link);
181}
182
183static bool tmpfs_init(void)
184{
185 if (!hash_table_create(&dentries, DENTRIES_BUCKETS, 1, &dentries_ops))
186 return false;
187 root = (tmpfs_dentry_t *) tmpfs_create_node(L_DIRECTORY);
188 root->lnkcnt = 1;
189 return root != NULL;
190}
191
192/** Compare one component of path to a directory entry.
193 *
194 * @param nodep Node to compare the path component with.
195 * @param component Array of characters holding component name.
196 *
197 * @return True on match, false otherwise.
198 */
199bool tmpfs_match(void *nodep, const char *component)
200{
201 tmpfs_dentry_t *dentry = (tmpfs_dentry_t *) nodep;
202
203 return !strcmp(dentry->name, component);
204}
205
206void *tmpfs_create_node(int lflag)
207{
208 assert((lflag & L_FILE) ^ (lflag & L_DIRECTORY));
209
210 tmpfs_dentry_t *node = malloc(sizeof(tmpfs_dentry_t));
211 if (!node)
212 return NULL;
213
214 tmpfs_dentry_initialize(node);
215 node->index = tmpfs_next_index++;
216 if (lflag & L_DIRECTORY)
217 node->type = TMPFS_DIRECTORY;
218 else
219 node->type = TMPFS_FILE;
220
221 /* Insert the new node into the dentry hash table. */
222 hash_table_insert(&dentries, &node->index, &node->dh_link);
223 return (void *) node;
224}
225
226bool tmpfs_link_node(void *prnt, void *chld, const char *nm)
227{
228 tmpfs_dentry_t *parentp = (tmpfs_dentry_t *) prnt;
229 tmpfs_dentry_t *childp = (tmpfs_dentry_t *) chld;
230
231 assert(parentp->type == TMPFS_DIRECTORY);
232
233 size_t len = strlen(nm);
234 char *name = malloc(len + 1);
235 if (!name)
236 return false;
237
238 childp->lnkcnt++;
239
240 strcpy(name, nm);
241 childp->name = name;
242
243 /* Insert the new node into the namespace. */
244 if (parentp->child) {
245 tmpfs_dentry_t *tmp = parentp->child;
246 while (tmp->sibling)
247 tmp = tmp->sibling;
248 tmp->sibling = childp;
249 } else {
250 parentp->child = childp;
251 }
252
253 return true;
254}
255
256int tmpfs_unlink_node(void *prnt, void *chld)
257{
258 tmpfs_dentry_t *parentp = (tmpfs_dentry_t *)prnt;
259 tmpfs_dentry_t *childp = (tmpfs_dentry_t *)chld;
260
261 if (!parentp)
262 return EBUSY;
263
264 if (childp->child)
265 return ENOTEMPTY;
266
267 if (parentp->child == childp) {
268 parentp->child = childp->sibling;
269 } else {
270 /* TODO: consider doubly linked list for organizing siblings. */
271 tmpfs_dentry_t *tmp = parentp->child;
272 while (tmp->sibling != childp)
273 tmp = tmp->sibling;
274 tmp->sibling = childp->sibling;
275 }
276 childp->sibling = NULL;
277
278 free(childp->name);
279 childp->name = NULL;
280
281 childp->lnkcnt--;
282
283 return EOK;
284}
285
286void tmpfs_destroy_node(void *nodep)
287{
288 tmpfs_dentry_t *dentry = (tmpfs_dentry_t *) nodep;
289
290 assert(!dentry->lnkcnt);
291 assert(!dentry->child);
292 assert(!dentry->sibling);
293
294 unsigned long index = dentry->index;
295 hash_table_remove(&dentries, &index, 1);
296
297 if (dentry->type == TMPFS_FILE)
298 free(dentry->data);
299 free(dentry);
300}
301
302void tmpfs_lookup(ipc_callid_t rid, ipc_call_t *request)
303{
304 /* Initialize TMPFS. */
305 if (!root && !tmpfs_init()) {
306 ipc_answer_0(rid, ENOMEM);
307 return;
308 }
309 libfs_lookup(&tmpfs_libfs_ops, tmpfs_reg.fs_handle, rid, request);
310}
311
312void tmpfs_read(ipc_callid_t rid, ipc_call_t *request)
313{
314 int dev_handle = IPC_GET_ARG1(*request);
315 unsigned long index = IPC_GET_ARG2(*request);
316 off_t pos = IPC_GET_ARG3(*request);
317
318 /*
319 * Lookup the respective dentry.
320 */
321 link_t *hlp;
322 hlp = hash_table_find(&dentries, &index);
323 if (!hlp) {
324 ipc_answer_0(rid, ENOENT);
325 return;
326 }
327 tmpfs_dentry_t *dentry = hash_table_get_instance(hlp, tmpfs_dentry_t,
328 dh_link);
329
330 /*
331 * Receive the read request.
332 */
333 ipc_callid_t callid;
334 size_t len;
335 if (!ipc_data_read_receive(&callid, &len)) {
336 ipc_answer_0(callid, EINVAL);
337 ipc_answer_0(rid, EINVAL);
338 return;
339 }
340
341 size_t bytes;
342 if (dentry->type == TMPFS_FILE) {
343 bytes = max(0, min(dentry->size - pos, len));
344 (void) ipc_data_read_finalize(callid, dentry->data + pos,
345 bytes);
346 } else {
347 int i;
348 tmpfs_dentry_t *cur;
349
350 assert(dentry->type == TMPFS_DIRECTORY);
351
352 /*
353 * Yes, we really use O(n) algorithm here.
354 * If it bothers someone, it could be fixed by introducing a
355 * hash table.
356 */
357 for (i = 0, cur = dentry->child; i < pos && cur; i++,
358 cur = cur->sibling)
359 ;
360
361 if (!cur) {
362 ipc_answer_0(callid, ENOENT);
363 ipc_answer_1(rid, ENOENT, 0);
364 return;
365 }
366
367 (void) ipc_data_read_finalize(callid, cur->name,
368 strlen(cur->name) + 1);
369 bytes = 1;
370 }
371
372 /*
373 * Answer the VFS_READ call.
374 */
375 ipc_answer_1(rid, EOK, bytes);
376}
377
378void tmpfs_write(ipc_callid_t rid, ipc_call_t *request)
379{
380 int dev_handle = IPC_GET_ARG1(*request);
381 unsigned long index = IPC_GET_ARG2(*request);
382 off_t pos = IPC_GET_ARG3(*request);
383
384 /*
385 * Lookup the respective dentry.
386 */
387 link_t *hlp;
388 hlp = hash_table_find(&dentries, &index);
389 if (!hlp) {
390 ipc_answer_0(rid, ENOENT);
391 return;
392 }
393 tmpfs_dentry_t *dentry = hash_table_get_instance(hlp, tmpfs_dentry_t,
394 dh_link);
395
396 /*
397 * Receive the write request.
398 */
399 ipc_callid_t callid;
400 size_t len;
401 if (!ipc_data_write_receive(&callid, &len)) {
402 ipc_answer_0(callid, EINVAL);
403 ipc_answer_0(rid, EINVAL);
404 return;
405 }
406
407 /*
408 * Check whether the file needs to grow.
409 */
410 if (pos + len <= dentry->size) {
411 /* The file size is not changing. */
412 (void) ipc_data_write_finalize(callid, dentry->data + pos, len);
413 ipc_answer_2(rid, EOK, len, dentry->size);
414 return;
415 }
416 size_t delta = (pos + len) - dentry->size;
417 /*
418 * At this point, we are deliberately extremely straightforward and
419 * simply realloc the contents of the file on every write that grows the
420 * file. In the end, the situation might not be as bad as it may look:
421 * our heap allocator can save us and just grow the block whenever
422 * possible.
423 */
424 void *newdata = realloc(dentry->data, dentry->size + delta);
425 if (!newdata) {
426 ipc_answer_0(callid, ENOMEM);
427 ipc_answer_2(rid, EOK, 0, dentry->size);
428 return;
429 }
430 /* Clear any newly allocated memory in order to emulate gaps. */
431 memset(newdata + dentry->size, 0, delta);
432 dentry->size += delta;
433 dentry->data = newdata;
434 (void) ipc_data_write_finalize(callid, dentry->data + pos, len);
435 ipc_answer_2(rid, EOK, len, dentry->size);
436}
437
438void tmpfs_truncate(ipc_callid_t rid, ipc_call_t *request)
439{
440 int dev_handle = IPC_GET_ARG1(*request);
441 unsigned long index = IPC_GET_ARG2(*request);
442 size_t size = IPC_GET_ARG3(*request);
443
444 /*
445 * Lookup the respective dentry.
446 */
447 link_t *hlp;
448 hlp = hash_table_find(&dentries, &index);
449 if (!hlp) {
450 ipc_answer_0(rid, ENOENT);
451 return;
452 }
453 tmpfs_dentry_t *dentry = hash_table_get_instance(hlp, tmpfs_dentry_t,
454 dh_link);
455
456 if (size == dentry->size) {
457 ipc_answer_0(rid, EOK);
458 return;
459 }
460
461 void *newdata = realloc(dentry->data, size);
462 if (!newdata) {
463 ipc_answer_0(rid, ENOMEM);
464 return;
465 }
466 if (size > dentry->size) {
467 size_t delta = size - dentry->size;
468 memset(newdata + dentry->size, 0, delta);
469 }
470 dentry->size = size;
471 dentry->data = newdata;
472 ipc_answer_0(rid, EOK);
473}
474
475void tmpfs_destroy(ipc_callid_t rid, ipc_call_t *request)
476{
477 int dev_handle = IPC_GET_ARG1(*request);
478 unsigned long index = IPC_GET_ARG2(*request);
479
480 link_t *hlp;
481 hlp = hash_table_find(&dentries, &index);
482 if (!hlp) {
483 ipc_answer_0(rid, ENOENT);
484 return;
485 }
486 tmpfs_dentry_t *dentry = hash_table_get_instance(hlp, tmpfs_dentry_t,
487 dh_link);
488 tmpfs_destroy_node(dentry);
489 ipc_answer_0(rid, EOK);
490}
491
492/**
493 * @}
494 */
Note: See TracBrowser for help on using the repository browser.