~martin-decky/helenos/rcu

« back to all changes in this revision

Viewing changes to uspace/srv/fs/ext2fs/ext2fs_ops.c

  • Committer: Jakub Jermar
  • Date: 2011-06-02 21:26:44 UTC
  • mfrom: (720.2.82 ext2-merge)
  • Revision ID: jakub@jermar.eu-20110602212644-t5p3o4bux1n8ybvd
Merge from http://ho.st.dcs.fmph.uniba.sk/~mato/bzr/helenos-ext2.

Changes made against the ext2 branch parent:
- removed .bzrignore
- removed all traces of pipefs

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright (c) 2008 Jakub Jermar
 
3
 * Copyright (c) 2011 Martin Sucha
 
4
 * All rights reserved.
 
5
 *
 
6
 * Redistribution and use in source and binary forms, with or without
 
7
 * modification, are permitted provided that the following conditions
 
8
 * are met:
 
9
 *
 
10
 * - Redistributions of source code must retain the above copyright
 
11
 *   notice, this list of conditions and the following disclaimer.
 
12
 * - Redistributions in binary form must reproduce the above copyright
 
13
 *   notice, this list of conditions and the following disclaimer in the
 
14
 *   documentation and/or other materials provided with the distribution.
 
15
 * - The name of the author may not be used to endorse or promote products
 
16
 *   derived from this software without specific prior written permission.
 
17
 *
 
18
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 
19
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 
20
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 
21
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 
22
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 
23
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 
24
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 
25
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 
26
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 
27
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
28
 */
 
29
 
 
30
/** @addtogroup fs
 
31
 * @{
 
32
 */ 
 
33
 
 
34
/**
 
35
 * @file        ext2fs_ops.c
 
36
 * @brief       Implementation of VFS operations for the EXT2 file system server.
 
37
 */
 
38
 
 
39
#include "ext2fs.h"
 
40
#include "../../vfs/vfs.h"
 
41
#include <libfs.h>
 
42
#include <libblock.h>
 
43
#include <libext2.h>
 
44
#include <ipc/services.h>
 
45
#include <ipc/devmap.h>
 
46
#include <macros.h>
 
47
#include <async.h>
 
48
#include <errno.h>
 
49
#include <str.h>
 
50
#include <byteorder.h>
 
51
#include <adt/hash_table.h>
 
52
#include <adt/list.h>
 
53
#include <assert.h>
 
54
#include <fibril_synch.h>
 
55
#include <sys/mman.h>
 
56
#include <align.h>
 
57
#include <adt/hash_table.h>
 
58
#include <sys/typefmt.h>
 
59
#include <malloc.h>
 
60
#include <stdio.h>
 
61
#include <inttypes.h>
 
62
 
 
63
#define EXT2FS_NODE(node)       ((node) ? (ext2fs_node_t *) (node)->data : NULL)
 
64
#define EXT2FS_DBG(format, ...) {if (false) printf("ext2fs: %s: " format "\n", __FUNCTION__, ##__VA_ARGS__);}
 
65
#define OPEN_NODES_KEYS 2
 
66
#define OPEN_NODES_DEV_HANDLE_KEY 0
 
67
#define OPEN_NODES_INODE_KEY 1
 
68
#define OPEN_NODES_BUCKETS 256
 
69
 
 
70
typedef struct ext2fs_instance {
 
71
        link_t link;
 
72
        devmap_handle_t devmap_handle;
 
73
        ext2_filesystem_t *filesystem;
 
74
        unsigned int open_nodes_count;
 
75
} ext2fs_instance_t;
 
76
 
 
77
typedef struct ext2fs_node {
 
78
        ext2fs_instance_t *instance;
 
79
        ext2_inode_ref_t *inode_ref;
 
80
        fs_node_t *fs_node;
 
81
        link_t link;
 
82
        unsigned int references;
 
83
} ext2fs_node_t;
 
84
 
 
85
/*
 
86
 * Forward declarations of auxiliary functions
 
87
 */
 
88
static int ext2fs_instance_get(devmap_handle_t, ext2fs_instance_t **);
 
89
static void ext2fs_read_directory(ipc_callid_t, ipc_callid_t, aoff64_t,
 
90
        size_t, ext2fs_instance_t *, ext2_inode_ref_t *);
 
91
static void ext2fs_read_file(ipc_callid_t, ipc_callid_t, aoff64_t,
 
92
        size_t, ext2fs_instance_t *, ext2_inode_ref_t *);
 
93
static bool ext2fs_is_dots(const uint8_t *, size_t);
 
94
static int ext2fs_node_get_core(fs_node_t **, ext2fs_instance_t *, fs_index_t);
 
95
static int ext2fs_node_put_core(ext2fs_node_t *);
 
96
 
 
97
/*
 
98
 * Forward declarations of EXT2 libfs operations.
 
99
 */
 
100
static int ext2fs_root_get(fs_node_t **, devmap_handle_t);
 
101
static int ext2fs_match(fs_node_t **, fs_node_t *, const char *);
 
102
static int ext2fs_node_get(fs_node_t **, devmap_handle_t, fs_index_t);
 
103
static int ext2fs_node_open(fs_node_t *);
 
104
static int ext2fs_node_put(fs_node_t *);
 
105
static int ext2fs_create_node(fs_node_t **, devmap_handle_t, int);
 
106
static int ext2fs_destroy_node(fs_node_t *);
 
107
static int ext2fs_link(fs_node_t *, fs_node_t *, const char *);
 
108
static int ext2fs_unlink(fs_node_t *, fs_node_t *, const char *);
 
109
static int ext2fs_has_children(bool *, fs_node_t *);
 
