compile fix for fuse_lo-plus
[fuse.git] / example / fuse_lo-plus.c
blobadbbbeecadc12d7add3a94e46823da24649195a5
1 /*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
5 This program can be distributed under the terms of the GNU GPL.
6 See the file COPYING.
7 */
9 /*
10 * gcc -Wall fuse_lo-plus.c `pkg-config fuse3 --cflags --libs` -o fuse_lo-plus
13 #define _GNU_SOURCE
14 #define FUSE_USE_VERSION 30
16 #ifdef HAVE_CONFIG_H
17 #include <config.h>
18 #endif
20 #include <fuse_lowlevel.h>
21 #include <unistd.h>
22 #include <stdlib.h>
23 #include <stdio.h>
24 #include <stddef.h>
25 #include <stdbool.h>
26 #include <string.h>
27 #include <dirent.h>
28 #include <assert.h>
29 #include <errno.h>
30 #include <err.h>
32 /* Compat stuff. Doesn't make it work, just makes it compile. */
33 #ifndef HAVE_FSTATAT
34 #warning fstatat(2) needed by this program
35 int fstatat(int dirfd, const char *pathname, struct stat *buf, int flags)
37 errno = ENOSYS;
38 return -1;
40 #endif
41 #ifndef HAVE_OPENAT
42 #warning openat(2) needed by this program
43 int openat(int dirfd, const char *pathname, int flags, ...)
45 errno = ENOSYS;
46 return -1;
48 #endif
49 #ifndef HAVE_READLINKAT
50 #warning readlinkat(2) needed by this program
51 ssize_t readlinkat(int dirfd, const char *pathname, char *buf, size_t bufsiz)
53 errno = ENOSYS;
54 return -1;
56 #endif
57 #ifndef AT_EMPTY_PATH
58 #warning AT_EMPTY_PATH needed by this program
59 #define AT_EMPTY_PATH 0
60 #endif
61 #ifndef AT_SYMLINK_NOFOLLOW
62 #warning AT_SYMLINK_NOFOLLOW needed by this program
63 #define AT_SYMLINK_NOFOLLOW 0
64 #endif
65 #ifndef O_PATH
66 #warning O_PATH needed by this program
67 #define O_PATH 0
68 #endif
69 #ifndef O_NOFOLLOW
70 #warning O_NOFOLLOW needed by this program
71 #define O_NOFOLLOW 0
72 #endif
74 struct lo_inode {
75 struct lo_inode *next;
76 struct lo_inode *prev;
77 int fd;
78 ino_t ino;
79 dev_t dev;
80 uint64_t nlookup;
83 struct lo_data {
84 int debug;
85 struct lo_inode root;
88 static struct lo_data *lo_data(fuse_req_t req)
90 return (struct lo_data *) fuse_req_userdata(req);
93 static struct lo_inode *lo_inode(fuse_req_t req, fuse_ino_t ino)
95 if (ino == FUSE_ROOT_ID)
96 return &lo_data(req)->root;
97 else
98 return (struct lo_inode *) (uintptr_t) ino;
101 static int lo_fd(fuse_req_t req, fuse_ino_t ino)
103 return lo_inode(req, ino)->fd;
106 static bool lo_debug(fuse_req_t req)
108 return lo_data(req)->debug != 0;
111 static void lo_getattr(fuse_req_t req, fuse_ino_t ino,
112 struct fuse_file_info *fi)
114 int res;
115 struct stat buf;
116 (void) fi;
118 res = fstatat(lo_fd(req, ino), "", &buf, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW);
119 if (res == -1)
120 return (void) fuse_reply_err(req, errno);
122 fuse_reply_attr(req, &buf, 1.0);
125 static struct lo_inode *lo_find(struct lo_data *lo, struct stat *st)
127 struct lo_inode *p;
129 for (p = lo->root.next; p != &lo->root; p = p->next) {
130 if (p->ino == st->st_ino && p->dev == st->st_dev)
131 return p;
133 return NULL;
136 static int lo_do_lookup(fuse_req_t req, fuse_ino_t parent, const char *name,
137 struct fuse_entry_param *e)
139 int newfd;
140 int res;
141 int saverr;
142 struct lo_inode *inode;
144 memset(e, 0, sizeof(*e));
145 e->attr_timeout = 1.0;
146 e->entry_timeout = 1.0;
148 newfd = openat(lo_fd(req, parent), name, O_PATH | O_NOFOLLOW);
149 if (newfd == -1)
150 goto out_err;
152 res = fstatat(newfd, "", &e->attr, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW);
153 if (res == -1)
154 goto out_err;
156 inode = lo_find(lo_data(req), &e->attr);
157 if (inode) {
158 close(newfd);
159 newfd = -1;
160 } else {
161 struct lo_inode *prev = &lo_data(req)->root;
162 struct lo_inode *next = prev->next;
163 saverr = ENOMEM;
164 inode = calloc(1, sizeof(struct lo_inode));
165 if (!inode)
166 goto out_err;
168 inode->fd = newfd;
169 inode->ino = e->attr.st_ino;
170 inode->dev = e->attr.st_dev;
172 next->prev = inode;
173 inode->next = next;
174 inode->prev = prev;
175 prev->next = inode;
177 inode->nlookup++;
178 e->ino = (uintptr_t) inode;
180 if (lo_debug(req))
181 fprintf(stderr, " %lli/%s -> %lli\n",
182 (unsigned long long) parent, name, (unsigned long long) e->ino);
184 return 0;
186 out_err:
187 saverr = errno;
188 if (newfd != -1)
189 close(newfd);
190 return saverr;
193 static void lo_lookup(fuse_req_t req, fuse_ino_t parent, const char *name)
195 struct fuse_entry_param e;
196 int err;
198 err = lo_do_lookup(req, parent, name, &e);
199 if (err)
200 fuse_reply_err(req, err);
201 else
202 fuse_reply_entry(req, &e);
205 static void lo_free(struct lo_inode *inode)
207 struct lo_inode *prev = inode->prev;
208 struct lo_inode *next = inode->next;
210 next->prev = prev;
211 prev->next = next;
212 close(inode->fd);
213 free(inode);
216 static void lo_forget(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup)
218 struct lo_inode *inode = lo_inode(req, ino);
220 if (lo_debug(req)) {
221 fprintf(stderr, " forget %lli %lli -%lli\n",
222 (unsigned long long) ino, (unsigned long long) inode->nlookup,
223 (unsigned long long) nlookup);
226 assert(inode->nlookup >= nlookup);
227 inode->nlookup -= nlookup;
229 if (!inode->nlookup)
230 lo_free(inode);
232 fuse_reply_none(req);
235 static void lo_readlink(fuse_req_t req, fuse_ino_t ino)
237 char buf[PATH_MAX + 1];
238 int res;
240 res = readlinkat(lo_fd(req, ino), "", buf, sizeof(buf));
241 if (res == -1)
242 return (void) fuse_reply_err(req, errno);
244 if (res == sizeof(buf))
245 return (void) fuse_reply_err(req, ENAMETOOLONG);
247 buf[res] = '\0';
249 fuse_reply_readlink(req, buf);
252 struct lo_dirp {
253 int fd;
254 DIR *dp;
255 struct dirent *entry;
256 off_t offset;
259 static struct lo_dirp *lo_dirp(struct fuse_file_info *fi)
261 return (struct lo_dirp *) (uintptr_t) fi->fh;
264 static void lo_opendir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
266 int error = ENOMEM;
267 struct lo_dirp *d = calloc(1, sizeof(struct lo_dirp));
268 if (d == NULL)
269 goto out_err;
271 d->fd = openat(lo_fd(req, ino), ".", O_RDONLY);
272 if (d->fd == -1)
273 goto out_errno;
275 d->dp = fdopendir(d->fd);
276 if (d->dp == NULL)
277 goto out_errno;
279 d->offset = 0;
280 d->entry = NULL;
282 fi->fh = (uintptr_t) d;
283 fuse_reply_open(req, fi);
284 return;
286 out_errno:
287 error = errno;
288 out_err:
289 if (d) {
290 if (d->fd != -1)
291 close(d->fd);
292 free(d);
294 fuse_reply_err(req, error);
297 static void lo_do_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
298 off_t offset, struct fuse_file_info *fi, int plus)
300 struct lo_dirp *d = lo_dirp(fi);
301 char *buf;
302 char *p;
303 size_t rem;
304 int err;
306 (void) ino;
308 buf = calloc(size, 1);
309 if (!buf)
310 return (void) fuse_reply_err(req, ENOMEM);
312 if (offset != d->offset) {
313 seekdir(d->dp, offset);
314 d->entry = NULL;
315 d->offset = offset;
317 p = buf;
318 rem = size;
319 while (1) {
320 size_t entsize;
321 off_t nextoff;
323 if (!d->entry) {
324 errno = 0;
325 d->entry = readdir(d->dp);
326 if (!d->entry) {
327 if (errno && rem == size) {
328 err = errno;
329 goto error;
331 break;
334 nextoff = telldir(d->dp);
335 if (plus) {
336 struct fuse_entry_param e;
338 err = lo_do_lookup(req, ino, d->entry->d_name, &e);
339 if (err)
340 goto error;
342 entsize = fuse_add_direntry_plus(req, p, rem,
343 d->entry->d_name,
344 &e, nextoff);
345 } else {
346 struct stat st = {
347 .st_ino = d->entry->d_ino,
348 .st_mode = d->entry->d_type << 12,
350 entsize = fuse_add_direntry(req, p, rem,
351 d->entry->d_name,
352 &st, nextoff);
354 if (entsize > rem)
355 break;
357 p += entsize;
358 rem -= entsize;
360 d->entry = NULL;
361 d->offset = nextoff;
364 fuse_reply_buf(req, buf, size - rem);
365 free(buf);
366 return;
368 error:
369 free(buf);
370 fuse_reply_err(req, err);
373 static void lo_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
374 off_t offset, struct fuse_file_info *fi)
376 lo_do_readdir(req, ino, size, offset, fi, 0);
379 static void lo_readdirplus(fuse_req_t req, fuse_ino_t ino, size_t size,
380 off_t offset, struct fuse_file_info *fi)
382 lo_do_readdir(req, ino, size, offset, fi, 1);
385 static void lo_releasedir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
387 struct lo_dirp *d = lo_dirp(fi);
388 (void) ino;
389 closedir(d->dp);
390 free(d);
391 fuse_reply_err(req, 0);
394 static void lo_open(fuse_req_t req, fuse_ino_t ino,
395 struct fuse_file_info *fi)
397 int fd;
398 char buf[64];
400 sprintf(buf, "/proc/self/fd/%i", lo_fd(req, ino));
401 fd = open(buf, fi->flags & ~O_NOFOLLOW);
402 if (fd == -1)
403 return (void) fuse_reply_err(req, errno);
405 fi->fh = fd;
406 fuse_reply_open(req, fi);
409 static void lo_release(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
411 (void) ino;
413 close(fi->fh);
414 fuse_reply_err(req, 0);
417 static void lo_read(fuse_req_t req, fuse_ino_t ino, size_t size,
418 off_t offset, struct fuse_file_info *fi)
420 struct fuse_bufvec buf = FUSE_BUFVEC_INIT(size);
422 (void) ino;
424 buf.buf[0].flags = FUSE_BUF_IS_FD | FUSE_BUF_FD_SEEK;
425 buf.buf[0].fd = fi->fh;
426 buf.buf[0].pos = offset;
428 fuse_reply_data(req, &buf, FUSE_BUF_SPLICE_MOVE);
431 static struct fuse_lowlevel_ops lo_oper = {
432 .lookup = lo_lookup,
433 .forget = lo_forget,
434 .getattr = lo_getattr,
435 .readlink = lo_readlink,
436 .opendir = lo_opendir,
437 .readdir = lo_readdir,
438 .readdirplus = lo_readdirplus,
439 .releasedir = lo_releasedir,
440 .open = lo_open,
441 .release = lo_release,
442 .read = lo_read,
445 #define LO_OPT(t, p, v) { t, offsetof(struct lo_data, p), v }
447 static const struct fuse_opt lo_opts[] = {
448 FUSE_OPT_KEY("debug", FUSE_OPT_KEY_KEEP),
449 FUSE_OPT_KEY("-d", FUSE_OPT_KEY_KEEP),
450 LO_OPT("debug", debug, 1),
451 LO_OPT("-d", debug, 1),
452 FUSE_OPT_END
455 int main(int argc, char *argv[])
457 struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
458 struct fuse_chan *ch;
459 char *mountpoint;
460 int ret = -1;
461 struct lo_data lo = { .debug = 0 };
463 if (fuse_opt_parse(&args, &lo, lo_opts, NULL) == -1)
464 exit(1);
465 lo.root.next = lo.root.prev = &lo.root;
466 lo.root.fd = open("/", O_PATH);
467 lo.root.nlookup = 2;
468 if (lo.root.fd == -1)
469 err(1, "open(\"/\", O_PATH)");
471 if (fuse_parse_cmdline(&args, &mountpoint, NULL, NULL) != -1 &&
472 (ch = fuse_mount(mountpoint, &args)) != NULL) {
473 struct fuse_session *se;
474 se = fuse_lowlevel_new(&args, &lo_oper, sizeof(lo_oper), &lo);
475 if (se != NULL) {
476 if (fuse_set_signal_handlers(se) != -1) {
477 fuse_session_add_chan(se, ch);
478 ret = fuse_session_loop(se);
479 fuse_remove_signal_handlers(se);
480 fuse_session_remove_chan(ch);
482 fuse_session_destroy(se);
484 fuse_unmount(mountpoint, ch);
485 free(mountpoint);
487 fuse_opt_free_args(&args);
489 while (lo.root.next != &lo.root)
490 lo_free(lo.root.next);
492 return ret ? 1 : 0;