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

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

Introduce the notion of lflag (i.e. lookup flags) to support the ability to
limit the scope of VFS node types that can be opened by open() and opendir(). In
the future, lflag will also specify actions for VFS_LOOKUP handlers that will be
carried out in situations such as the VFS node is not found (e.g. implementation
of mkdir() and open() with O_CREAT in oflag).

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