110
static fs_index_t ext2fs_index_get(fs_node_t *);
 
111
static aoff64_t ext2fs_size_get(fs_node_t *);
 
112
static unsigned ext2fs_lnkcnt_get(fs_node_t *);
 
113
static char ext2fs_plb_get_char(unsigned);
 
114
static bool ext2fs_is_directory(fs_node_t *);
 
115
static bool ext2fs_is_file(fs_node_t *node);
 
116
static devmap_handle_t ext2fs_device_get(fs_node_t *node);
 
117
 
 
118
/*
 
119
 * Static variables
 
120
 */
 
121
static LIST_INITIALIZE(instance_list);
 
122
static FIBRIL_MUTEX_INITIALIZE(instance_list_mutex);
 
123
static hash_table_t open_nodes;
 
124
static FIBRIL_MUTEX_INITIALIZE(open_nodes_lock);
 
125
 
 
126
/* Hash table interface for open nodes hash table */
 
127
static hash_index_t open_nodes_hash(unsigned long key[])
 
128
{
 
129
        /* TODO: This is very simple and probably can be improved */
 
130
        return key[OPEN_NODES_INODE_KEY] % OPEN_NODES_BUCKETS;
 
131
}
 
132
 
 
133
static int open_nodes_compare(unsigned long key[], hash_count_t keys, 
 
134
    link_t *item)
 
135
{
 
136
        ext2fs_node_t *enode = hash_table_get_instance(item, ext2fs_node_t, link);
 
137
        assert(keys > 0);
 
138
        if (enode->instance->devmap_handle !=
 
139
            ((devmap_handle_t) key[OPEN_NODES_DEV_HANDLE_KEY])) {
 
140
                return false;
 
141
        }
 
142
        if (keys == 1) {
 
143
                return true;
 
144
        }
 
145
        assert(keys == 2);
 
146
        return (enode->inode_ref->index == key[OPEN_NODES_INODE_KEY]);
 
147
}
 
148
 
 
149
static void open_nodes_remove_cb(link_t *link)
 
150
{
 
151
        /* We don't use remove callback for this hash table */
 
152
}
 
153
 
 
154
static hash_table_operations_t open_nodes_ops = {
 
155
        .hash = open_nodes_hash,
 
156
        .compare = open_nodes_compare,
 
157
        .remove_callback = open_nodes_remove_cb,
 
158
};
 
159
 
 
160
/**
 
161
 * 
 
162
 */
 
163
int ext2fs_global_init(void)
 
164
{
 
165
        if (!hash_table_create(&open_nodes, OPEN_NODES_BUCKETS,
 
166
            OPEN_NODES_KEYS, &open_nodes_ops)) {
 
167
                return ENOMEM;
 
168
        }
 
169
        return EOK;
 
170
}
 
171
 
 
172
int ext2fs_global_fini(void)
 
173
{
 
174
        hash_table_destroy(&open_nodes);
 
175
        return EOK;
 
176
}
 
177
 
 
178
 
 
179
/*
 
180
 * EXT2 libfs operations.
 
181
 */
 
182
 
 
183
/**
 
184
 * Find an instance of filesystem for the given devmap_handle
 
185
 */
 
186
int ext2fs_instance_get(devmap_handle_t devmap_handle, ext2fs_instance_t **inst)
 
187
{
 
188
        EXT2FS_DBG("(%" PRIun ", -)", devmap_handle);
 
189
        link_t *link;
 
190
        ext2fs_instance_t *tmp;
 
191
        
 
192
        fibril_mutex_lock(&instance_list_mutex);
 
193
 
 
194
        if (list_empty(&instance_list)) {
 
195
                EXT2FS_DBG("list empty");
 
196
                fibril_mutex_unlock(&instance_list_mutex);
 
197
                return EINVAL;
 
198
        }
 
199
 
 
200
        for (link = instance_list.next; link != &instance_list; link = link->next) {
 
201
                tmp = list_get_instance(link, ext2fs_instance_t, link);
 
202
                
 
203
                if (tmp->devmap_handle == devmap_handle) {
 
204
                        *inst = tmp;
 
205
                        fibril_mutex_unlock(&instance_list_mutex);
 
206
                        return EOK;
 
207
                }
 
208
        }
 
209
        
 
210
        EXT2FS_DBG("not found");
 
211
        
 
212
        fibril_mutex_unlock(&instance_list_mutex);
 
213
        return EINVAL;
 
214
}
 
215
 
 
216
 
 
217
 
 
218
int ext2fs_root_get(fs_node_t **rfn, devmap_handle_t devmap_handle)
 
219
{
 
220
        EXT2FS_DBG("(-, %" PRIun ")", devmap_handle);
 
221
        return ext2fs_node_get(rfn, devmap_handle, EXT2_INODE_ROOT_INDEX);
 
222
}
 
223
 
 
224
int ext2fs_match(fs_node_t **rfn, fs_node_t *pfn, const char *component)
 
