• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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. This implementation is a little more sophisticated
16  * than the one in passthrough.c, so performance is not quite as bad.
17  *
18  * Compile with:
19  *
20  *     gcc -Wall passthrough_fh.c `pkg-config fuse3 --cflags --libs` -lulockmgr -o passthrough_fh
21  *
22  * ## Source code ##
23  * \include passthrough_fh.c
24  */
25 
26 #define FUSE_USE_VERSION 31
27 
28 #define _GNU_SOURCE
29 
30 #include <fuse.h>
31 
32 #ifdef HAVE_LIBULOCKMGR
33 #include <ulockmgr.h>
34 #endif
35 
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <unistd.h>
40 #include <fcntl.h>
41 #include <sys/stat.h>
42 #include <dirent.h>
43 #include <errno.h>
44 #include <sys/time.h>
45 #ifdef HAVE_SETXATTR
46 #include <sys/xattr.h>
47 #endif
48 #include <sys/file.h> /* flock(2) */
49 
xmp_init(struct fuse_conn_info * conn,struct fuse_config * cfg)50 static void *xmp_init(struct fuse_conn_info *conn,
51 		      struct fuse_config *cfg)
52 {
53 	(void) conn;
54 	cfg->use_ino = 1;
55 	cfg->nullpath_ok = 1;
56 
57 	/* Pick up changes from lower filesystem right away. This is
58 	   also necessary for better hardlink support. When the kernel
59 	   calls the unlink() handler, it does not know the inode of
60 	   the to-be-removed entry and can therefore not invalidate
61 	   the cache of the associated inode - resulting in an
62 	   incorrect st_nlink value being reported for any remaining
63 	   hardlinks to this inode. */
64 	cfg->entry_timeout = 0;
65 	cfg->attr_timeout = 0;
66 	cfg->negative_timeout = 0;
67 
68 	return NULL;
69 }
70 
xmp_getattr(const char * path,struct stat * stbuf,struct fuse_file_info * fi)71 static int xmp_getattr(const char *path, struct stat *stbuf,
72 			struct fuse_file_info *fi)
73 {
74 	int res;
75 
76 	(void) path;
77 
78 	if(fi)
79 		res = fstat(fi->fh, stbuf);
80 	else
81 		res = lstat(path, stbuf);
82 	if (res == -1)
83 		return -errno;
84 
85 	return 0;
86 }
87 
xmp_access(const char * path,int mask)88 static int xmp_access(const char *path, int mask)
89 {
90 	int res;
91 
92 	res = access(path, mask);
93 	if (res == -1)
94 		return -errno;
95 
96 	return 0;
97 }
98 
xmp_readlink(const char * path,char * buf,size_t size)99 static int xmp_readlink(const char *path, char *buf, size_t size)
100 {
101 	int res;
102 
103 	res = readlink(path, buf, size - 1);
104 	if (res == -1)
105 		return -errno;
106 
107 	buf[res] = '\0';
108 	return 0;
109 }
110 
111 struct xmp_dirp {
112 	DIR *dp;
113 	struct dirent *entry;
114 	off_t offset;
115 };
116 
xmp_opendir(const char * path,struct fuse_file_info * fi)117 static int xmp_opendir(const char *path, struct fuse_file_info *fi)
118 {
119 	int res;
120 	struct xmp_dirp *d = malloc(sizeof(struct xmp_dirp));
121 	if (d == NULL)
122 		return -ENOMEM;
123 
124 	d->dp = opendir(path);
125 	if (d->dp == NULL) {
126 		res = -errno;
127 		free(d);
128 		return res;
129 	}
130 	d->offset = 0;
131 	d->entry = NULL;
132 
133 	fi->fh = (unsigned long) d;
134 	return 0;
135 }
136 
get_dirp(struct fuse_file_info * fi)137 static inline struct xmp_dirp *get_dirp(struct fuse_file_info *fi)
138 {
139 	return (struct xmp_dirp *) (uintptr_t) fi->fh;
140 }
141 
xmp_readdir(const char * path,void * buf,fuse_fill_dir_t filler,off_t offset,struct fuse_file_info * fi,enum fuse_readdir_flags flags)142 static int xmp_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
143 		       off_t offset, struct fuse_file_info *fi,
144 		       enum fuse_readdir_flags flags)
145 {
146 	struct xmp_dirp *d = get_dirp(fi);
147 
148 	(void) path;
149 	if (offset != d->offset) {
150 #ifndef __FreeBSD__
151 		seekdir(d->dp, offset);
152 #else
153 		/* Subtract the one that we add when calling
154 		   telldir() below */
155 		seekdir(d->dp, offset-1);
156 #endif
157 		d->entry = NULL;
158 		d->offset = offset;
159 	}
160 	while (1) {
161 		struct stat st;
162 		off_t nextoff;
163 		enum fuse_fill_dir_flags fill_flags = 0;
164 
165 		if (!d->entry) {
166 			d->entry = readdir(d->dp);
167 			if (!d->entry)
168 				break;
169 		}
170 #ifdef HAVE_FSTATAT
171 		if (flags & FUSE_READDIR_PLUS) {
172 			int res;
173 
174 			res = fstatat(dirfd(d->dp), d->entry->d_name, &st,
175 				      AT_SYMLINK_NOFOLLOW);
176 			if (res != -1)
177 				fill_flags |= FUSE_FILL_DIR_PLUS;
178 		}
179 #endif
180 		if (!(fill_flags & FUSE_FILL_DIR_PLUS)) {
181 			memset(&st, 0, sizeof(st));
182 			st.st_ino = d->entry->d_ino;
183 			st.st_mode = d->entry->d_type << 12;
184 		}
185 		nextoff = telldir(d->dp);
186 #ifdef __FreeBSD__
187 		/* Under FreeBSD, telldir() may return 0 the first time
188 		   it is called. But for libfuse, an offset of zero
189 		   means that offsets are not supported, so we shift
190 		   everything by one. */
191 		nextoff++;
192 #endif
193 		if (filler(buf, d->entry->d_name, &st, nextoff, fill_flags))
194 			break;
195 
196 		d->entry = NULL;
197 		d->offset = nextoff;
198 	}
199 
200 	return 0;
201 }
202 
xmp_releasedir(const char * path,struct fuse_file_info * fi)203 static int xmp_releasedir(const char *path, struct fuse_file_info *fi)
204 {
205 	struct xmp_dirp *d = get_dirp(fi);
206 	(void) path;
207 	closedir(d->dp);
208 	free(d);
209 	return 0;
210 }
211 
xmp_mknod(const char * path,mode_t mode,dev_t rdev)212 static int xmp_mknod(const char *path, mode_t mode, dev_t rdev)
213 {
214 	int res;
215 
216 	if (S_ISFIFO(mode))
217 		res = mkfifo(path, mode);
218 	else
219 		res = mknod(path, mode, rdev);
220 	if (res == -1)
221 		return -errno;
222 
223 	return 0;
224 }
225 
xmp_mkdir(const char * path,mode_t mode)226 static int xmp_mkdir(const char *path, mode_t mode)
227 {
228 	int res;
229 
230 	res = mkdir(path, mode);
231 	if (res == -1)
232 		return -errno;
233 
234 	return 0;
235 }
236 
xmp_unlink(const char * path)237 static int xmp_unlink(const char *path)
238 {
239 	int res;
240 
241 	res = unlink(path);
242 	if (res == -1)
243 		return -errno;
244 
245 	return 0;
246 }
247 
xmp_rmdir(const char * path)248 static int xmp_rmdir(const char *path)
249 {
250 	int res;
251 
252 	res = rmdir(path);
253 	if (res == -1)
254 		return -errno;
255 
256 	return 0;
257 }
258 
xmp_symlink(const char * from,const char * to)259 static int xmp_symlink(const char *from, const char *to)
260 {
261 	int res;
262 
263 	res = symlink(from, to);
264 	if (res == -1)
265 		return -errno;
266 
267 	return 0;
268 }
269 
xmp_rename(const char * from,const char * to,unsigned int flags)270 static int xmp_rename(const char *from, const char *to, unsigned int flags)
271 {
272 	int res;
273 
274 	/* When we have renameat2() in libc, then we can implement flags */
275 	if (flags)
276 		return -EINVAL;
277 
278 	res = rename(from, to);
279 	if (res == -1)
280 		return -errno;
281 
282 	return 0;
283 }
284 
xmp_link(const char * from,const char * to)285 static int xmp_link(const char *from, const char *to)
286 {
287 	int res;
288 
289 	res = link(from, to);
290 	if (res == -1)
291 		return -errno;
292 
293 	return 0;
294 }
295 
xmp_chmod(const char * path,mode_t mode,struct fuse_file_info * fi)296 static int xmp_chmod(const char *path, mode_t mode,
297 		     struct fuse_file_info *fi)
298 {
299 	int res;
300 
301 	if(fi)
302 		res = fchmod(fi->fh, mode);
303 	else
304 		res = chmod(path, mode);
305 	if (res == -1)
306 		return -errno;
307 
308 	return 0;
309 }
310 
xmp_chown(const char * path,uid_t uid,gid_t gid,struct fuse_file_info * fi)311 static int xmp_chown(const char *path, uid_t uid, gid_t gid,
312 		     struct fuse_file_info *fi)
313 {
314 	int res;
315 
316 	if (fi)
317 		res = fchown(fi->fh, uid, gid);
318 	else
319 		res = lchown(path, uid, gid);
320 	if (res == -1)
321 		return -errno;
322 
323 	return 0;
324 }
325 
xmp_truncate(const char * path,off_t size,struct fuse_file_info * fi)326 static int xmp_truncate(const char *path, off_t size,
327 			 struct fuse_file_info *fi)
328 {
329 	int res;
330 
331 	if(fi)
332 		res = ftruncate(fi->fh, size);
333 	else
334 		res = truncate(path, size);
335 
336 	if (res == -1)
337 		return -errno;
338 
339 	return 0;
340 }
341 
342 #ifdef HAVE_UTIMENSAT
xmp_utimens(const char * path,const struct timespec ts[2],struct fuse_file_info * fi)343 static int xmp_utimens(const char *path, const struct timespec ts[2],
344 		       struct fuse_file_info *fi)
345 {
346 	int res;
347 
348 	/* don't use utime/utimes since they follow symlinks */
349 	if (fi)
350 		res = futimens(fi->fh, ts);
351 	else
352 		res = utimensat(0, path, ts, AT_SYMLINK_NOFOLLOW);
353 	if (res == -1)
354 		return -errno;
355 
356 	return 0;
357 }
358 #endif
359 
xmp_create(const char * path,mode_t mode,struct fuse_file_info * fi)360 static int xmp_create(const char *path, mode_t mode, struct fuse_file_info *fi)
361 {
362 	int fd;
363 
364 	fd = open(path, fi->flags, mode);
365 	if (fd == -1)
366 		return -errno;
367 
368 	fi->fh = fd;
369 	return 0;
370 }
371 
xmp_open(const char * path,struct fuse_file_info * fi)372 static int xmp_open(const char *path, struct fuse_file_info *fi)
373 {
374 	int fd;
375 
376 	fd = open(path, fi->flags);
377 	if (fd == -1)
378 		return -errno;
379 
380 	fi->fh = fd;
381 	return 0;
382 }
383 
xmp_read(const char * path,char * buf,size_t size,off_t offset,struct fuse_file_info * fi)384 static int xmp_read(const char *path, char *buf, size_t size, off_t offset,
385 		    struct fuse_file_info *fi)
386 {
387 	int res;
388 
389 	(void) path;
390 	res = pread(fi->fh, buf, size, offset);
391 	if (res == -1)
392 		res = -errno;
393 
394 	return res;
395 }
396 
xmp_read_buf(const char * path,struct fuse_bufvec ** bufp,size_t size,off_t offset,struct fuse_file_info * fi)397 static int xmp_read_buf(const char *path, struct fuse_bufvec **bufp,
398 			size_t size, off_t offset, struct fuse_file_info *fi)
399 {
400 	struct fuse_bufvec *src;
401 
402 	(void) path;
403 
404 	src = malloc(sizeof(struct fuse_bufvec));
405 	if (src == NULL)
406 		return -ENOMEM;
407 
408 	*src = FUSE_BUFVEC_INIT(size);
409 
410 	src->buf[0].flags = FUSE_BUF_IS_FD | FUSE_BUF_FD_SEEK;
411 	src->buf[0].fd = fi->fh;
412 	src->buf[0].pos = offset;
413 
414 	*bufp = src;
415 
416 	return 0;
417 }
418 
xmp_write(const char * path,const char * buf,size_t size,off_t offset,struct fuse_file_info * fi)419 static int xmp_write(const char *path, const char *buf, size_t size,
420 		     off_t offset, struct fuse_file_info *fi)
421 {
422 	int res;
423 
424 	(void) path;
425 	res = pwrite(fi->fh, buf, size, offset);
426 	if (res == -1)
427 		res = -errno;
428 
429 	return res;
430 }
431 
xmp_write_buf(const char * path,struct fuse_bufvec * buf,off_t offset,struct fuse_file_info * fi)432 static int xmp_write_buf(const char *path, struct fuse_bufvec *buf,
433 		     off_t offset, struct fuse_file_info *fi)
434 {
435 	struct fuse_bufvec dst = FUSE_BUFVEC_INIT(fuse_buf_size(buf));
436 
437 	(void) path;
438 
439 	dst.buf[0].flags = FUSE_BUF_IS_FD | FUSE_BUF_FD_SEEK;
440 	dst.buf[0].fd = fi->fh;
441 	dst.buf[0].pos = offset;
442 
443 	return fuse_buf_copy(&dst, buf, FUSE_BUF_SPLICE_NONBLOCK);
444 }
445 
xmp_statfs(const char * path,struct statvfs * stbuf)446 static int xmp_statfs(const char *path, struct statvfs *stbuf)
447 {
448 	int res;
449 
450 	res = statvfs(path, stbuf);
451 	if (res == -1)
452 		return -errno;
453 
454 	return 0;
455 }
456 
xmp_flush(const char * path,struct fuse_file_info * fi)457 static int xmp_flush(const char *path, struct fuse_file_info *fi)
458 {
459 	int res;
460 
461 	(void) path;
462 	/* This is called from every close on an open file, so call the
463 	   close on the underlying filesystem.	But since flush may be
464 	   called multiple times for an open file, this must not really
465 	   close the file.  This is important if used on a network
466 	   filesystem like NFS which flush the data/metadata on close() */
467 	res = close(dup(fi->fh));
468 	if (res == -1)
469 		return -errno;
470 
471 	return 0;
472 }
473 
xmp_release(const char * path,struct fuse_file_info * fi)474 static int xmp_release(const char *path, struct fuse_file_info *fi)
475 {
476 	(void) path;
477 	close(fi->fh);
478 
479 	return 0;
480 }
481 
xmp_fsync(const char * path,int isdatasync,struct fuse_file_info * fi)482 static int xmp_fsync(const char *path, int isdatasync,
483 		     struct fuse_file_info *fi)
484 {
485 	int res;
486 	(void) path;
487 
488 #ifndef HAVE_FDATASYNC
489 	(void) isdatasync;
490 #else
491 	if (isdatasync)
492 		res = fdatasync(fi->fh);
493 	else
494 #endif
495 		res = fsync(fi->fh);
496 	if (res == -1)
497 		return -errno;
498 
499 	return 0;
500 }
501 
502 #ifdef HAVE_POSIX_FALLOCATE
xmp_fallocate(const char * path,int mode,off_t offset,off_t length,struct fuse_file_info * fi)503 static int xmp_fallocate(const char *path, int mode,
504 			off_t offset, off_t length, struct fuse_file_info *fi)
505 {
506 	(void) path;
507 
508 	if (mode)
509 		return -EOPNOTSUPP;
510 
511 	return -posix_fallocate(fi->fh, offset, length);
512 }
513 #endif
514 
515 #ifdef HAVE_SETXATTR
516 /* xattr operations are optional and can safely be left unimplemented */
xmp_setxattr(const char * path,const char * name,const char * value,size_t size,int flags)517 static int xmp_setxattr(const char *path, const char *name, const char *value,
518 			size_t size, int flags)
519 {
520 	int res = lsetxattr(path, name, value, size, flags);
521 	if (res == -1)
522 		return -errno;
523 	return 0;
524 }
525 
xmp_getxattr(const char * path,const char * name,char * value,size_t size)526 static int xmp_getxattr(const char *path, const char *name, char *value,
527 			size_t size)
528 {
529 	int res = lgetxattr(path, name, value, size);
530 	if (res == -1)
531 		return -errno;
532 	return res;
533 }
534 
xmp_listxattr(const char * path,char * list,size_t size)535 static int xmp_listxattr(const char *path, char *list, size_t size)
536 {
537 	int res = llistxattr(path, list, size);
538 	if (res == -1)
539 		return -errno;
540 	return res;
541 }
542 
xmp_removexattr(const char * path,const char * name)543 static int xmp_removexattr(const char *path, const char *name)
544 {
545 	int res = lremovexattr(path, name);
546 	if (res == -1)
547 		return -errno;
548 	return 0;
549 }
550 #endif /* HAVE_SETXATTR */
551 
552 #ifdef HAVE_LIBULOCKMGR
xmp_lock(const char * path,struct fuse_file_info * fi,int cmd,struct flock * lock)553 static int xmp_lock(const char *path, struct fuse_file_info *fi, int cmd,
554 		    struct flock *lock)
555 {
556 	(void) path;
557 
558 	return ulockmgr_op(fi->fh, cmd, lock, &fi->lock_owner,
559 			   sizeof(fi->lock_owner));
560 }
561 #endif
562 
xmp_flock(const char * path,struct fuse_file_info * fi,int op)563 static int xmp_flock(const char *path, struct fuse_file_info *fi, int op)
564 {
565 	int res;
566 	(void) path;
567 
568 	res = flock(fi->fh, op);
569 	if (res == -1)
570 		return -errno;
571 
572 	return 0;
573 }
574 
575 #ifdef HAVE_COPY_FILE_RANGE
xmp_copy_file_range(const char * path_in,struct fuse_file_info * fi_in,off_t off_in,const char * path_out,struct fuse_file_info * fi_out,off_t off_out,size_t len,int flags)576 static ssize_t xmp_copy_file_range(const char *path_in,
577 				   struct fuse_file_info *fi_in,
578 				   off_t off_in, const char *path_out,
579 				   struct fuse_file_info *fi_out,
580 				   off_t off_out, size_t len, int flags)
581 {
582 	ssize_t res;
583 	(void) path_in;
584 	(void) path_out;
585 
586 	res = copy_file_range(fi_in->fh, &off_in, fi_out->fh, &off_out, len,
587 			      flags);
588 	if (res == -1)
589 		return -errno;
590 
591 	return res;
592 }
593 #endif
594 
xmp_lseek(const char * path,off_t off,int whence,struct fuse_file_info * fi)595 static off_t xmp_lseek(const char *path, off_t off, int whence, struct fuse_file_info *fi)
596 {
597 	off_t res;
598 	(void) path;
599 
600 	res = lseek(fi->fh, off, whence);
601 	if (res == -1)
602 		return -errno;
603 
604 	return res;
605 }
606 
607 static const struct fuse_operations xmp_oper = {
608 	.init           = xmp_init,
609 	.getattr	= xmp_getattr,
610 	.access		= xmp_access,
611 	.readlink	= xmp_readlink,
612 	.opendir	= xmp_opendir,
613 	.readdir	= xmp_readdir,
614 	.releasedir	= xmp_releasedir,
615 	.mknod		= xmp_mknod,
616 	.mkdir		= xmp_mkdir,
617 	.symlink	= xmp_symlink,
618 	.unlink		= xmp_unlink,
619 	.rmdir		= xmp_rmdir,
620 	.rename		= xmp_rename,
621 	.link		= xmp_link,
622 	.chmod		= xmp_chmod,
623 	.chown		= xmp_chown,
624 	.truncate	= xmp_truncate,
625 #ifdef HAVE_UTIMENSAT
626 	.utimens	= xmp_utimens,
627 #endif
628 	.create		= xmp_create,
629 	.open		= xmp_open,
630 	.read		= xmp_read,
631 	.read_buf	= xmp_read_buf,
632 	.write		= xmp_write,
633 	.write_buf	= xmp_write_buf,
634 	.statfs		= xmp_statfs,
635 	.flush		= xmp_flush,
636 	.release	= xmp_release,
637 	.fsync		= xmp_fsync,
638 #ifdef HAVE_POSIX_FALLOCATE
639 	.fallocate	= xmp_fallocate,
640 #endif
641 #ifdef HAVE_SETXATTR
642 	.setxattr	= xmp_setxattr,
643 	.getxattr	= xmp_getxattr,
644 	.listxattr	= xmp_listxattr,
645 	.removexattr	= xmp_removexattr,
646 #endif
647 #ifdef HAVE_LIBULOCKMGR
648 	.lock		= xmp_lock,
649 #endif
650 	.flock		= xmp_flock,
651 #ifdef HAVE_COPY_FILE_RANGE
652 	.copy_file_range = xmp_copy_file_range,
653 #endif
654 	.lseek		= xmp_lseek,
655 };
656 
main(int argc,char * argv[])657 int main(int argc, char *argv[])
658 {
659 	umask(0);
660 	return fuse_main(argc, argv, &xmp_oper, NULL);
661 }
662