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

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

More elegant, flexible and complete version of 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 (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 /* handle miss: excessive components */
300 if (!dtmp && next <= last) {
301 if (lflag & L_CREATE) {
302 if (dcur->type != TMPFS_DIRECTORY) {
303 ipc_answer_0(rid, ENOTDIR);
304 return;
305 }
306
307 /* collect next component */
308 while (next <= last) {
309 if (PLB_GET_CHAR(next) == '/') {
310 /* more than one component */
311 ipc_answer_0(rid, ENOENT);
312 return;
313 }
314 if (len + 1 == NAME_MAX) {
315 /* component length overflow */
316 ipc_answer_0(rid, ENAMETOOLONG);
317 return;
318 }
319 component[len++] = PLB_GET_CHAR(next);
320 next++; /* process next character */
321 }
322 assert(len);
323 component[len] = '\0';
324 len = 0;
325
326 unsigned long index;
327 index = create_node(dcur, component, lflag);
328 if (index) {
329 ipc_answer_4(rid, EOK,
330 tmpfs_reg.fs_handle, dev_handle,
331 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 }
342
343 /* handle hit */
344 if (lflag & L_DESTROY) {
345 int res = destroy_component(dcur);
346 ipc_answer_0(rid, res);
347 return;
348 }
349 if ((lflag & (L_CREATE | L_EXCLUSIVE)) == (L_CREATE | L_EXCLUSIVE)) {
350 ipc_answer_0(rid, EEXIST);
351 return;
352 }
353 if ((lflag & L_FILE) && (dcur->type != TMPFS_FILE)) {
354 ipc_answer_0(rid, EISDIR);
355 return;
356 }
357 if ((lflag & L_DIRECTORY) && (dcur->type != TMPFS_DIRECTORY)) {
358 ipc_answer_0(rid, ENOTDIR);
359 return;
360 }
361
362 ipc_answer_4(rid, EOK, tmpfs_reg.fs_handle, dev_handle, dcur->index,
363 dcur->size);
364}
365
366void tmpfs_read(ipc_callid_t rid, ipc_call_t *request)
367{
368 int dev_handle = IPC_GET_ARG1(*request);
369 unsigned long index = IPC_GET_ARG2(*request);
370 off_t pos = IPC_GET_ARG3(*request);
371
372 /*
373 * Lookup the respective dentry.
374 */
375 link_t *hlp;
376 hlp = hash_table_find(&dentries, &index);
377 if (!hlp) {
378 ipc_answer_0(rid, ENOENT);
379 return;
380 }
381 tmpfs_dentry_t *dentry = hash_table_get_instance(hlp, tmpfs_dentry_t,
382 dh_link);
383
384 /*
385 * Receive the read request.
386 */
387 ipc_callid_t callid;
388 size_t len;
389 if (!ipc_data_read_receive(&callid, &len)) {
390 ipc_answer_0(callid, EINVAL);
391 ipc_answer_0(rid, EINVAL);
392 return;
393 }
394
395 size_t bytes;
396 if (dentry->type == TMPFS_FILE) {
397 bytes = max(0, min(dentry->size - pos, len));
398 (void) ipc_data_read_finalize(callid, dentry->data + pos,
399 bytes);
400 } else {
401 int i;
402 tmpfs_dentry_t *cur = dentry->child;
403
404 assert(dentry->type == TMPFS_DIRECTORY);
405
406 /*
407 * Yes, we really use O(n) algorithm here.
408 * If it bothers someone, it could be fixed by introducing a
409 * hash table.
410 */
411 for (i = 0, cur = dentry->child; i < pos && cur; i++,
412 cur = cur->sibling)
413 ;
414
415 if (!cur) {
416 ipc_answer_0(callid, ENOENT);
417 ipc_answer_1(rid, ENOENT, 0);
418 return;
419 }
420
421 (void) ipc_data_read_finalize(callid, cur->name,
422 strlen(cur->name) + 1);
423 bytes = 1;
424 }
425
426 /*
427 * Answer the VFS_READ call.
428 */
429 ipc_answer_1(rid, EOK, bytes);
430}
431
432void tmpfs_write(ipc_callid_t rid, ipc_call_t *request)
433{
434 int dev_handle = IPC_GET_ARG1(*request);
435 unsigned long index = IPC_GET_ARG2(*request);
436 off_t pos = IPC_GET_ARG3(*request);
437
438 /*
439 * Lookup the respective dentry.
440 */
441 link_t *hlp;
442 hlp = hash_table_find(&dentries, &index);
443 if (!hlp) {
444 ipc_answer_0(rid, ENOENT);
445 return;
446 }
447 tmpfs_dentry_t *dentry = hash_table_get_instance(hlp, tmpfs_dentry_t,
448 dh_link);
449
450 /*
451 * Receive the write request.
452 */
453 ipc_callid_t callid;
454 size_t len;
455 if (!ipc_data_write_receive(&callid, &len)) {
456 ipc_answer_0(callid, EINVAL);
457 ipc_answer_0(rid, EINVAL);
458 return;
459 }
460
461 /*
462 * Check whether the file needs to grow.
463 */
464 if (pos + len <= dentry->size) {
465 /* The file size is not changing. */
466 (void) ipc_data_write_finalize(callid, dentry->data + pos, len);
467 ipc_answer_1(rid, EOK, len);
468 return;
469 }
470 size_t delta = (pos + len) - dentry->size;
471 /*
472 * At this point, we are deliberately extremely straightforward and
473 * simply realloc the contents of the file on every write that grows the
474 * file. In the end, the situation might not be as bad as it may look:
475 * our heap allocator can save us and just grow the block whenever
476 * possible.
477 */
478 void *newdata = realloc(dentry->data, dentry->size + delta);
479 if (!newdata) {
480 ipc_answer_0(callid, ENOMEM);
481 ipc_answer_1(rid, EOK, 0);
482 return;
483 }
484 /* Clear any newly allocated memory in order to emulate gaps. */
485 memset(newdata + dentry->size, 0, delta);
486 dentry->size += delta;
487 dentry->data = newdata;
488 (void) ipc_data_write_finalize(callid, dentry->data + pos, len);
489 ipc_answer_2(rid, EOK, len, dentry->size);
490}
491
492void tmpfs_truncate(ipc_callid_t rid, ipc_call_t *request)
493{
494 int dev_handle = IPC_GET_ARG1(*request);
495 unsigned long index = IPC_GET_ARG2(*request);
496 size_t size = IPC_GET_ARG3(*request);
497
498 /*
499 * Lookup the respective dentry.
500 */
501 link_t *hlp;
502 hlp = hash_table_find(&dentries, &index);
503 if (!hlp) {
504 ipc_answer_0(rid, ENOENT);
505 return;
506 }
507 tmpfs_dentry_t *dentry = hash_table_get_instance(hlp, tmpfs_dentry_t,
508 dh_link);
509
510 if (size == dentry->size) {
511 ipc_answer_0(rid, EOK);
512 return;
513 }
514
515 void *newdata = realloc(dentry->data, size);
516 if (!newdata) {
517 ipc_answer_0(rid, ENOMEM);
518 return;
519 }
520 if (size > dentry->size) {
521 size_t delta = size - dentry->size;
522 memset(newdata + dentry->size, 0, delta);
523 }
524 dentry->size = size;
525 dentry->data = newdata;
526 ipc_answer_0(rid, EOK);
527}
528
529/**
530 * @}
531 */
Note: See TracBrowser for help on using the repository browser.