225
{
 
226
        EXT2FS_DBG("(-,-,%s)", component);
 
227
        ext2fs_node_t *eparent = EXT2FS_NODE(pfn);
 
228
        ext2_filesystem_t *fs;
 
229
        ext2_directory_iterator_t it;
 
230
        int rc;
 
231
        size_t name_size;
 
232
        size_t component_size;
 
233
        bool found = false;
 
234
        
 
235
        fs = eparent->instance->filesystem;
 
236
        
 
237
        if (!ext2_inode_is_type(fs->superblock, eparent->inode_ref->inode,
 
238
            EXT2_INODE_MODE_DIRECTORY)) {
 
239
                return ENOTDIR;
 
240
        }
 
241
        
 
242
        rc = ext2_directory_iterator_init(&it, fs, eparent->inode_ref);
 
243
        if (rc != EOK) {
 
244
                return rc;
 
245
        }
 
246
 
 
247
        /* Find length of component in bytes
 
248
         * TODO: check for library function call that does this
 
249
         */
 
250
        component_size = 0;
 
251
        while (*(component+component_size) != 0) {
 
252
                component_size++;
 
253
        }
 
254
        
 
255
        while (it.current != NULL) {
 
256
                /* ignore empty directory entries */
 
257
                if (it.current->inode != 0) {
 
258
                        name_size = ext2_directory_entry_ll_get_name_length(fs->superblock,
 
259
                                it.current);
 
260
 
 
261
                        if (name_size == component_size && bcmp(component, &it.current->name,
 
262
                                    name_size) == 0) {
 
263
                                rc = ext2fs_node_get_core(rfn, eparent->instance,
 
264
                                        it.current->inode);
 
265
                                if (rc != EOK) {
 
266
                                        ext2_directory_iterator_fini(&it);
 
267
                                        return rc;
 
268
                                }
 
269
                                found = true;
 
270
                                break;
 
271
                        }
 
272
                }
 
273
                
 
274
                rc = ext2_directory_iterator_next(&it);
 
275
                if (rc != EOK) {
 
276
                        ext2_directory_iterator_fini(&it);
 
277
                        return rc;
 
278
                }
 
279
        }
 
280
        
 
281
        ext2_directory_iterator_fini(&it);
 
282
        
 
283
        if (!found) {
 
284
                return ENOENT;
 
285
        }
 
286
        
 
287
        return EOK;
 
288
}
 
289
 
 
290
/** Instantiate a EXT2 in-core node. */
 
291
int ext2fs_node_get(fs_node_t **rfn, devmap_handle_t devmap_handle, fs_index_t index)
 
292
{
 
293
        EXT2FS_DBG("(-,%" PRIun ",%u)", devmap_handle, index);
 
294
        
 
295
        ext2fs_instance_t *inst = NULL;
 
296
        int rc;
 
297
        
 
298
        rc = ext2fs_instance_get(devmap_handle, &inst);
 
299
        if (rc != EOK) {
 
300
                return rc;
 
301
        }
 
302
        
 
303
        return ext2fs_node_get_core(rfn, inst, index);
 
304
}
 
305
 
 
306
int ext2fs_node_get_core(fs_node_t **rfn, ext2fs_instance_t *inst,
 
307
                fs_index_t index)
 
308
{
 
309
        int rc;
 
310
        fs_node_t *node = NULL;
 
311
        ext2fs_node_t *enode = NULL;
 
312
        
 
313
        ext2_inode_ref_t *inode_ref = NULL;
 
314
 
 
315
        fibril_mutex_lock(&open_nodes_lock);
 
316
        
 
317
        /* Check if the node is not already open */
 
318
        unsigned long key[] = {
 
319
                [OPEN_NODES_DEV_HANDLE_KEY] = inst->devmap_handle,
 
320
                [OPEN_NODES_INODE_KEY] = index,
 
321
        };
 
322
        link_t *already_open = hash_table_find(&open_nodes, key);
 
323
 
 
324
        if (already_open) {
 
325
                enode = hash_table_get_instance(already_open, ext2fs_node_t, link);
 
326
                *rfn = enode->fs_node;
 
327
                enode->references++;
 
328
 
 
329
                fibril_mutex_unlock(&open_nodes_lock);
 
330
                return EOK;
 
331
        }
 
332
 
 
333
        enode = malloc(sizeof(ext2fs_node_t));
 
334
        if (enode == NULL) {
 
335
                fibril_mutex_unlock(&open_nodes_lock);
 
336
                return ENOMEM;
 
337
        }
 
338
 
 
339
        node = malloc(sizeof(fs_node_t));
 
340
        if (node == NULL) {
 
341
                free(enode);
 
342
                fibril_mutex_unlock(&open_nodes_lock);
 
343
                return ENOMEM;
 
344
        }       
 
345
        fs_node_initialize(node);
 
346
 
 
347
        rc = ext2_filesystem_get_inode_ref(inst->filesystem, index, &inode_ref);
 
348
        if (rc != EOK) {
 
349
                free(enode);
 
350
                free(node);
 
351
                fibril_mutex_unlock(&open_nodes_lock);
 
352
                return rc;
 
353
        }
 
354
        
 
355
        enode->inode_ref = inode_ref;
 
356
        enode->instance = inst;
 
357
        enode->references = 1;
 
358
        enode->fs_node = node;
 
359
        link_initialize(&enode->link);
 
360
        
 
361
        node->data = enode;
 
362
        *rfn = node;
 
363
        
 
364
        hash_table_insert(&open_nodes, key, &enode->link);
 
365
        inst->open_nodes_count++;
 
366
        
 
367
        EXT2FS_DBG("inode: %u", inode_ref->index);
 
368
        
 
369
        EXT2FS_DBG("EOK");
 
370
 
 
371
        fibril_mutex_unlock(&open_nodes_lock);
 
372
        return EOK;
 
373
}
 
374
 
 
375
int ext2fs_node_open(fs_node_t *fn)
 
376
{
 
377
        EXT2FS_DBG("");
 
378
        /*
 
379
         * Opening a file is stateless, nothing
 
380
         * to be done here.
 
381
         */
 
382
        return EOK;
 
383
}
 
384
 
 
385
int ext2fs_node_put(fs_node_t *fn)
 
