1 #include <string.h> |
|
2 #include <errno.h> |
|
3 #include <stdlib.h> |
|
4 #include <assert.h> |
|
5 |
|
6 #include "simple.h" |
|
7 #include "dirbuf.h" |
|
8 #include "lib/log.h" |
|
9 #include "lib/math.h" |
|
10 #include "lib/misc.h" |
|
11 |
|
12 struct simple_fs { |
|
13 const struct simple_node *inode_table; |
|
14 |
|
15 size_t inode_count; |
|
16 }; |
|
17 |
|
18 /* |
|
19 * Used for stat/entry timeouts... not sure how this should really be set. |
|
20 */ |
|
21 #define CACHE_TIMEOUT 1.0 |
|
22 |
|
23 static void _simple_stat (struct stat *stat, const struct simple_node *node) { |
|
24 stat->st_ino = node->inode; |
|
25 stat->st_mode = node->mode_type | node->mode_perm; |
|
26 stat->st_nlink = 1; |
|
27 stat->st_size = node->data ? strlen(node->data) : 0; |
|
28 } |
|
29 |
|
30 /* |
|
31 * Fetch the simple_node for the given inode. |
|
32 * |
|
33 * Returns NULL for invalid inodes. |
|
34 */ |
|
35 static const struct simple_node *_simple_get_ino (struct simple_fs *fs, fuse_ino_t ino) { |
|
36 // make sure it's a valid inode |
|
37 if (ino < 1 || ino > fs->inode_count) { |
|
38 WARNING("invalid inode=%zu", ino); |
|
39 return NULL; |
|
40 } |
|
41 |
|
42 // return the node |
|
43 return fs->inode_table + (ino - 1); |
|
44 } |
|
45 |
|
46 static void simple_lookup (fuse_req_t req, fuse_ino_t parent, const char *name) { |
|
47 struct simple_fs *fs = fuse_req_userdata(req); |
|
48 const struct simple_node *node; |
|
49 struct fuse_entry_param e; ZINIT(e); |
|
50 int err; |
|
51 |
|
52 INFO("[simple.lookup %p] parent=%lu, name=`%s'", fs, parent, name); |
|
53 |
|
54 // find the matching node |
|
55 for (node = fs->inode_table; node->inode > 0; node++) { |
|
56 if (node->parent == parent && strcmp(node->name, name) == 0) |
|
57 break; |
|
58 |
|
59 } |
|
60 |
|
61 // did we find it? |
|
62 if (node->inode) { |
|
63 // set up the entry |
|
64 e.ino = node->inode; |
|
65 e.generation = 0x01; |
|
66 _simple_stat(&e.attr, node); |
|
67 e.attr_timeout = CACHE_TIMEOUT; |
|
68 e.entry_timeout = CACHE_TIMEOUT; |
|
69 |
|
70 // reply |
|
71 if ((err = fuse_reply_entry(req, &e))) |
|
72 EERROR(err, "fuse_reply_entry"); |
|
73 |
|
74 } else { |
|
75 // not found |
|
76 err = ENOENT; |
|
77 goto error; |
|
78 } |
|
79 |
|
80 // success |
|
81 return; |
|
82 |
|
83 error: |
|
84 if ((err = fuse_reply_err(req, err))) |
|
85 EWARNING(err, "fuse_reply_err"); |
|
86 } |
|
87 |
|
88 static void simple_getattr (fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) { |
|
89 struct simple_fs *fs = fuse_req_userdata(req); |
|
90 const struct simple_node *node; |
|
91 struct stat stbuf; ZINIT(stbuf); |
|
92 int err; |
|
93 |
|
94 INFO("[simple.getattr %p] ino=%lu", fs, ino); |
|
95 |
|
96 // look up the node |
|
97 if ((node = _simple_get_ino(fs, ino)) == NULL) |
|
98 EERROR(err = EINVAL, "bad inode"); |
|
99 |
|
100 // set up the stbuf |
|
101 _simple_stat(&stbuf, node); |
|
102 |
|
103 // reply |
|
104 if ((err = fuse_reply_attr(req, &stbuf, CACHE_TIMEOUT))) |
|
105 EERROR(err, "fuse_reply_attr"); |
|
106 |
|
107 // suceccss |
|
108 return; |
|
109 |
|
110 error: |
|
111 if ((err = fuse_reply_err(req, err))) |
|
112 EWARNING(err, "fuse_reply_err"); |
|
113 } |
|
114 |
|
115 static void simple_readlink (fuse_req_t req, fuse_ino_t ino) { |
|
116 struct simple_fs *fs = fuse_req_userdata(req); |
|
117 const struct simple_node *node; |
|
118 int err; |
|
119 |
|
120 INFO("[simple.readlink %p] ino=%lu", fs, ino); |
|
121 |
|
122 // look up the node |
|
123 if ((node = _simple_get_ino(fs, ino)) == NULL) |
|
124 EERROR(err = EINVAL, "bad inode"); |
|
125 |
|
126 // check that it's a symlink |
|
127 if (node->mode_type != S_IFLNK) |
|
128 EERROR(err = EINVAL, "bad mode"); |
|
129 |
|
130 // return the contents |
|
131 if ((err = fuse_reply_readlink(req, node->data))) |
|
132 EERROR(err, "fuse_reply_readlink"); |
|
133 |
|
134 // suceccss |
|
135 return; |
|
136 |
|
137 error: |
|
138 if ((err = fuse_reply_err(req, err))) |
|
139 EWARNING(err, "fuse_reply_err"); |
|
140 |
|
141 } |
|
142 |
|
143 static void simple_readdir (fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi) { |
|
144 struct simple_fs *fs = fuse_req_userdata(req); |
|
145 const struct simple_node *dir_node, *node; |
|
146 struct dirbuf buf; |
|
147 int err; |
|
148 |
|
149 INFO("[simple.readdir] ino=%lu, size=%zu, off=%zu, fi=%p", ino, size, off, fi); |
|
150 |
|
151 // look up the inode |
|
152 if ((dir_node = _simple_get_ino(fs, ino)) == NULL) |
|
153 EERROR(err = EINVAL, "bad inode"); |
|
154 |
|
155 // check that it's a dir |
|
156 if (dir_node->mode_type != S_IFDIR) |
|
157 EERROR(err = ENOTDIR, "bad mode"); |
|
158 |
|
159 // fill in the dirbuf |
|
160 if (dirbuf_init(&buf, size, off)) |
|
161 ERROR("failed to init dirbuf"); |
|
162 |
|
163 // add . and .. |
|
164 // we set the next offset to 2, because all dirent offsets will be larger than that |
|
165 err = dirbuf_add(req, &buf, 0, 1, ".", dir_node->inode, S_IFDIR ) |
|
166 || dirbuf_add(req, &buf, 1, 2, "..", dir_node->inode, S_IFDIR ); |
|
167 |
|
168 if (err != 0) |
|
169 EERROR(err, "failed to add . and .. dirents"); |
|
170 |
|
171 // look up all child nodes |
|
172 for (node = fs->inode_table; node->inode; node++) { |
|
173 // skip non-children |
|
174 if (node->parent != dir_node->inode) |
|
175 continue; |
|
176 |
|
177 // child node offsets are just inode + 2 |
|
178 if ((err = dirbuf_add(req, &buf, node->inode + 2, node->inode + 3, node->name, node->inode, node->mode_type)) < 0) |
|
179 EERROR(err, "failed to add dirent for inode=%lu", node->inode); |
|
180 |
|
181 // stop if it's full |
|
182 if (err > 0) |
|
183 break; |
|
184 } |
|
185 |
|
186 // send it |
|
187 if ((err = -dirbuf_done(req, &buf))) |
|
188 EERROR(err, "failed to send buf"); |
|
189 |
|
190 // success |
|
191 return; |
|
192 |
|
193 error: |
|
194 if ((err = fuse_reply_err(req, err))) |
|
195 EWARNING(err, "fuse_reply_err"); |
|
196 } |
|
197 |
|
198 static void simple_read (fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi) { |
|
199 struct simple_fs *fs = fuse_req_userdata(req); |
|
200 const struct simple_node *node; |
|
201 int err ; |
|
202 |
|
203 // fi is unused |
|
204 (void) fi; |
|
205 |
|
206 INFO("[simple.read] ino=%lu, size=%zu, off=%zu, fi=%p", ino, size, off, fi); |
|
207 |
|
208 // look up the inode |
|
209 if ((node = _simple_get_ino(fs, ino)) == NULL) |
|
210 EERROR(err = EINVAL, "bad inode"); |
|
211 |
|
212 // check that it's a dir |
|
213 if (node->mode_type != S_IFREG) |
|
214 EERROR(err = (node->mode_type == S_IFDIR ? EISDIR : EINVAL), "bad mode"); |
|
215 |
|
216 // seek past EOF? |
|
217 if (off >= strlen(node->data)) { |
|
218 // offset is out-of-file, so return EOF |
|
219 if ((err = fuse_reply_buf(req, NULL, 0))) |
|
220 EERROR(err, "fuse_reply_buf size=0"); |
|
221 |
|
222 } else { |
|
223 // reply with the requested file data |
|
224 if ((err = fuse_reply_buf(req, node->data + off, MIN(strlen(node->data) - off, size)))) |
|
225 EERROR(err, "fuse_reply_buf buf=%p + %zu, size=MIN(%zu, %zu)", node->data, off, strlen(node->data) - off, size); |
|
226 } |
|
227 |
|
228 // success |
|
229 return; |
|
230 |
|
231 error: |
|
232 if ((err = fuse_reply_err(req, err))) |
|
233 EWARNING(err, "fuse_reply_err"); |
|
234 } |
|
235 |
|
236 |
|
237 /* |
|
238 * Define our fuse_lowlevel_ops struct. |
|
239 */ |
|
240 static struct fuse_lowlevel_ops simple_ops = { |
|
241 .lookup = simple_lookup, |
|
242 |
|
243 .getattr = simple_getattr, |
|
244 |
|
245 .readlink = simple_readlink, |
|
246 |
|
247 .readdir = simple_readdir, |
|
248 |
|
249 .read = simple_read, |
|
250 }; |
|
251 |
|
252 struct fuse_lowlevel_ops *simple_init () { |
|
253 return &simple_ops; |
|
254 } |
|
255 |
|
256 struct simple_fs *simple_new (const struct simple_node *node_list) { |
|
257 struct simple_fs *fs = NULL; |
|
258 const struct simple_node *node; |
|
259 |
|
260 // generate |
|
261 if ((fs = calloc(1, sizeof(*fs))) == NULL) |
|
262 ERROR("calloc"); |
|
263 |
|
264 // remember node_list |
|
265 fs->inode_count = 0; |
|
266 fs->inode_table = node_list; |
|
267 |
|
268 // validate it |
|
269 for (node = fs->inode_table; node->inode; node++) { |
|
270 // update inode_count |
|
271 fs->inode_count++; |
|
272 |
|
273 // check that parent is valid |
|
274 assert(node->inode == fs->inode_count); |
|
275 assert(node->parent < node->inode); |
|
276 } |
|
277 |
|
278 // success |
|
279 return fs; |
|
280 |
|
281 error: |
|
282 return NULL; |
|
283 } |
|