1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
|
#ifndef MERCURY_KERNEL_STORAGE_HPP
#define MERCURY_KERNEL_STORAGE_HPP
#include <mercury/kernel/utility.hpp>
#include <cstdint>
namespace mercury::kernel::storage {
typedef uint64_t node_id_t;
typedef uint64_t directory_iter_t;
enum file_type {
regular_file,
directory,
symlink
};
enum io_result {
success,
mount_point_already_used,
not_a_directory,
not_supported,
out_of_bounds,
device_error,
fs_corrupt,
not_found,
bad_path,
};
class file_system_instance {
public:
virtual ~file_system_instance() {}
virtual io_result get_root_node(node_id_t &out) = 0;
//get_first_child sets iter_out to a "directory iterator" representing the
//first child of this node. get_next_child gets the child after the child
//represented by the value of iter passed in, and changes iter to represent
//that next child. for an empty directory node, get_first_child returns
//io_result::not_found. when iter represents the last child of a node,
//get_next_child also returns io_result::not_found.
virtual io_result get_first_child(node_id_t node, node_id_t &out, directory_iter_t &iter_out) = 0;
virtual io_result get_next_child(node_id_t node, node_id_t &out, directory_iter_t &iter) = 0;
virtual io_result get_child(node_id_t node, node_id_t &out, const char *name, unsigned name_len) = 0;
virtual io_result get_name_length(node_id_t node, unsigned &length_out) = 0;
//buffer is assumed to be long enough - call get_name_length first.
virtual io_result get_name(node_id_t node, char *buffer, unsigned &length_out) = 0;
virtual io_result get_file_length(node_id_t node, uint64_t &length_out) = 0;
virtual io_result get_file_type(node_id_t node, file_type &out) = 0;
virtual io_result resize_file(node_id_t node, uint64_t new_length) = 0;
virtual io_result read_bytes_from_file(node_id_t node, uint64_t start, uint64_t count, void *into) = 0;
virtual io_result write_bytes_into_file(node_id_t node, uint64_t start, uint64_t count, const void *from) = 0;
};
class block_device {
private:
uint8_t *block_cache;
uint64_t block_cache_i;
bool block_cache_dirty;
io_result load_cache_block(uint64_t i);
protected:
//implemented in driver, bounds not checked
virtual io_result read_blocks_no_cache(uint64_t start, uint64_t count, void *into) = 0;
virtual io_result write_blocks_no_cache(uint64_t start, uint64_t count, const void *from) = 0;
//it is assumed that this is only called once, by the driver, after
//block_size and block_count have been set, and before read_bytes or
//write_bytes can be called (e.g. in the derived class's constructor)
void allocate_block_cache();
public:
//set by storage component
file_system_instance *mounted_as;
utility::uuid id;
//set by driver
uint64_t block_size;
uint64_t block_count;
virtual ~block_device() {
if (block_cache)
delete block_cache;
}
io_result read_bytes(uint64_t start, uint64_t count, void *into);
io_result write_bytes(uint64_t start, uint64_t count, const void *from);
};
typedef io_result (*file_system_mounter)(block_device *bd, file_system_instance *&fs_out);
extern utility::list<block_device *> *block_devices;
void init_storage();
//a canon path contains no . or empty directory names, and
//contains no .. except for at the start of a relative path.
struct canon_path {
bool absolute;
unsigned parent_count;
utility::vector<utility::string> segments;
void parent();
void rel(const canon_path &r);
utility::string to_string(bool trailing_slash) const;
};
void canonize_path(const char *str, unsigned len, canon_path &out);
//path must be absolute.
io_result mount_device(block_device *bd, const canon_path &path, file_system_mounter mounter);
//path must be absolute. symlinks are always resolved on nodes other than the
//final one. they are resolved on the final node if and only if resolve final
//node is set to true. mount points are always traversed, including on the
//final node. this assumes that the path is under some mount point, and the
//same for any symlinks traversed (e.g. / is mounted).
io_result look_up_absolute_path(const canon_path &path, block_device *&bd_out,
node_id_t &node_out, bool resolve_final_node, canon_path &path_without_symlinks_out);
}
#endif
|