386
{
 
387
        EXT2FS_DBG("");
 
388
        int rc;
 
389
        ext2fs_node_t *enode = EXT2FS_NODE(fn);
 
390
        
 
391
        fibril_mutex_lock(&open_nodes_lock);
 
392
 
 
393
        assert(enode->references > 0);
 
394
        enode->references--;
 
395
        if (enode->references == 0) {
 
396
                rc = ext2fs_node_put_core(enode);
 
397
                if (rc != EOK) {
 
398
                        fibril_mutex_unlock(&open_nodes_lock);
 
399
                        return rc;
 
400
                }
 
401
        }
 
402
 
 
403
        fibril_mutex_unlock(&open_nodes_lock);
 
404
        
 
405
        return EOK;
 
406
}
 
407
 
 
408
int ext2fs_node_put_core(ext2fs_node_t *enode)
 
409
{
 
410
        int rc;
 
411
 
 
412
        unsigned long key[] = {
 
413
                [OPEN_NODES_DEV_HANDLE_KEY] = enode->instance->devmap_handle,
 
414
                [OPEN_NODES_INODE_KEY] = enode->inode_ref->index,
 
415
        };
 
416
        hash_table_remove(&open_nodes, key, OPEN_NODES_KEYS);
 
417
        assert(enode->instance->open_nodes_count > 0);
 
418
        enode->instance->open_nodes_count--;
 
419
 
 
420
        rc = ext2_filesystem_put_inode_ref(enode->inode_ref);
 
421
        if (rc != EOK) {
 
422
                EXT2FS_DBG("ext2_filesystem_put_inode_ref failed");
 
423
                return rc;
 
424
        }
 
425
 
 
426
        free(enode->fs_node);
 
427
        free(enode);
 
428
        return EOK;
 
429
}
 
430
 
 
431
int ext2fs_create_node(fs_node_t **rfn, devmap_handle_t devmap_handle, int flags)
 
432
{
 
433
        EXT2FS_DBG("");
 
434
        // TODO
 
435
        return ENOTSUP;
 
436
}
 
437
 
 
438
int ext2fs_destroy_node(fs_node_t *fn)
 
439
{
 
440
        EXT2FS_DBG("");
 
441
        // TODO
 
442
        return ENOTSUP;
 
443
}
 
444
 
 
445
int ext2fs_link(fs_node_t *pfn, fs_node_t *cfn, const char *name)
 
446
{
 
447
        EXT2FS_DBG("");
 
448
        // TODO
 
449
        return ENOTSUP;
 
450
}
 
451
 
 
452
int ext2fs_unlink(fs_node_t *pfn, fs_node_t *cfn, const char *nm)
 
453
{
 
454
        EXT2FS_DBG("");
 
455
        // TODO
 
456
        return ENOTSUP;
 
457
}
 
458
 
 
459
int ext2fs_has_children(bool *has_children, fs_node_t *fn)
 
460
{
 
461
        EXT2FS_DBG("");
 
462
        ext2fs_node_t *enode = EXT2FS_NODE(fn);
 
463
        ext2_directory_iterator_t it;
 
464
        ext2_filesystem_t *fs;
 
465
        int rc;
 
466
        bool found = false;
 
467
        size_t name_size;
 
468
 
 
469
        fs = enode->instance->filesystem;
 
470
 
 
471
        if (!ext2_inode_is_type(fs->superblock, enode->inode_ref->inode,
 
472
            EXT2_INODE_MODE_DIRECTORY)) {
 
473
                *has_children = false;
 
474
                EXT2FS_DBG("EOK - false");
 
475
                return EOK;
 
476
        }
 
477
        
 
478
        rc = ext2_directory_iterator_init(&it, fs, enode->inode_ref);
 
479
        if (rc != EOK) {
 
480
                EXT2FS_DBG("error %u", rc);
 
481
                return rc;
 
482
        }
 
483
        
 
484
        /* Find a non-empty directory entry */
 
485
        while (it.current != NULL) {
 
486
                if (it.current->inode != 0) {
 
487
                        name_size = ext2_directory_entry_ll_get_name_length(fs->superblock,
 
488
                                it.current);
 
489
                        if (!ext2fs_is_dots(&it.current->name, name_size)) {
 
490
                                found = true;
 
491
                                break;
 
492
                        }
 
493
                }
 
494
                
 
495
                rc = ext2_directory_iterator_next(&it);
 
496
                if (rc != EOK) {
 
497
                        ext2_directory_iterator_fini(&it);
 
498
                        EXT2FS_DBG("error %u", rc);
 
499
                        return rc;
 
500
                }
 
501
        }
 
502
        
 
503
        rc = ext2_directory_iterator_fini(&it);
 
504
        if (rc != EOK) {
 
505
                EXT2FS_DBG("error %u", rc);
 
506
                return rc;
 
507
        }
 
508
 
 
509
        *has_children = found;
 
510
        EXT2FS_DBG("EOK");
 
511
        
 
512
        return EOK;
 
513
}
 
514
 
 
515
 
 
516
fs_index_t ext2fs_index_get(fs_node_t *fn)
 
517
{
 
518
        ext2fs_node_t *enode = EXT2FS_NODE(fn);
 
519
        EXT2FS_DBG("%u", enode->inode_ref->index);
 
520
        return enode->inode_ref->index;
 
521
}
 
522
 
 
523
aoff64_t ext2fs_size_get(fs_node_t *fn)
 
524
{
 
525
        ext2fs_node_t *enode = EXT2FS_NODE(fn);
 
526
        aoff64_t size = ext2_inode_get_size(enode->instance->filesystem->superblock,
 
527
            enode->inode_ref->inode);
 
528
        EXT2FS_DBG("%" PRIu64, size);
 
529
        return size;
 
530
}
 
