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