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.
10 * gcc -Wall fuse_lo-plus.c `pkg-config fuse3 --cflags --libs` -o fuse_lo-plus
14 #define FUSE_USE_VERSION 30
20 #include <fuse_lowlevel.h>
32 /* Compat stuff. Doesn't make it work, just makes it compile. */
34 #warning fstatat(2) needed by this program
35 int fstatat(int dirfd
, const char *pathname
, struct stat
*buf
, int flags
)
42 #warning openat(2) needed by this program
43 int openat(int dirfd
, const char *pathname
, int flags
, ...)
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
)
58 #warning AT_EMPTY_PATH needed by this program
59 #define AT_EMPTY_PATH 0
61 #ifndef AT_SYMLINK_NOFOLLOW
62 #warning AT_SYMLINK_NOFOLLOW needed by this program
63 #define AT_SYMLINK_NOFOLLOW 0
66 #warning O_PATH needed by this program
70 #warning O_NOFOLLOW needed by this program
75 struct lo_inode
*next
;
76 struct lo_inode
*prev
;
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
;
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
)
118 res
= fstatat(lo_fd(req
, ino
), "", &buf
, AT_EMPTY_PATH
| AT_SYMLINK_NOFOLLOW
);
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
)
129 for (p
= lo
->root
.next
; p
!= &lo
->root
; p
= p
->next
) {
130 if (p
->ino
== st
->st_ino
&& p
->dev
== st
->st_dev
)
136 static int lo_do_lookup(fuse_req_t req
, fuse_ino_t parent
, const char *name
,
137 struct fuse_entry_param
*e
)
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
);
152 res
= fstatat(newfd
, "", &e
->attr
, AT_EMPTY_PATH
| AT_SYMLINK_NOFOLLOW
);
156 inode
= lo_find(lo_data(req
), &e
->attr
);
161 struct lo_inode
*prev
= &lo_data(req
)->root
;
162 struct lo_inode
*next
= prev
->next
;
164 inode
= calloc(1, sizeof(struct lo_inode
));
169 inode
->ino
= e
->attr
.st_ino
;
170 inode
->dev
= e
->attr
.st_dev
;
178 e
->ino
= (uintptr_t) inode
;
181 fprintf(stderr
, " %lli/%s -> %lli\n",
182 (unsigned long long) parent
, name
, (unsigned long long) e
->ino
);
193 static void lo_lookup(fuse_req_t req
, fuse_ino_t parent
, const char *name
)
195 struct fuse_entry_param e
;
198 err
= lo_do_lookup(req
, parent
, name
, &e
);
200 fuse_reply_err(req
, err
);
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
;
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
);
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
;
232 fuse_reply_none(req
);
235 static void lo_readlink(fuse_req_t req
, fuse_ino_t ino
)
237 char buf
[PATH_MAX
+ 1];
240 res
= readlinkat(lo_fd(req
, ino
), "", buf
, sizeof(buf
));
242 return (void) fuse_reply_err(req
, errno
);
244 if (res
== sizeof(buf
))
245 return (void) fuse_reply_err(req
, ENAMETOOLONG
);
249 fuse_reply_readlink(req
, buf
);
255 struct dirent
*entry
;
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
)
267 struct lo_dirp
*d
= calloc(1, sizeof(struct lo_dirp
));
271 d
->fd
= openat(lo_fd(req
, ino
), ".", O_RDONLY
);
275 d
->dp
= fdopendir(d
->fd
);
282 fi
->fh
= (uintptr_t) d
;
283 fuse_reply_open(req
, fi
);
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
);
308 buf
= calloc(size
, 1);
310 return (void) fuse_reply_err(req
, ENOMEM
);
312 if (offset
!= d
->offset
) {
313 seekdir(d
->dp
, offset
);
325 d
->entry
= readdir(d
->dp
);
327 if (errno
&& rem
== size
) {
334 nextoff
= telldir(d
->dp
);
336 struct fuse_entry_param e
;
338 err
= lo_do_lookup(req
, ino
, d
->entry
->d_name
, &e
);
342 entsize
= fuse_add_direntry_plus(req
, p
, rem
,
347 .st_ino
= d
->entry
->d_ino
,
348 .st_mode
= d
->entry
->d_type
<< 12,
350 entsize
= fuse_add_direntry(req
, p
, rem
,
364 fuse_reply_buf(req
, buf
, size
- rem
);
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
);
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
)
400 sprintf(buf
, "/proc/self/fd/%i", lo_fd(req
, ino
));
401 fd
= open(buf
, fi
->flags
& ~O_NOFOLLOW
);
403 return (void) fuse_reply_err(req
, errno
);
406 fuse_reply_open(req
, fi
);
409 static void lo_release(fuse_req_t req
, fuse_ino_t ino
, struct fuse_file_info
*fi
)
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
);
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
= {
434 .getattr
= lo_getattr
,
435 .readlink
= lo_readlink
,
436 .opendir
= lo_opendir
,
437 .readdir
= lo_readdir
,
438 .readdirplus
= lo_readdirplus
,
439 .releasedir
= lo_releasedir
,
441 .release
= lo_release
,
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),
455 int main(int argc
, char *argv
[])
457 struct fuse_args args
= FUSE_ARGS_INIT(argc
, argv
);
458 struct fuse_chan
*ch
;
461 struct lo_data lo
= { .debug
= 0 };
463 if (fuse_opt_parse(&args
, &lo
, lo_opts
, NULL
) == -1)
465 lo
.root
.next
= lo
.root
.prev
= &lo
.root
;
466 lo
.root
.fd
= open("/", O_PATH
);
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
);
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
);
487 fuse_opt_free_args(&args
);
489 while (lo
.root
.next
!= &lo
.root
)
490 lo_free(lo
.root
.next
);