531
 
 
532
unsigned ext2fs_lnkcnt_get(fs_node_t *fn)
 
533
{
 
534
        ext2fs_node_t *enode = EXT2FS_NODE(fn);
 
535
        unsigned count = ext2_inode_get_usage_count(enode->inode_ref->inode);
 
536
        EXT2FS_DBG("%u", count);
 
537
        return count;
 
538
}
 
539
 
 
540
char ext2fs_plb_get_char(unsigned pos)
 
541
{
 
542
        return ext2fs_reg.plb_ro[pos % PLB_SIZE];
 
543
}
 
544
 
 
545
bool ext2fs_is_directory(fs_node_t *fn)
 
546
{
 
547
        ext2fs_node_t *enode = EXT2FS_NODE(fn);
 
548
        bool is_dir = ext2_inode_is_type(enode->instance->filesystem->superblock,
 
549
            enode->inode_ref->inode, EXT2_INODE_MODE_DIRECTORY);
 
550
        EXT2FS_DBG("%s", is_dir ? "true" : "false");
 
551
        EXT2FS_DBG("%u", enode->inode_ref->index);
 
552
        return is_dir;
 
553
}
 
554
 
 
555
bool ext2fs_is_file(fs_node_t *fn)
 
556
{
 
557
        ext2fs_node_t *enode = EXT2FS_NODE(fn);
 
558
        bool is_file = ext2_inode_is_type(enode->instance->filesystem->superblock,
 
559
            enode->inode_ref->inode, EXT2_INODE_MODE_FILE);
 
560
        EXT2FS_DBG("%s", is_file ? "true" : "false");
 
561
        return is_file;
 
562
}
 
563
 
 
564
devmap_handle_t ext2fs_device_get(fs_node_t *fn)
 
565
{
 
566
        EXT2FS_DBG("");
 
567
        ext2fs_node_t *enode = EXT2FS_NODE(fn);
 
568
        return enode->instance->devmap_handle;
 
569
}
 
570
 
 
571
/** libfs operations */
 
572
libfs_ops_t ext2fs_libfs_ops = {
 
573
        .root_get = ext2fs_root_get,
 
574
        .match = ext2fs_match,
 
575
        .node_get = ext2fs_node_get,
 
576
        .node_open = ext2fs_node_open,
 
577
        .node_put = ext2fs_node_put,
 
578
        .create = ext2fs_create_node,
 
579
        .destroy = ext2fs_destroy_node,
 
580
        .link = ext2fs_link,
 
581
        .unlink = ext2fs_unlink,
 
582
        .has_children = ext2fs_has_children,
 
583
        .index_get = ext2fs_index_get,
 
584
        .size_get = ext2fs_size_get,
 
585
        .lnkcnt_get = ext2fs_lnkcnt_get,
 
586
        .plb_get_char = ext2fs_plb_get_char,
 
587
        .is_directory = ext2fs_is_directory,
 
588
        .is_file = ext2fs_is_file,
 
589
        .device_get = ext2fs_device_get
 
590
};
 
591
 
 
592
/*
 
593
 * VFS operations.
 
594
 */
 
595
 
 
596
void ext2fs_mounted(ipc_callid_t rid, ipc_call_t *request)
 
597
{
 
598
        EXT2FS_DBG("");
 
599
        int rc;
 
600
        devmap_handle_t devmap_handle = (devmap_handle_t) IPC_GET_ARG1(*request);
 
601
        ext2_filesystem_t *fs;
 
602
        ext2fs_instance_t *inst;
 
603
        bool read_only;
 
604
        
 
605
        /* Accept the mount options */
 
606
        char *opts;
 
607
        rc = async_data_write_accept((void **) &opts, true, 0, 0, 0, NULL);
 
608
        
 
609
        if (rc != EOK) {
 
610
                async_answer_0(rid, rc);
 
611
                return;
 
612
        }
 
613
 
 
614
        free(opts);
 
615
        
 
616
        /* Allocate libext2 filesystem structure */
 
617
        fs = (ext2_filesystem_t *) malloc(sizeof(ext2_filesystem_t));
 
618
        if (fs == NULL) {
 
619
                async_answer_0(rid, ENOMEM);
 
620
                return;
 
621
        }
 
622
        
 
623
        /* Allocate instance structure */
 
624
        inst = (ext2fs_instance_t *) malloc(sizeof(ext2fs_instance_t));
 
625
        if (inst == NULL) {
 
626
                free(fs);
 
627
                async_answer_0(rid, ENOMEM);
 
628
                return;
 
629
        }
 
630
        
 
631
        /* Initialize the filesystem  */
 
632
        rc = ext2_filesystem_init(fs, devmap_handle);
 
633
        if (rc != EOK) {
 
634
                free(fs);
 
635
                free(inst);
 
636
                async_answer_0(rid, rc);
 
637
                return;
 
638
        }
 
639
        
 
640
        /* Do some sanity checking */
 
641
        rc = ext2_filesystem_check_sanity(fs);
 
642
        if (rc != EOK) {
 
643
                ext2_filesystem_fini(fs);
 
644
                free(fs);
 
645
                free(inst);
 
646
                async_answer_0(rid, rc);
 
647
                return;
 
648
        }
 
649
        
 
650
        /* Check flags */
 
651
        rc = ext2_filesystem_check_flags(fs, &read_only);
 
652
        if (rc != EOK) {
 
653
                ext2_filesystem_fini(fs);
 
654
                free(fs);
 
655
                free(inst);
 
656
                async_answer_0(rid, rc);
 
657
                return;
 
658
        }
 
659
        
 
660
        /* Initialize instance */
 
661
        link_initialize(&inst->link);
 
662
        inst->devmap_handle = devmap_handle;
 
663
        inst->filesystem = fs;
 
664
        inst->open_nodes_count = 0;
 
665
        
 
666
        /* Read root node */
 
667
        fs_node_t *root_node;
 
668
        rc = ext2fs_node_get_core(&root_node, inst, EXT2_INODE_ROOT_INDEX);
 
669
        if (rc != EOK) {
 
670
                ext2_filesystem_fini(fs);
 
671
                free(fs);
 
672
                free(inst);
 
673
                async_answer_0(rid, rc);
 
674
                return;
 
675
        }
 
676
        ext2fs_node_t *enode = EXT2FS_NODE(root_node);
 
677
                
 
678
        /* Add instance to the list */
 
679
        fibril_mutex_lock(&instance_list_mutex);
 
680
        list_append(&inst->link, &instance_list);
 
681
        fibril_mutex_unlock(&instance_list_mutex);
 
682
        
 
683
        async_answer_3(rid, EOK,
 
684
            EXT2_INODE_ROOT_INDEX,
 
685
            0,
 
686
            ext2_inode_get_usage_count(enode->inode_ref->inode));
 
687
        
 
688
        ext2fs_node_put(root_node);
 
689
}
 
