source: mainline/uspace/srv/fs/tmpfs/tmpfs_ops.c@ 1fe186f

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

Move handling of the miss on excessive components out of the main loop in
tmpfs_lookup.

  • Property mode set to 100644
File size: 12.6 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 <dirent.h>
49#include <assert.h>
50#include <sys/types.h>
51#include <libadt/hash_table.h>
52#include <as.h>
53
54#define min(a, b) ((a) < (b) ? (a) : (b))
55#define max(a, b) ((a) > (b) ? (a) : (b))
56
57#define PLB_GET_CHAR(i) (tmpfs_reg.plb_ro[(i) % PLB_SIZE])
58
59#define DENTRIES_BUCKETS 256
60
61/*
62 * Hash table of all directory entries.
63 */
64hash_table_t dentries;
65
66static hash_index_t dentries_hash(unsigned long *key)
67{
68 return *key % DENTRIES_BUCKETS;
69}
70
71static int dentries_compare(unsigned long *key, hash_count_t keys,
72 link_t *item)
73{
74 tmpfs_dentry_t *dentry = hash_table_get_instance(item, tmpfs_dentry_t,
75 dh_link);
76 return dentry->index == *key;
77}
78
79static void dentries_remove_callback(link_t *item)
80{
81}
82
83/** TMPFS dentries hash table operations. */
84hash_table_operations_t dentries_ops = {
85 .hash = dentries_hash,
86 .compare = dentries_compare,
87 .remove_callback = dentries_remove_callback
88};
89
90unsigned tmpfs_next_index = 1;
91
92static void tmpfs_dentry_initialize(tmpfs_dentry_t *dentry)
93{
94 dentry->index = 0;
95 dentry->parent = NULL;
96 dentry->sibling = NULL;
97 dentry->child = NULL;
98 dentry->name = NULL;
99 dentry->type = TMPFS_NONE;
100 dentry->size = 0;
101 dentry->data = NULL;
102 link_initialize(&dentry->dh_link);
103}
104
105/*
106 * For now, we don't distinguish between different dev_handles/instances. All
107 * requests resolve to the only instance, rooted in the following variable.
108 */
109static tmpfs_dentry_t *root;
110
111static bool tmpfs_init(void)
112{
113 if (!hash_table_create(&dentries, DENTRIES_BUCKETS, 1, &dentries_ops))
114 return false;
115
116 root = (tmpfs_dentry_t *) malloc(sizeof(tmpfs_dentry_t));
117 if (!root)
118 return false;
119 tmpfs_dentry_initialize(root);
120 root->index = tmpfs_next_index++;
121 root->name = "";
122 root->type = TMPFS_DIRECTORY;
123 hash_table_insert(&dentries, &root->index, &root->dh_link);
124
125 /*
126 * This is only for debugging. Once we can create files and directories
127 * using VFS, we can get rid of this.
128 */
129 tmpfs_dentry_t *d;
130 d = (tmpfs_dentry_t *) malloc(sizeof(tmpfs_dentry_t));
131 if (!d) {
132 free(root);
133 root = NULL;
134 return false;
135 }
136 tmpfs_dentry_initialize(d);
137 d->index = tmpfs_next_index++;
138 root->child = d;
139 d->parent = root;
140 d->type = TMPFS_DIRECTORY;
141 d->name = "dir1";
142 hash_table_insert(&dentries, &d->index, &d->dh_link);
143
144 d = (tmpfs_dentry_t *) malloc(sizeof(tmpfs_dentry_t));
145 if (!d) {
146 free(root->child);
147 free(root);
148 root = NULL;
149 return false;
150 }
151 tmpfs_dentry_initialize(d);
152 d->index = tmpfs_next_index++;
153 root->child->sibling = d;
154 d->parent = root;
155 d->type = TMPFS_DIRECTORY;
156 d->name = "dir2";
157 hash_table_insert(&dentries, &d->index, &d->dh_link);
158
159 d = (tmpfs_dentry_t *) malloc(sizeof(tmpfs_dentry_t));
160 if (!d) {
161 free(root->child->sibling);
162 free(root->child);
163 free(root);
164 root = NULL;
165 return false;
166 }
167 tmpfs_dentry_initialize(d);
168 d->index = tmpfs_next_index++;
169 root->child->child = d;
170 d->parent = root->child;
171 d->type = TMPFS_FILE;
172 d->name = "file1";
173 d->data = "This is the contents of /dir1/file1.\n";
174 d->size = strlen(d->data);
175 hash_table_insert(&dentries, &d->index, &d->dh_link);
176
177 d = (tmpfs_dentry_t *) malloc(sizeof(tmpfs_dentry_t));
178 if (!d) {
179 free(root->child->sibling);
180 free(root->child->child);
181 free(root->child);
182 free(root);
183 root = NULL;
184 return false;
185 }
186 tmpfs_dentry_initialize(d);
187 d->index = tmpfs_next_index++;
188 root->child->sibling->child = d;
189 d->parent = root->child->sibling;
190 d->type = TMPFS_FILE;
191 d->name = "file2";
192 d->data = "This is the contents of /dir2/file2.\n";
193 d->size = strlen(d->data);
194 hash_table_insert(&dentries, &d->index, &d->dh_link);
195
196 return true;
197}
198
199/** Compare one component of path to a directory entry.
200 *
201 * @param dentry Directory entry to compare the path component with.
202 * @param component Array of characters holding component name.
203 *
204 * @return True on match, false otherwise.
205 */
206static bool match_component(tmpfs_dentry_t *dentry, const char *component)
207{
208 return !strcmp(dentry->name, component);
209}
210
211static unsigned long create_node(tmpfs_dentry_t *dentry,
212 const char *component, int lflag)
213{
214 return 0;
215}
216
217static int destroy_component(tmpfs_dentry_t *dentry)
218{
219 return EPERM;
220}
221
222void tmpfs_lookup(ipc_callid_t rid, ipc_call_t *request)
223{
224 unsigned next = IPC_GET_ARG1(*request);
225 unsigned last = IPC_GET_ARG2(*request);
226 int dev_handle = IPC_GET_ARG3(*request);
227 int lflag = IPC_GET_ARG4(*request);
228
229 if (last < next)
230 last += PLB_SIZE;
231
232 /*
233 * Initialize TMPFS.
234 */
235 if (!root && !tmpfs_init()) {
236 ipc_answer_0(rid, ENOMEM);
237 return;
238 }
239
240 tmpfs_dentry_t *dtmp = root->child;
241 tmpfs_dentry_t *dcur = root;
242
243 if (PLB_GET_CHAR(next) == '/')
244 next++; /* eat slash */
245
246 char component[NAME_MAX + 1];
247 int len = 0;
248 while (dtmp && next <= last) {
249
250 /* collect the component */
251 if (PLB_GET_CHAR(next) != '/') {
252 if (len + 1 == NAME_MAX) {
253 /* comopnent length overflow */
254 ipc_answer_0(rid, ENAMETOOLONG);
255 return;
256 }
257 component[len++] = PLB_GET_CHAR(next);
258 next++; /* process next character */
259 if (next <= last)
260 continue;
261 }
262
263 assert(len);
264 component[len] = '\0';
265 next++; /* eat slash */
266 len = 0;
267
268 /* match the component */
269 while (dtmp && !match_component(dtmp, component))
270 dtmp = dtmp->sibling;
271
272 /* handle miss: match amongst siblings */
273 if (!dtmp) {
274 if ((next > last) && (lflag & L_CREATE)) {
275 /* no components left and L_CREATE specified */
276 if (dcur->type != TMPFS_DIRECTORY) {
277 ipc_answer_0(rid, ENOTDIR);
278 return;
279 }
280 unsigned long index = create_node(dcur,
281 component, lflag);
282 if (index) {
283 ipc_answer_4(rid, EOK,
284 tmpfs_reg.fs_handle, dev_handle,
285 index, 0);
286 } else {
287 ipc_answer_0(rid, ENOSPC);
288 }
289 return;
290 }
291 ipc_answer_0(rid, ENOENT);
292 return;
293 }
294
295 /* descent one level */
296 dcur = dtmp;
297 dtmp = dtmp->child;
298 }
299
300 /* handle miss: excessive components */
301 if (!dtmp && next <= last) {
302 if (lflag & L_CREATE) {
303 if (dcur->type != TMPFS_DIRECTORY) {
304 ipc_answer_0(rid, ENOTDIR);
305 return;
306 }
307
308 /* collect next component */
309 while (next <= last) {
310 if (PLB_GET_CHAR(next) == '/') {
311 /* more than one component */
312 ipc_answer_0(rid, ENOENT);
313 return;
314 }
315 if (len + 1 == NAME_MAX) {
316 /* component length overflow */
317 ipc_answer_0(rid, ENAMETOOLONG);
318 return;
319 }
320 component[len++] = PLB_GET_CHAR(next);
321 next++; /* process next character */
322 }
323 assert(len);
324 component[len] = '\0';
325 len = 0;
326
327 unsigned long index;
328 index = create_node(dcur, component, lflag);
329 if (index) {
330 ipc_answer_4(rid, EOK, tmpfs_reg.fs_handle,
331 dev_handle, index, 0);
332 } else {
333 ipc_answer_0(rid, ENOSPC);
334 }
335 return;
336 }
337 ipc_answer_0(rid, ENOENT);
338 return;
339 }
340
341 /* handle hit */
342 if (lflag & L_DESTROY) {
343 int res = destroy_component(dcur);
344 ipc_answer_0(rid, res);
345 return;
346 }
347 if ((lflag & (L_CREATE | L_EXCLUSIVE)) == (L_CREATE | L_EXCLUSIVE)) {
348 ipc_answer_0(rid, EEXIST);
349 return;
350 }
351 if ((lflag & L_FILE) && (dcur->type != TMPFS_FILE)) {
352 ipc_answer_0(rid, EISDIR);
353 return;
354 }
355 if ((lflag & L_DIRECTORY) && (dcur->type != TMPFS_DIRECTORY)) {
356 ipc_answer_0(rid, ENOTDIR);
357 return;
358 }
359
360 ipc_answer_4(rid, EOK, tmpfs_reg.fs_handle, dev_handle, dcur->index,
361 dcur->size);
362}
363
364void tmpfs_read(ipc_callid_t rid, ipc_call_t *request)
365{
366 int dev_handle = IPC_GET_ARG1(*request);
367 unsigned long index = IPC_GET_ARG2(*request);
368 off_t pos = IPC_GET_ARG3(*request);
369
370 /*
371 * Lookup the respective dentry.
372 */
373 link_t *hlp;
374 hlp = hash_table_find(&dentries, &index);
375 if (!hlp) {
376 ipc_answer_0(rid, ENOENT);
377 return;
378 }
379 tmpfs_dentry_t *dentry = hash_table_get_instance(hlp, tmpfs_dentry_t,
380 dh_link);
381
382 /*
383 * Receive the read request.
384 */
385 ipc_callid_t callid;
386 size_t len;
387 if (!ipc_data_read_receive(&callid, &len)) {
388 ipc_answer_0(callid, EINVAL);
389 ipc_answer_0(rid, EINVAL);
390 return;
391 }
392
393 size_t bytes;
394 if (dentry->type == TMPFS_FILE) {
395 bytes = max(0, min(dentry->size - pos, len));
396 (void) ipc_data_read_finalize(callid, dentry->data + pos,
397 bytes);
398 } else {
399 int i;
400 tmpfs_dentry_t *cur = dentry->child;
401
402 assert(dentry->type == TMPFS_DIRECTORY);
403
404 /*
405 * Yes, we really use O(n) algorithm here.
406 * If it bothers someone, it could be fixed by introducing a
407 * hash table.
408 */
409 for (i = 0, cur = dentry->child; i < pos && cur; i++,
410 cur = cur->sibling)
411 ;
412
413 if (!cur) {
414 ipc_answer_0(callid, ENOENT);
415 ipc_answer_1(rid, ENOENT, 0);
416 return;
417 }
418
419 (void) ipc_data_read_finalize(callid, cur->name,
420 strlen(cur->name) + 1);
421 bytes = 1;
422 }
423
424 /*
425 * Answer the VFS_READ call.
426 */
427 ipc_answer_1(rid, EOK, bytes);
428}
429
430void tmpfs_write(ipc_callid_t rid, ipc_call_t *request)
431{
432 int dev_handle = IPC_GET_ARG1(*request);
433 unsigned long index = IPC_GET_ARG2(*request);
434 off_t pos = IPC_GET_ARG3(*request);
435
436 /*
437 * Lookup the respective dentry.
438 */
439 link_t *hlp;
440 hlp = hash_table_find(&dentries, &index);
441 if (!hlp) {
442 ipc_answer_0(rid, ENOENT);
443 return;
444 }
445 tmpfs_dentry_t *dentry = hash_table_get_instance(hlp, tmpfs_dentry_t,
446 dh_link);
447
448 /*
449 * Receive the write request.
450 */
451 ipc_callid_t callid;
452 size_t len;
453 if (!ipc_data_write_receive(&callid, &len)) {
454 ipc_answer_0(callid, EINVAL);
455 ipc_answer_0(rid, EINVAL);
456 return;
457 }
458
459 /*
460 * Check whether the file needs to grow.
461 */
462 if (pos + len <= dentry->size) {
463 /* The file size is not changing. */
464 (void) ipc_data_write_finalize(callid, dentry->data + pos, len);
465 ipc_answer_1(rid, EOK, len);
466 return;
467 }
468 size_t delta = (pos + len) - dentry->size;
469 /*
470 * At this point, we are deliberately extremely straightforward and
471 * simply realloc the contents of the file on every write that grows the
472 * file. In the end, the situation might not be as bad as it may look:
473 * our heap allocator can save us and just grow the block whenever
474 * possible.
475 */
476 void *newdata = realloc(dentry->data, dentry->size + delta);
477 if (!newdata) {
478 ipc_answer_0(callid, ENOMEM);
479 ipc_answer_1(rid, EOK, 0);
480 return;
481 }
482 /* Clear any newly allocated memory in order to emulate gaps. */
483 memset(newdata + dentry->size, 0, delta);
484 dentry->size += delta;
485 dentry->data = newdata;
486 (void) ipc_data_write_finalize(callid, dentry->data + pos, len);
487 ipc_answer_2(rid, EOK, len, dentry->size);
488}
489
490void tmpfs_truncate(ipc_callid_t rid, ipc_call_t *request)
491{
492 int dev_handle = IPC_GET_ARG1(*request);
493 unsigned long index = IPC_GET_ARG2(*request);
494 size_t size = IPC_GET_ARG3(*request);
495
496 /*
497 * Lookup the respective dentry.
498 */
499 link_t *hlp;
500 hlp = hash_table_find(&dentries, &index);
501 if (!hlp) {
502 ipc_answer_0(rid, ENOENT);
503 return;
504 }
505 tmpfs_dentry_t *dentry = hash_table_get_instance(hlp, tmpfs_dentry_t,
506 dh_link);
507
508 if (size == dentry->size) {
509 ipc_answer_0(rid, EOK);
510 return;
511 }
512
513 void *newdata = realloc(dentry->data, size);
514 if (!newdata) {
515 ipc_answer_0(rid, ENOMEM);
516 return;
517 }
518 if (size > dentry->size) {
519 size_t delta = size - dentry->size;
520 memset(newdata + dentry->size, 0, delta);
521 }
522 dentry->size = size;
523 dentry->data = newdata;
524 ipc_answer_0(rid, EOK);
525}
526
527/**
528 * @}
529 */
Note: See TracBrowser for help on using the repository browser.