xref: /third_party/libfuse/example/passthrough.c (revision 6881f68f)
1/*
2  FUSE: Filesystem in Userspace
3  Copyright (C) 2001-2007  Miklos Szeredi <miklos@szeredi.hu>
4  Copyright (C) 2011       Sebastian Pipping <sebastian@pipping.org>
5
6  This program can be distributed under the terms of the GNU GPLv2.
7  See the file COPYING.
8*/
9
10/** @file
11 *
12 * This file system mirrors the existing file system hierarchy of the
13 * system, starting at the root file system. This is implemented by
14 * just "passing through" all requests to the corresponding user-space
15 * libc functions. Its performance is terrible.
16 *
17 * Compile with
18 *
19 *     gcc -Wall passthrough.c `pkg-config fuse3 --cflags --libs` -o passthrough
20 *
21 * ## Source code ##
22 * \include passthrough.c
23 */
24
25
26#define FUSE_USE_VERSION 31
27
28#define _GNU_SOURCE
29
30#ifdef linux
31/* For pread()/pwrite()/utimensat() */
32#define _XOPEN_SOURCE 700
33#endif
34
35#include <fuse.h>
36#include <stdio.h>
37#include <string.h>
38#include <unistd.h>
39#include <fcntl.h>
40#include <sys/stat.h>
41#include <dirent.h>
42#include <errno.h>
43#ifdef __FreeBSD__
44#include <sys/socket.h>
45#include <sys/un.h>
46#endif
47#include <sys/time.h>
48#ifdef HAVE_SETXATTR
49#include <sys/xattr.h>
50#endif
51
52#include "passthrough_helpers.h"
53
54static int fill_dir_plus = 0;
55
56static void *xmp_init(struct fuse_conn_info *conn,
57		      struct fuse_config *cfg)
58{
59	(void) conn;
60	cfg->use_ino = 1;
61
62	/* Pick up changes from lower filesystem right away. This is
63	   also necessary for better hardlink support. When the kernel
64	   calls the unlink() handler, it does not know the inode of
65	   the to-be-removed entry and can therefore not invalidate
66	   the cache of the associated inode - resulting in an
67	   incorrect st_nlink value being reported for any remaining
68	   hardlinks to this inode. */
69	cfg->entry_timeout = 0;
70	cfg->attr_timeout = 0;
71	cfg->negative_timeout = 0;
72
73	return NULL;
74}
75
76static int xmp_getattr(const char *path, struct stat *stbuf,
77		       struct fuse_file_info *fi)
78{
79	(void) fi;
80	int res;
81
82	res = lstat(path, stbuf);
83	if (res == -1)
84		return -errno;
85
86	return 0;
87}
88
89static int xmp_access(const char *path, int mask)
90{
91	int res;
92
93	res = access(path, mask);
94	if (res == -1)
95		return -errno;
96
97	return 0;
98}
99
100static int xmp_readlink(const char *path, char *buf, size_t size)
101{
102	int res;
103
104	res = readlink(path, buf, size - 1);
105	if (res == -1)
106		return -errno;
107
108	buf[res] = '\0';
109	return 0;
110}
111
112
113static int xmp_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
114		       off_t offset, struct fuse_file_info *fi,
115		       enum fuse_readdir_flags flags)
116{
117	DIR *dp;
118	struct dirent *de;
119
120	(void) offset;
121	(void) fi;
122	(void) flags;
123
124	dp = opendir(path);
125	if (dp == NULL)
126		return -errno;
127
128	while ((de = readdir(dp)) != NULL) {
129		struct stat st;
130		memset(&st, 0, sizeof(st));
131		st.st_ino = de->d_ino;
132		st.st_mode = de->d_type << 12;
133		if (filler(buf, de->d_name, &st, 0, fill_dir_plus))
134			break;
135	}
136
137	closedir(dp);
138	return 0;
139}
140
141static int xmp_mknod(const char *path, mode_t mode, dev_t rdev)
142{
143	int res;
144
145	res = mknod_wrapper(AT_FDCWD, path, NULL, mode, rdev);
146	if (res == -1)
147		return -errno;
148
149	return 0;
150}
151
152static int xmp_mkdir(const char *path, mode_t mode)
153{
154	int res;
155
156	res = mkdir(path, mode);
157	if (res == -1)
158		return -errno;
159
160	return 0;
161}
162
163static int xmp_unlink(const char *path)
164{
165	int res;
166
167	res = unlink(path);
168	if (res == -1)
169		return -errno;
170
171	return 0;
172}
173
174static int xmp_rmdir(const char *path)
175{
176	int res;
177
178	res = rmdir(path);
179	if (res == -1)
180		return -errno;
181
182	return 0;
183}
184
185static int xmp_symlink(const char *from, const char *to)
186{
187	int res;
188
189	res = symlink(from, to);
190	if (res == -1)
191		return -errno;
192
193	return 0;
194}
195
196static int xmp_rename(const char *from, const char *to, unsigned int flags)
197{
198	int res;
199
200	if (flags)
201		return -EINVAL;
202
203	res = rename(from, to);
204	if (res == -1)
205		return -errno;
206
207	return 0;
208}
209
210static int xmp_link(const char *from, const char *to)
211{
212	int res;
213
214	res = link(from, to);
215	if (res == -1)
216		return -errno;
217
218	return 0;
219}
220
221static int xmp_chmod(const char *path, mode_t mode,
222		     struct fuse_file_info *fi)
223{
224	(void) fi;
225	int res;
226
227	res = chmod(path, mode);
228	if (res == -1)
229		return -errno;
230
231	return 0;
232}
233
234static int xmp_chown(const char *path, uid_t uid, gid_t gid,
235		     struct fuse_file_info *fi)
236{
237	(void) fi;
238	int res;
239
240	res = lchown(path, uid, gid);
241	if (res == -1)
242		return -errno;
243
244	return 0;
245}
246
247static int xmp_truncate(const char *path, off_t size,
248			struct fuse_file_info *fi)
249{
250	int res;
251
252	if (fi != NULL)
253		res = ftruncate(fi->fh, size);
254	else
255		res = truncate(path, size);
256	if (res == -1)
257		return -errno;
258
259	return 0;
260}
261
262#ifdef HAVE_UTIMENSAT
263static int xmp_utimens(const char *path, const struct timespec ts[2],
264		       struct fuse_file_info *fi)
265{
266	(void) fi;
267	int res;
268
269	/* don't use utime/utimes since they follow symlinks */
270	res = utimensat(0, path, ts, AT_SYMLINK_NOFOLLOW);
271	if (res == -1)
272		return -errno;
273
274	return 0;
275}
276#endif
277
278static int xmp_create(const char *path, mode_t mode,
279		      struct fuse_file_info *fi)
280{
281	int res;
282
283	res = open(path, fi->flags, mode);
284	if (res == -1)
285		return -errno;
286
287	fi->fh = res;
288	return 0;
289}
290
291static int xmp_open(const char *path, struct fuse_file_info *fi)
292{
293	int res;
294
295	res = open(path, fi->flags);
296	if (res == -1)
297		return -errno;
298
299	fi->fh = res;
300	return 0;
301}
302
303static int xmp_read(const char *path, char *buf, size_t size, off_t offset,
304		    struct fuse_file_info *fi)
305{
306	int fd;
307	int res;
308
309	if(fi == NULL)
310		fd = open(path, O_RDONLY);
311	else
312		fd = fi->fh;
313
314	if (fd == -1)
315		return -errno;
316
317	res = pread(fd, buf, size, offset);
318	if (res == -1)
319		res = -errno;
320
321	if(fi == NULL)
322		close(fd);
323	return res;
324}
325
326static int xmp_write(const char *path, const char *buf, size_t size,
327		     off_t offset, struct fuse_file_info *fi)
328{
329	int fd;
330	int res;
331
332	(void) fi;
333	if(fi == NULL)
334		fd = open(path, O_WRONLY);
335	else
336		fd = fi->fh;
337
338	if (fd == -1)
339		return -errno;
340
341	res = pwrite(fd, buf, size, offset);
342	if (res == -1)
343		res = -errno;
344
345	if(fi == NULL)
346		close(fd);
347	return res;
348}
349
350static int xmp_statfs(const char *path, struct statvfs *stbuf)
351{
352	int res;
353
354	res = statvfs(path, stbuf);
355	if (res == -1)
356		return -errno;
357
358	return 0;
359}
360
361static int xmp_release(const char *path, struct fuse_file_info *fi)
362{
363	(void) path;
364	close(fi->fh);
365	return 0;
366}
367
368static int xmp_fsync(const char *path, int isdatasync,
369		     struct fuse_file_info *fi)
370{
371	/* Just a stub.	 This method is optional and can safely be left
372	   unimplemented */
373
374	(void) path;
375	(void) isdatasync;
376	(void) fi;
377	return 0;
378}
379
380#ifdef HAVE_POSIX_FALLOCATE
381static int xmp_fallocate(const char *path, int mode,
382			off_t offset, off_t length, struct fuse_file_info *fi)
383{
384	int fd;
385	int res;
386
387	(void) fi;
388
389	if (mode)
390		return -EOPNOTSUPP;
391
392	if(fi == NULL)
393		fd = open(path, O_WRONLY);
394	else
395		fd = fi->fh;
396
397	if (fd == -1)
398		return -errno;
399
400	res = -posix_fallocate(fd, offset, length);
401
402	if(fi == NULL)
403		close(fd);
404	return res;
405}
406#endif
407
408#ifdef HAVE_SETXATTR
409/* xattr operations are optional and can safely be left unimplemented */
410static int xmp_setxattr(const char *path, const char *name, const char *value,
411			size_t size, int flags)
412{
413	int res = lsetxattr(path, name, value, size, flags);
414	if (res == -1)
415		return -errno;
416	return 0;
417}
418
419static int xmp_getxattr(const char *path, const char *name, char *value,
420			size_t size)
421{
422	int res = lgetxattr(path, name, value, size);
423	if (res == -1)
424		return -errno;
425	return res;
426}
427
428static int xmp_listxattr(const char *path, char *list, size_t size)
429{
430	int res = llistxattr(path, list, size);
431	if (res == -1)
432		return -errno;
433	return res;
434}
435
436static int xmp_removexattr(const char *path, const char *name)
437{
438	int res = lremovexattr(path, name);
439	if (res == -1)
440		return -errno;
441	return 0;
442}
443#endif /* HAVE_SETXATTR */
444
445#ifdef HAVE_COPY_FILE_RANGE
446static ssize_t xmp_copy_file_range(const char *path_in,
447				   struct fuse_file_info *fi_in,
448				   off_t offset_in, const char *path_out,
449				   struct fuse_file_info *fi_out,
450				   off_t offset_out, size_t len, int flags)
451{
452	int fd_in, fd_out;
453	ssize_t res;
454
455	if(fi_in == NULL)
456		fd_in = open(path_in, O_RDONLY);
457	else
458		fd_in = fi_in->fh;
459
460	if (fd_in == -1)
461		return -errno;
462
463	if(fi_out == NULL)
464		fd_out = open(path_out, O_WRONLY);
465	else
466		fd_out = fi_out->fh;
467
468	if (fd_out == -1) {
469		close(fd_in);
470		return -errno;
471	}
472
473	res = copy_file_range(fd_in, &offset_in, fd_out, &offset_out, len,
474			      flags);
475	if (res == -1)
476		res = -errno;
477
478	if (fi_out == NULL)
479		close(fd_out);
480	if (fi_in == NULL)
481		close(fd_in);
482
483	return res;
484}
485#endif
486
487static off_t xmp_lseek(const char *path, off_t off, int whence, struct fuse_file_info *fi)
488{
489	int fd;
490	off_t res;
491
492	if (fi == NULL)
493		fd = open(path, O_RDONLY);
494	else
495		fd = fi->fh;
496
497	if (fd == -1)
498		return -errno;
499
500	res = lseek(fd, off, whence);
501	if (res == -1)
502		res = -errno;
503
504	if (fi == NULL)
505		close(fd);
506	return res;
507}
508
509static const struct fuse_operations xmp_oper = {
510	.init           = xmp_init,
511	.getattr	= xmp_getattr,
512	.access		= xmp_access,
513	.readlink	= xmp_readlink,
514	.readdir	= xmp_readdir,
515	.mknod		= xmp_mknod,
516	.mkdir		= xmp_mkdir,
517	.symlink	= xmp_symlink,
518	.unlink		= xmp_unlink,
519	.rmdir		= xmp_rmdir,
520	.rename		= xmp_rename,
521	.link		= xmp_link,
522	.chmod		= xmp_chmod,
523	.chown		= xmp_chown,
524	.truncate	= xmp_truncate,
525#ifdef HAVE_UTIMENSAT
526	.utimens	= xmp_utimens,
527#endif
528	.open		= xmp_open,
529	.create 	= xmp_create,
530	.read		= xmp_read,
531	.write		= xmp_write,
532	.statfs		= xmp_statfs,
533	.release	= xmp_release,
534	.fsync		= xmp_fsync,
535#ifdef HAVE_POSIX_FALLOCATE
536	.fallocate	= xmp_fallocate,
537#endif
538#ifdef HAVE_SETXATTR
539	.setxattr	= xmp_setxattr,
540	.getxattr	= xmp_getxattr,
541	.listxattr	= xmp_listxattr,
542	.removexattr	= xmp_removexattr,
543#endif
544#ifdef HAVE_COPY_FILE_RANGE
545	.copy_file_range = xmp_copy_file_range,
546#endif
547	.lseek		= xmp_lseek,
548};
549
550int main(int argc, char *argv[])
551{
552	enum { MAX_ARGS = 10 };
553	int i,new_argc;
554	char *new_argv[MAX_ARGS];
555
556	umask(0);
557			/* Process the "--plus" option apart */
558	for (i=0, new_argc=0; (i<argc) && (new_argc<MAX_ARGS); i++) {
559		if (!strcmp(argv[i], "--plus")) {
560			fill_dir_plus = FUSE_FILL_DIR_PLUS;
561		} else {
562			new_argv[new_argc++] = argv[i];
563		}
564	}
565	return fuse_main(new_argc, new_argv, &xmp_oper, NULL);
566}
567