690
 
 
691
void ext2fs_mount(ipc_callid_t rid, ipc_call_t *request)
 
692
{
 
693
        EXT2FS_DBG("");
 
694
        libfs_mount(&ext2fs_libfs_ops, ext2fs_reg.fs_handle, rid, request);
 
695
}
 
696
 
 
697
void ext2fs_unmounted(ipc_callid_t rid, ipc_call_t *request)
 
698
{
 
699
        EXT2FS_DBG("");
 
700
        devmap_handle_t devmap_handle = (devmap_handle_t) IPC_GET_ARG1(*request);
 
701
        ext2fs_instance_t *inst;
 
702
        int rc;
 
703
        
 
704
        rc = ext2fs_instance_get(devmap_handle, &inst);
 
705
        
 
706
        if (rc != EOK) {
 
707
                async_answer_0(rid, rc);
 
708
                return;
 
709
        }
 
710
        
 
711
        fibril_mutex_lock(&open_nodes_lock);
 
712
 
 
713
        EXT2FS_DBG("open_nodes_count = %d", inst->open_nodes_count)
 
714
        if (inst->open_nodes_count != 0) {
 
715
                fibril_mutex_unlock(&open_nodes_lock);
 
716
                async_answer_0(rid, EBUSY);
 
717
                return;
 
718
        }
 
719
        
 
720
        /* Remove the instance from the list */
 
721
        fibril_mutex_lock(&instance_list_mutex);
 
722
        list_remove(&inst->link);
 
723
        fibril_mutex_unlock(&instance_list_mutex);
 
724
 
 
725
        fibril_mutex_unlock(&open_nodes_lock);
 
726
        
 
727
        ext2_filesystem_fini(inst->filesystem);
 
728
        
 
729
        async_answer_0(rid, EOK);
 
730
}
 
731
 
 
732
void ext2fs_unmount(ipc_callid_t rid, ipc_call_t *request)
 
733
{
 
734
        EXT2FS_DBG("");
 
735
        libfs_unmount(&ext2fs_libfs_ops, rid, request);
 
736
}
 
737
 
 
738
void ext2fs_lookup(ipc_callid_t rid, ipc_call_t *request)
 
739
{
 
740
        EXT2FS_DBG("");
 
741
        libfs_lookup(&ext2fs_libfs_ops, ext2fs_reg.fs_handle, rid, request);
 
742
}
 
743
 
 
744
void ext2fs_read(ipc_callid_t rid, ipc_call_t *request)
 
745
{
 
746
        EXT2FS_DBG("");
 
747
        devmap_handle_t devmap_handle = (devmap_handle_t) IPC_GET_ARG1(*request);
 
748
        fs_index_t index = (fs_index_t) IPC_GET_ARG2(*request);
 
749
        aoff64_t pos =
 
750
            (aoff64_t) MERGE_LOUP32(IPC_GET_ARG3(*request), IPC_GET_ARG4(*request));
 
751
        
 
752
        ext2fs_instance_t *inst;
 
753
        ext2_inode_ref_t *inode_ref;
 
754
        int rc;
 
755
        
 
756
        /*
 
757
         * Receive the read request.
 
758
         */
 
759
        ipc_callid_t callid;
 
760
        size_t size;
 
761
        if (!async_data_read_receive(&callid, &size)) {
 
762
                async_answer_0(callid, EINVAL);
 
763
                async_answer_0(rid, EINVAL);
 
764
                return;
 
765
        }
 
766
        
 
767
        rc = ext2fs_instance_get(devmap_handle, &inst);
 
768
        if (rc != EOK) {
 
769
                async_answer_0(callid, rc);
 
770
                async_answer_0(rid, rc);
 
771
                return;
 
772
        }
 
773
        
 
774
        rc = ext2_filesystem_get_inode_ref(inst->filesystem, index, &inode_ref);
 
775
        if (rc != EOK) {
 
776
                async_answer_0(callid, rc);
 
777
                async_answer_0(rid, rc);
 
778
                return;
 
779
        }
 
780
        
 
781
        if (ext2_inode_is_type(inst->filesystem->superblock, inode_ref->inode,
 
782
                    EXT2_INODE_MODE_FILE)) {
 
783
                ext2fs_read_file(rid, callid, pos, size, inst, inode_ref);
 
784
        }
 
785
        else if (ext2_inode_is_type(inst->filesystem->superblock, inode_ref->inode,
 
786
                    EXT2_INODE_MODE_DIRECTORY)) {
 
787
                ext2fs_read_directory(rid, callid, pos, size, inst, inode_ref);
 
788
        }
 
789
        else {
 
790
                /* Other inode types not supported */
 
791
                async_answer_0(callid, ENOTSUP);
 
792
                async_answer_0(rid, ENOTSUP);
 
793
        }
 
794
        
 
795
        ext2_filesystem_put_inode_ref(inode_ref);
 
796
        
 
797
}
 
