1 #include <string.h> |
|
2 #include <errno.h> |
|
3 #include <stdlib.h> |
|
4 |
|
5 #include <event2/event.h> |
|
6 #include <fuse/fuse_opt.h> |
|
7 |
|
8 #include "lib/log.h" |
|
9 #include "lib/math.h" |
|
10 #include "lib/signals.h" |
|
11 #include "evfuse.h" |
|
12 #include "dirbuf.h" |
|
13 |
|
14 const char *file_name = "hello"; |
|
15 const char *file_data = "Hello World\n"; |
|
16 |
|
17 static struct hello { |
|
18 struct event_base *ev_base; |
|
19 |
|
20 struct signals *signals; |
|
21 |
|
22 struct evfuse *ev_fuse; |
|
23 |
|
24 } ctx; |
|
25 |
|
26 void hello_init (void *userdata, struct fuse_conn_info *conn) { |
|
27 INFO("[hello.init] userdata=%p, conn=%p", userdata, conn); |
|
28 } |
|
29 |
|
30 void hello_destroy (void *userdata) { |
|
31 INFO("[hello.destroy] userdata=%p", userdata); |
|
32 } |
|
33 |
|
34 void hello_lookup (fuse_req_t req, fuse_ino_t parent, const char *name) { |
|
35 struct fuse_entry_param e; |
|
36 |
|
37 INFO("[hello.lookup] (uid=%d, pid=%d) parent=%lu name=%s", fuse_req_ctx(req)->uid, fuse_req_ctx(req)->pid, parent, name); |
|
38 |
|
39 // the world is flat |
|
40 if (parent != 1 || strcmp(name, file_name)) { |
|
41 fuse_reply_err(req, ENOENT); |
|
42 |
|
43 return; |
|
44 } |
|
45 |
|
46 // set up the entry |
|
47 memset(&e, 0, sizeof(e)); |
|
48 e.ino = 2; |
|
49 e.attr_timeout = 1.0; |
|
50 e.entry_timeout = 1.0; |
|
51 e.attr.st_mode = S_IFREG | 0444; |
|
52 e.attr.st_nlink = 1; |
|
53 e.attr.st_size = strlen(file_data); |
|
54 |
|
55 // reply |
|
56 fuse_reply_entry(req, &e); |
|
57 } |
|
58 |
|
59 void hello_getattr (fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) { |
|
60 struct stat stbuf; |
|
61 |
|
62 INFO("[hello.getattr] (uid=%d, pid=%d) ino=%lu, fi=%p", fuse_req_ctx(req)->uid, fuse_req_ctx(req)->pid, ino, fi); |
|
63 |
|
64 memset(&stbuf, 0, sizeof(stbuf)); |
|
65 |
|
66 // the root dir, or the file? |
|
67 if (ino == 1) { |
|
68 stbuf.st_mode = S_IFDIR | 0555; |
|
69 stbuf.st_nlink = 2; |
|
70 |
|
71 } else if (ino == 2) { |
|
72 stbuf.st_mode = S_IFREG | 0444; |
|
73 stbuf.st_nlink = 1; |
|
74 stbuf.st_size = strlen(file_data); |
|
75 |
|
76 } else { |
|
77 fuse_reply_err(req, ENOENT); |
|
78 return; |
|
79 } |
|
80 |
|
81 // reply |
|
82 fuse_reply_attr(req, &stbuf, 1.0); |
|
83 } |
|
84 |
|
85 |
|
86 void hello_readdir (fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi) { |
|
87 int err = 0; |
|
88 struct dirbuf buf; |
|
89 |
|
90 INFO("[hello.readdir] ino=%lu, size=%zu, off=%zu, fi=%p", ino, size, off, fi); |
|
91 |
|
92 // there exists only one dir |
|
93 if (ino != 1) { |
|
94 fuse_reply_err(req, ENOTDIR); |
|
95 return; |
|
96 } |
|
97 |
|
98 // fill in the dirbuf |
|
99 if (dirbuf_init(&buf, size, off)) |
|
100 ERROR("failed to init dirbuf"); |
|
101 |
|
102 err = dirbuf_add(req, &buf, 0, 1, ".", 1, S_IFDIR ) |
|
103 || dirbuf_add(req, &buf, 1, 2, "..", 1, S_IFDIR ) |
|
104 || dirbuf_add(req, &buf, 2, 3, file_name, 2, S_IFREG ); |
|
105 |
|
106 if (err < 0) |
|
107 ERROR("failed to add dirents to buf"); |
|
108 |
|
109 // send it |
|
110 if ((err = -dirbuf_done(req, &buf))) |
|
111 EERROR(-err, "failed to send buf"); |
|
112 |
|
113 // success |
|
114 return; |
|
115 |
|
116 error: |
|
117 if ((err = fuse_reply_err(req, err ? err : EIO))) |
|
118 EWARNING(err, "failed to send error reply"); |
|
119 } |
|
120 |
|
121 void hello_open (fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) { |
|
122 int err = 0; |
|
123 |
|
124 INFO("[hello.open] ino=%lu, fi=%p, fi->flags=%08X", ino, fi, fi->flags); |
|
125 |
|
126 if (ino != 2) { |
|
127 // must open our only file, not the dir |
|
128 fuse_reply_err(req, ino == 1 ? EISDIR : ENOENT); |
|
129 return; |
|
130 |
|
131 } else if ((fi->flags & 0x03) != O_RDONLY) { |
|
132 // "permission denied" |
|
133 fuse_reply_err(req, EACCES); |
|
134 return; |
|
135 } |
|
136 |
|
137 // XXX: update fi stuff? |
|
138 |
|
139 // open it! |
|
140 if ((err = fuse_reply_open(req, fi))) |
|
141 EERROR(err, "fuse_reply_open"); |
|
142 |
|
143 // success |
|
144 return; |
|
145 |
|
146 error: |
|
147 if ((err = fuse_reply_err(req, err ? err : EIO))) |
|
148 EWARNING(err, "failed to send error reply"); |
|
149 } |
|
150 |
|
151 void hello_read (fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi) { |
|
152 int err = 0; |
|
153 |
|
154 // fi is unused |
|
155 (void) fi; |
|
156 |
|
157 INFO("[hello.read] ino=%lu, size=%zu, off=%zu, fi=%p", ino, size, off, fi); |
|
158 |
|
159 if (ino != 2) { |
|
160 // EEK! |
|
161 FATAL("wrong inode"); |
|
162 } |
|
163 |
|
164 if (off >= strlen(file_data)) { |
|
165 // offset is out-of-file, so return EOF |
|
166 err = fuse_reply_buf(req, NULL, 0); |
|
167 |
|
168 } else { |
|
169 // reply with the requested file data |
|
170 err = fuse_reply_buf(req, file_data + off, MIN(strlen(file_data) - off, size)); |
|
171 } |
|
172 |
|
173 // reply |
|
174 if (err) |
|
175 PERROR("fuse_reply_buf"); |
|
176 |
|
177 // success |
|
178 return; |
|
179 |
|
180 error: |
|
181 if ((err = fuse_reply_err(req, err ? err : EIO))) |
|
182 EWARNING(err, "failed to send error reply"); |
|
183 } |
|
184 |
|
185 void hello_getxattr (fuse_req_t req, fuse_ino_t ino, const char *name, size_t size) { |
|
186 INFO("[hello.getxattr] ino=%lu, name=`%s', size=%zu", ino, name, size); |
|
187 |
|
188 fuse_reply_err(req, ENOSYS); |
|
189 } |
|
190 |
|
191 struct fuse_lowlevel_ops hello_llops = { |
|
192 .init = &hello_init, |
|
193 .destroy = &hello_destroy, |
|
194 |
|
195 .lookup = &hello_lookup, |
|
196 .getattr = &hello_getattr, |
|
197 |
|
198 .open = &hello_open, |
|
199 |
|
200 .read = &hello_read, |
|
201 |
|
202 .readdir = &hello_readdir, |
|
203 |
|
204 .getxattr = hello_getxattr, |
|
205 }; |
|
206 |
|
207 |
|
208 int main (int argc, char **argv) { |
|
209 struct fuse_args fuse_args = FUSE_ARGS_INIT(argc, argv); |
|
210 |
|
211 // zero |
|
212 memset(&ctx, 0, sizeof(ctx)); |
|
213 |
|
214 // init libevent |
|
215 if ((ctx.ev_base = event_base_new()) == NULL) |
|
216 ERROR("event_base_new"); |
|
217 |
|
218 // setup signals |
|
219 if ((ctx.signals = signals_default(ctx.ev_base)) == NULL) |
|
220 ERROR("signals_default"); |
|
221 |
|
222 // open fuse |
|
223 if ((ctx.ev_fuse = evfuse_new(ctx.ev_base, &fuse_args, &hello_llops, &ctx)) == NULL) |
|
224 ERROR("evfuse_new"); |
|
225 |
|
226 // run libevent |
|
227 INFO("running libevent loop"); |
|
228 |
|
229 if (event_base_dispatch(ctx.ev_base)) |
|
230 PERROR("event_base_dispatch"); |
|
231 |
|
232 // clean shutdown |
|
233 |
|
234 error : |
|
235 // cleanup |
|
236 if (ctx.ev_fuse) |
|
237 evfuse_free(ctx.ev_fuse); |
|
238 |
|
239 if (ctx.signals) |
|
240 signals_free(ctx.signals); |
|
241 |
|
242 if (ctx.ev_base) |
|
243 event_base_free(ctx.ev_base); |
|
244 |
|
245 fuse_opt_free_args(&fuse_args); |
|
246 } |
|
247 |
|