798
 
 
799
/**
 
800
 * Determine whether given directory entry name is . or ..
 
801
 */
 
802
bool ext2fs_is_dots(const uint8_t *name, size_t name_size) {
 
803
        if (name_size == 1 && name[0] == '.') {
 
804
                return true;
 
805
        }
 
806
        
 
807
        if (name_size == 2 && name[0] == '.' && name[1] == '.') {
 
808
                return true;
 
809
        }
 
810
        
 
811
        return false;
 
812
}
 
813
 
 
814
void ext2fs_read_directory(ipc_callid_t rid, ipc_callid_t callid, aoff64_t pos,
 
815
        size_t size, ext2fs_instance_t *inst, ext2_inode_ref_t *inode_ref)
 
816
{
 
817
        ext2_directory_iterator_t it;
 
818
        aoff64_t cur;
 
819
        uint8_t *buf;
 
820
        size_t name_size;
 
821
        int rc;
 
822
        bool found = false;
 
823
        
 
824
        rc = ext2_directory_iterator_init(&it, inst->filesystem, inode_ref);
 
825
        if (rc != EOK) {
 
826
                async_answer_0(callid, rc);
 
827
                async_answer_0(rid, rc);
 
828
                return;
 
829
        }
 
830
        
 
831
        /* Find the index we want to read
 
832
         * Note that we need to iterate and count as
 
833
         * the underlying structure is a linked list
 
834
         * Moreover, we want to skip . and .. entries
 
835
         * as these are not used in HelenOS
 
836
         */
 
837
        cur = 0;
 
838
        while (it.current != NULL) {
 
839
                if (it.current->inode == 0) {
 
840
                        goto skip;
 
841
                }
 
842
                
 
843
                name_size = ext2_directory_entry_ll_get_name_length(
 
844
                        inst->filesystem->superblock, it.current);
 
845
                
 
846
                /* skip . and .. */
 
847
                if (ext2fs_is_dots(&it.current->name, name_size)) {
 
848
                        goto skip;
 
849
                }
 
850
                
 
851
                /* Is this the dir entry we want to read? */
 
852
                if (cur == pos) {
 
853
                        /* The on-disk entry does not contain \0 at the end
 
854
                         * end of entry name, so we copy it to new buffer
 
855
                         * and add the \0 at the end
 
856
                         */
 
857
                        buf = malloc(name_size+1);
 
858
                        if (buf == NULL) {
 
859
                                ext2_directory_iterator_fini(&it);
 
860
                                async_answer_0(callid, ENOMEM);
 
861
                                async_answer_0(rid, ENOMEM);
 
862
                                return;
 
863
                        }
 
864
                        memcpy(buf, &it.current->name, name_size);
 
865
                        *(buf+name_size) = 0;
 
866
                        found = true;
 
867
                        (void) async_data_read_finalize(callid, buf, name_size+1);
 
868
                        free(buf);
 
869
                        break;
 
870
                }
 
871
                cur++;
 
872
                
 
873
skip:
 
874
                rc = ext2_directory_iterator_next(&it);
 
875
                if (rc != EOK) {
 
876
                        ext2_directory_iterator_fini(&it);
 
877
                        async_answer_0(callid, rc);
 
878
                        async_answer_0(rid, rc);
 
879
                        return;
 
880
                }
 
881
        }
 
882
        
 
883
        rc = ext2_directory_iterator_fini(&it);
 
884
        if (rc != EOK) {
 
885
                async_answer_0(rid, rc);
 
886
                return;
 
887
        }
 
888
        
 
889
        if (found) {
 
890
                async_answer_1(rid, EOK, 1);
 
891
        }
 
892
        else {
 
893
                async_answer_0(callid, ENOENT);
 
894
                async_answer_0(rid, ENOENT);
 
895
        }
 
896
}
 
897
 
 
898
void ext2fs_read_file(ipc_callid_t rid, ipc_callid_t callid, aoff64_t pos,
 
899
        size_t size, ext2fs_instance_t *inst, ext2_inode_ref_t *inode_ref)
 
900
{
 
901
        int rc;
 
902
        uint32_t block_size;
 
903
        aoff64_t file_block;
 
904
        uint64_t file_size;
 
905
        uint32_t fs_block;
 
906
        size_t offset_in_block;
 
907
        size_t bytes;
 
908
        block_t *block;
 
909
        uint8_t *buffer;
 
910
        
 
911
        file_size = ext2_inode_get_size(inst->filesystem->superblock,
 
912
                inode_ref->inode);
 
913
        
 
914
        if (pos >= file_size) {
 
915
                /* Read 0 bytes successfully */
 
916
                async_data_read_finalize(callid, NULL, 0);
 
917
                async_answer_1(rid, EOK, 0);
 
918
                return;
 
919
        }
 
920
        
 
921
        /* For now, we only read data from one block at a time */
 
922
        block_size = ext2_superblock_get_block_size(inst->filesystem->superblock);
 
923
        file_block = pos / block_size;
 
924
        offset_in_block = pos % block_size;
 
925
        bytes = min(block_size - offset_in_block, size);
 
926
        
 
927
        /* Handle end of file */
 
928
        if (pos + bytes > file_size) {
 
929
                bytes = file_size - pos;
 
930
        }
 
931
        
 
932
        /* Get the real block number */
 
933
        rc = ext2_filesystem_get_inode_data_block_index(inst->filesystem,
 
934
                inode_ref->inode, file_block, &fs_block);
 
935
        if (rc != EOK) {
 
936
                async_answer_0(callid, rc);
 
937
                async_answer_0(rid, rc);
 
938
                return;
 
939
        }
 
940
        
 
941
        /* Check for sparse file
 
942
         * If ext2_filesystem_get_inode_data_block_index returned
 
943
         * fs_block == 0, it means that the given block is not allocated for the 
 
944
         * file and we need to return a buffer of zeros
 
945
         */
 
946
        if (fs_block == 0) {
 
947
                buffer = malloc(bytes);
 
948
                if (buffer == NULL) {
 
949
                        async_answer_0(callid, ENOMEM);
 
950
                        async_answer_0(rid, ENOMEM);
 
951
                        return;
 
952
                }
 
953
                
 
954
                memset(buffer, 0, bytes);
 
955
                
 
956
                async_data_read_finalize(callid, buffer, bytes);
 
957
                async_answer_1(rid, EOK, bytes);
 
958
                
 
959
                free(buffer);
 
960
                
 
961
                return;
 
962
        }
 
963
        
 
964
        /* Usual case - we need to read a block from device */
 
965
        rc = block_get(&block, inst->devmap_handle, fs_block, BLOCK_FLAGS_NONE);
 
966
        if (rc != EOK) {
 
967
                async_answer_0(callid, rc);
 
968
                async_answer_0(rid, rc);
 
969
                return;
 
970
        }
 
971
        
 
972
        assert(offset_in_block + bytes <= block_size);
 
973
        async_data_read_finalize(callid, block->data + offset_in_block, bytes);
 
974
        
 
975
        rc = block_put(block);
 
976
        if (rc != EOK) {
 
977
                async_answer_0(rid, rc);
 
978
                return;
 
979
        }
 
980
                
 
981
        async_answer_1(rid, EOK, bytes);
 
982
}
 
983
 
 
984
void ext2fs_write(ipc_callid_t rid, ipc_call_t *request)
 
985
{
 
986
        EXT2FS_DBG("");
 
987
//      devmap_handle_t devmap_handle = (devmap_handle_t) IPC_GET_ARG1(*request);
 
988
//      fs_index_t index = (fs_index_t) IPC_GET_ARG2(*request);
 
989
//      aoff64_t pos =
 
990
//          (aoff64_t) MERGE_LOUP32(IPC_GET_ARG3(*request), IPC_GET_ARG4(*request));
 
991
        
 
992
        // TODO
 
993
        async_answer_0(rid, ENOTSUP);
 
994
}
 
995
 
 
996
void ext2fs_truncate(ipc_callid_t rid, ipc_call_t *request)
 
997
{
 
998
        EXT2FS_DBG("");
 
999
//      devmap_handle_t devmap_handle = (devmap_handle_t) IPC_GET_ARG1(*request);
 
1000
//      fs_index_t index = (fs_index_t) IPC_GET_ARG2(*request);
 
1001
//      aoff64_t size =
 
1002
//          (aoff64_t) MERGE_LOUP32(IPC_GET_ARG3(*request), IPC_GET_ARG4(*request));
 
1003
        
 
1004
        // TODO
 
1005
        async_answer_0(rid, ENOTSUP);
 
1006
}
 
1007
 
 
1008
void ext2fs_close(ipc_callid_t rid, ipc_call_t *request)
 
1009
{
 
1010
        EXT2FS_DBG("");
 
1011
        async_answer_0(rid, EOK);
 
1012
}
 
1013
 
 
1014
void ext2fs_destroy(ipc_callid_t rid, ipc_call_t *request)
 
1015
{
 
1016
        EXT2FS_DBG("");
 
1017
//      devmap_handle_t devmap_handle = (devmap_handle_t)IPC_GET_ARG1(*request);
 
1018
//      fs_index_t index = (fs_index_t)IPC_GET_ARG2(*request);
 
1019
        
 
1020
        // TODO
 
1021
        async_answer_0(rid, ENOTSUP);
 
1022
}
 
1023
 
 
1024
void ext2fs_open_node(ipc_callid_t rid, ipc_call_t *request)
 
1025
{
 
1026
        EXT2FS_DBG("");
 
1027
        libfs_open_node(&ext2fs_libfs_ops, ext2fs_reg.fs_handle, rid, request);
 
1028
}
 
1029
 
 
1030
void ext2fs_stat(ipc_callid_t rid, ipc_call_t *request)
 
1031
{
 
1032
        EXT2FS_DBG("");
 
1033
        libfs_stat(&ext2fs_libfs_ops, ext2fs_reg.fs_handle, rid, request);
 
1034
}
 
1035
 
 
1036
void ext2fs_sync(ipc_callid_t rid, ipc_call_t *request)
 
1037
{
 
1038
        EXT2FS_DBG("");
 
1039
//      devmap_handle_t devmap_handle = (devmap_handle_t) IPC_GET_ARG1(*request);
 
1040
//      fs_index_t index = (fs_index_t) IPC_GET_ARG2(*request);
 
1041
        
 
1042
        // TODO
 
1043
        async_answer_0(rid, ENOTSUP);
 
1044
}
 
1045
 
 
1046
/**
 
1047
 * @}
 
1048
 */