xref: /third_party/libfuse/lib/modules/subdir.c (revision 6881f68f)
1/*
2  fuse subdir module: offset paths with a base directory
3  Copyright (C) 2007  Miklos Szeredi <miklos@szeredi.hu>
4
5  This program can be distributed under the terms of the GNU LGPLv2.
6  See the file COPYING.LIB
7*/
8
9#include <fuse_config.h>
10
11#include <fuse.h>
12#include <stdio.h>
13#include <stdlib.h>
14#include <stddef.h>
15#include <string.h>
16#include <errno.h>
17
18struct subdir {
19	char *base;
20	size_t baselen;
21	int rellinks;
22	struct fuse_fs *next;
23};
24
25static struct subdir *subdir_get(void)
26{
27	return fuse_get_context()->private_data;
28}
29
30static int subdir_addpath(struct subdir *d, const char *path, char **newpathp)
31{
32	char *newpath = NULL;
33
34	if (path != NULL) {
35		unsigned newlen = d->baselen + strlen(path);
36
37		newpath = malloc(newlen + 2);
38		if (!newpath)
39			return -ENOMEM;
40
41		if (path[0] == '/')
42			path++;
43		strcpy(newpath, d->base);
44		strcpy(newpath + d->baselen, path);
45		if (!newpath[0])
46			strcpy(newpath, ".");
47	}
48	*newpathp = newpath;
49
50	return 0;
51}
52
53static int subdir_getattr(const char *path, struct stat *stbuf,
54			  struct fuse_file_info *fi)
55{
56	struct subdir *d = subdir_get();
57	char *newpath;
58	int err = subdir_addpath(d, path, &newpath);
59	if (!err) {
60		err = fuse_fs_getattr(d->next, newpath, stbuf, fi);
61		free(newpath);
62	}
63	return err;
64}
65
66static int subdir_access(const char *path, int mask)
67{
68	struct subdir *d = subdir_get();
69	char *newpath;
70	int err = subdir_addpath(d, path, &newpath);
71	if (!err) {
72		err = fuse_fs_access(d->next, newpath, mask);
73		free(newpath);
74	}
75	return err;
76}
77
78
79static int count_components(const char *p)
80{
81	int ctr;
82
83	for (; *p == '/'; p++);
84	for (ctr = 0; *p; ctr++) {
85		for (; *p && *p != '/'; p++);
86		for (; *p == '/'; p++);
87	}
88	return ctr;
89}
90
91static void strip_common(const char **sp, const char **tp)
92{
93	const char *s = *sp;
94	const char *t = *tp;
95	do {
96		for (; *s == '/'; s++);
97		for (; *t == '/'; t++);
98		*tp = t;
99		*sp = s;
100		for (; *s == *t && *s && *s != '/'; s++, t++);
101	} while ((*s == *t && *s) || (!*s && *t == '/') || (*s == '/' && !*t));
102}
103
104static void transform_symlink(struct subdir *d, const char *path,
105			      char *buf, size_t size)
106{
107	const char *l = buf;
108	size_t llen;
109	char *s;
110	int dotdots;
111	int i;
112
113	if (l[0] != '/' || d->base[0] != '/')
114		return;
115
116	strip_common(&l, &path);
117	if (l - buf < (long) d->baselen)
118		return;
119
120	dotdots = count_components(path);
121	if (!dotdots)
122		return;
123	dotdots--;
124
125	llen = strlen(l);
126	if (dotdots * 3 + llen + 2 > size)
127		return;
128
129	s = buf + dotdots * 3;
130	if (llen)
131		memmove(s, l, llen + 1);
132	else if (!dotdots)
133		strcpy(s, ".");
134	else
135		*s = '\0';
136
137	for (s = buf, i = 0; i < dotdots; i++, s += 3)
138		memcpy(s, "../", 3);
139}
140
141
142static int subdir_readlink(const char *path, char *buf, size_t size)
143{
144	struct subdir *d = subdir_get();
145	char *newpath;
146	int err = subdir_addpath(d, path, &newpath);
147	if (!err) {
148		err = fuse_fs_readlink(d->next, newpath, buf, size);
149		if (!err && d->rellinks)
150			transform_symlink(d, newpath, buf, size);
151		free(newpath);
152	}
153	return err;
154}
155
156static int subdir_opendir(const char *path, struct fuse_file_info *fi)
157{
158	struct subdir *d = subdir_get();
159	char *newpath;
160	int err = subdir_addpath(d, path, &newpath);
161	if (!err) {
162		err = fuse_fs_opendir(d->next, newpath, fi);
163		free(newpath);
164	}
165	return err;
166}
167
168static int subdir_readdir(const char *path, void *buf,
169			  fuse_fill_dir_t filler, off_t offset,
170			  struct fuse_file_info *fi,
171			  enum fuse_readdir_flags flags)
172{
173	struct subdir *d = subdir_get();
174	char *newpath;
175	int err = subdir_addpath(d, path, &newpath);
176	if (!err) {
177		err = fuse_fs_readdir(d->next, newpath, buf, filler, offset,
178				      fi, flags);
179		free(newpath);
180	}
181	return err;
182}
183
184static int subdir_releasedir(const char *path, struct fuse_file_info *fi)
185{
186	struct subdir *d = subdir_get();
187	char *newpath;
188	int err = subdir_addpath(d, path, &newpath);
189	if (!err) {
190		err = fuse_fs_releasedir(d->next, newpath, fi);
191		free(newpath);
192	}
193	return err;
194}
195
196static int subdir_mknod(const char *path, mode_t mode, dev_t rdev)
197{
198	struct subdir *d = subdir_get();
199	char *newpath;
200	int err = subdir_addpath(d, path, &newpath);
201	if (!err) {
202		err = fuse_fs_mknod(d->next, newpath, mode, rdev);
203		free(newpath);
204	}
205	return err;
206}
207
208static int subdir_mkdir(const char *path, mode_t mode)
209{
210	struct subdir *d = subdir_get();
211	char *newpath;
212	int err = subdir_addpath(d, path, &newpath);
213	if (!err) {
214		err = fuse_fs_mkdir(d->next, newpath, mode);
215		free(newpath);
216	}
217	return err;
218}
219
220static int subdir_unlink(const char *path)
221{
222	struct subdir *d = subdir_get();
223	char *newpath;
224	int err = subdir_addpath(d, path, &newpath);
225	if (!err) {
226		err = fuse_fs_unlink(d->next, newpath);
227		free(newpath);
228	}
229	return err;
230}
231
232static int subdir_rmdir(const char *path)
233{
234	struct subdir *d = subdir_get();
235	char *newpath;
236	int err = subdir_addpath(d, path, &newpath);
237	if (!err) {
238		err = fuse_fs_rmdir(d->next, newpath);
239		free(newpath);
240	}
241	return err;
242}
243
244static int subdir_symlink(const char *from, const char *path)
245{
246	struct subdir *d = subdir_get();
247	char *newpath;
248	int err = subdir_addpath(d, path, &newpath);
249	if (!err) {
250		err = fuse_fs_symlink(d->next, from, newpath);
251		free(newpath);
252	}
253	return err;
254}
255
256static int subdir_rename(const char *from, const char *to, unsigned int flags)
257{
258	struct subdir *d = subdir_get();
259	char *newfrom;
260	char *newto;
261	int err = subdir_addpath(d, from, &newfrom);
262	if (!err) {
263		err = subdir_addpath(d, to, &newto);
264		if (!err) {
265			err = fuse_fs_rename(d->next, newfrom, newto, flags);
266			free(newto);
267		}
268		free(newfrom);
269	}
270	return err;
271}
272
273static int subdir_link(const char *from, const char *to)
274{
275	struct subdir *d = subdir_get();
276	char *newfrom;
277	char *newto;
278	int err = subdir_addpath(d, from, &newfrom);
279	if (!err) {
280		err = subdir_addpath(d, to, &newto);
281		if (!err) {
282			err = fuse_fs_link(d->next, newfrom, newto);
283			free(newto);
284		}
285		free(newfrom);
286	}
287	return err;
288}
289
290static int subdir_chmod(const char *path, mode_t mode,
291			struct fuse_file_info *fi)
292{
293	struct subdir *d = subdir_get();
294	char *newpath;
295	int err = subdir_addpath(d, path, &newpath);
296	if (!err) {
297		err = fuse_fs_chmod(d->next, newpath, mode, fi);
298		free(newpath);
299	}
300	return err;
301}
302
303static int subdir_chown(const char *path, uid_t uid, gid_t gid,
304			struct fuse_file_info *fi)
305{
306	struct subdir *d = subdir_get();
307	char *newpath;
308	int err = subdir_addpath(d, path, &newpath);
309	if (!err) {
310		err = fuse_fs_chown(d->next, newpath, uid, gid, fi);
311		free(newpath);
312	}
313	return err;
314}
315
316static int subdir_truncate(const char *path, off_t size,
317			   struct fuse_file_info *fi)
318{
319	struct subdir *d = subdir_get();
320	char *newpath;
321	int err = subdir_addpath(d, path, &newpath);
322	if (!err) {
323		err = fuse_fs_truncate(d->next, newpath, size, fi);
324		free(newpath);
325	}
326	return err;
327}
328
329static int subdir_utimens(const char *path, const struct timespec ts[2],
330			  struct fuse_file_info *fi)
331{
332	struct subdir *d = subdir_get();
333	char *newpath;
334	int err = subdir_addpath(d, path, &newpath);
335	if (!err) {
336		err = fuse_fs_utimens(d->next, newpath, ts, fi);
337		free(newpath);
338	}
339	return err;
340}
341
342static int subdir_create(const char *path, mode_t mode,
343			 struct fuse_file_info *fi)
344{
345	struct subdir *d = subdir_get();
346	char *newpath;
347	int err = subdir_addpath(d, path, &newpath);
348	if (!err) {
349		err = fuse_fs_create(d->next, newpath, mode, fi);
350		free(newpath);
351	}
352	return err;
353}
354
355static int subdir_open(const char *path, struct fuse_file_info *fi)
356{
357	struct subdir *d = subdir_get();
358	char *newpath;
359	int err = subdir_addpath(d, path, &newpath);
360	if (!err) {
361		err = fuse_fs_open(d->next, newpath, fi);
362		free(newpath);
363	}
364	return err;
365}
366
367static int subdir_read_buf(const char *path, struct fuse_bufvec **bufp,
368			   size_t size, off_t offset, struct fuse_file_info *fi)
369{
370	struct subdir *d = subdir_get();
371	char *newpath;
372	int err = subdir_addpath(d, path, &newpath);
373	if (!err) {
374		err = fuse_fs_read_buf(d->next, newpath, bufp, size, offset, fi);
375		free(newpath);
376	}
377	return err;
378}
379
380static int subdir_write_buf(const char *path, struct fuse_bufvec *buf,
381			off_t offset, struct fuse_file_info *fi)
382{
383	struct subdir *d = subdir_get();
384	char *newpath;
385	int err = subdir_addpath(d, path, &newpath);
386	if (!err) {
387		err = fuse_fs_write_buf(d->next, newpath, buf, offset, fi);
388		free(newpath);
389	}
390	return err;
391}
392
393static int subdir_statfs(const char *path, struct statvfs *stbuf)
394{
395	struct subdir *d = subdir_get();
396	char *newpath;
397	int err = subdir_addpath(d, path, &newpath);
398	if (!err) {
399		err = fuse_fs_statfs(d->next, newpath, stbuf);
400		free(newpath);
401	}
402	return err;
403}
404
405static int subdir_flush(const char *path, struct fuse_file_info *fi)
406{
407	struct subdir *d = subdir_get();
408	char *newpath;
409	int err = subdir_addpath(d, path, &newpath);
410	if (!err) {
411		err = fuse_fs_flush(d->next, newpath, fi);
412		free(newpath);
413	}
414	return err;
415}
416
417static int subdir_release(const char *path, struct fuse_file_info *fi)
418{
419	struct subdir *d = subdir_get();
420	char *newpath;
421	int err = subdir_addpath(d, path, &newpath);
422	if (!err) {
423		err = fuse_fs_release(d->next, newpath, fi);
424		free(newpath);
425	}
426	return err;
427}
428
429static int subdir_fsync(const char *path, int isdatasync,
430			struct fuse_file_info *fi)
431{
432	struct subdir *d = subdir_get();
433	char *newpath;
434	int err = subdir_addpath(d, path, &newpath);
435	if (!err) {
436		err = fuse_fs_fsync(d->next, newpath, isdatasync, fi);
437		free(newpath);
438	}
439	return err;
440}
441
442static int subdir_fsyncdir(const char *path, int isdatasync,
443			   struct fuse_file_info *fi)
444{
445	struct subdir *d = subdir_get();
446	char *newpath;
447	int err = subdir_addpath(d, path, &newpath);
448	if (!err) {
449		err = fuse_fs_fsyncdir(d->next, newpath, isdatasync, fi);
450		free(newpath);
451	}
452	return err;
453}
454
455static int subdir_setxattr(const char *path, const char *name,
456			   const char *value, size_t size, int flags)
457{
458	struct subdir *d = subdir_get();
459	char *newpath;
460	int err = subdir_addpath(d, path, &newpath);
461	if (!err) {
462		err = fuse_fs_setxattr(d->next, newpath, name, value, size,
463				       flags);
464		free(newpath);
465	}
466	return err;
467}
468
469static int subdir_getxattr(const char *path, const char *name, char *value,
470			   size_t size)
471{
472	struct subdir *d = subdir_get();
473	char *newpath;
474	int err = subdir_addpath(d, path, &newpath);
475	if (!err) {
476		err = fuse_fs_getxattr(d->next, newpath, name, value, size);
477		free(newpath);
478	}
479	return err;
480}
481
482static int subdir_listxattr(const char *path, char *list, size_t size)
483{
484	struct subdir *d = subdir_get();
485	char *newpath;
486	int err = subdir_addpath(d, path, &newpath);
487	if (!err) {
488		err = fuse_fs_listxattr(d->next, newpath, list, size);
489		free(newpath);
490	}
491	return err;
492}
493
494static int subdir_removexattr(const char *path, const char *name)
495{
496	struct subdir *d = subdir_get();
497	char *newpath;
498	int err = subdir_addpath(d, path, &newpath);
499	if (!err) {
500		err = fuse_fs_removexattr(d->next, newpath, name);
501		free(newpath);
502	}
503	return err;
504}
505
506static int subdir_lock(const char *path, struct fuse_file_info *fi, int cmd,
507		       struct flock *lock)
508{
509	struct subdir *d = subdir_get();
510	char *newpath;
511	int err = subdir_addpath(d, path, &newpath);
512	if (!err) {
513		err = fuse_fs_lock(d->next, newpath, fi, cmd, lock);
514		free(newpath);
515	}
516	return err;
517}
518
519static int subdir_flock(const char *path, struct fuse_file_info *fi, int op)
520{
521	struct subdir *d = subdir_get();
522	char *newpath;
523	int err = subdir_addpath(d, path, &newpath);
524	if (!err) {
525		err = fuse_fs_flock(d->next, newpath, fi, op);
526		free(newpath);
527	}
528	return err;
529}
530
531static int subdir_bmap(const char *path, size_t blocksize, uint64_t *idx)
532{
533	struct subdir *d = subdir_get();
534	char *newpath;
535	int err = subdir_addpath(d, path, &newpath);
536	if (!err) {
537		err = fuse_fs_bmap(d->next, newpath, blocksize, idx);
538		free(newpath);
539	}
540	return err;
541}
542
543static off_t subdir_lseek(const char *path, off_t off, int whence,
544			  struct fuse_file_info *fi)
545{
546	struct subdir *ic = subdir_get();
547	char *newpath;
548	int res = subdir_addpath(ic, path, &newpath);
549	if (!res) {
550		res = fuse_fs_lseek(ic->next, newpath, off, whence, fi);
551		free(newpath);
552	}
553	return res;
554}
555
556static void *subdir_init(struct fuse_conn_info *conn,
557			 struct fuse_config *cfg)
558{
559	struct subdir *d = subdir_get();
560	fuse_fs_init(d->next, conn, cfg);
561	/* Don't touch cfg->nullpath_ok, we can work with
562	   either */
563	return d;
564}
565
566static void subdir_destroy(void *data)
567{
568	struct subdir *d = data;
569	fuse_fs_destroy(d->next);
570	free(d->base);
571	free(d);
572}
573
574static const struct fuse_operations subdir_oper = {
575	.destroy	= subdir_destroy,
576	.init		= subdir_init,
577	.getattr	= subdir_getattr,
578	.access		= subdir_access,
579	.readlink	= subdir_readlink,
580	.opendir	= subdir_opendir,
581	.readdir	= subdir_readdir,
582	.releasedir	= subdir_releasedir,
583	.mknod		= subdir_mknod,
584	.mkdir		= subdir_mkdir,
585	.symlink	= subdir_symlink,
586	.unlink		= subdir_unlink,
587	.rmdir		= subdir_rmdir,
588	.rename		= subdir_rename,
589	.link		= subdir_link,
590	.chmod		= subdir_chmod,
591	.chown		= subdir_chown,
592	.truncate	= subdir_truncate,
593	.utimens	= subdir_utimens,
594	.create		= subdir_create,
595	.open		= subdir_open,
596	.read_buf	= subdir_read_buf,
597	.write_buf	= subdir_write_buf,
598	.statfs		= subdir_statfs,
599	.flush		= subdir_flush,
600	.release	= subdir_release,
601	.fsync		= subdir_fsync,
602	.fsyncdir	= subdir_fsyncdir,
603	.setxattr	= subdir_setxattr,
604	.getxattr	= subdir_getxattr,
605	.listxattr	= subdir_listxattr,
606	.removexattr	= subdir_removexattr,
607	.lock		= subdir_lock,
608	.flock		= subdir_flock,
609	.bmap		= subdir_bmap,
610	.lseek		= subdir_lseek,
611};
612
613static const struct fuse_opt subdir_opts[] = {
614	FUSE_OPT_KEY("-h", 0),
615	FUSE_OPT_KEY("--help", 0),
616	{ "subdir=%s", offsetof(struct subdir, base), 0 },
617	{ "rellinks", offsetof(struct subdir, rellinks), 1 },
618	{ "norellinks", offsetof(struct subdir, rellinks), 0 },
619	FUSE_OPT_END
620};
621
622static void subdir_help(void)
623{
624	printf(
625"    -o subdir=DIR	    prepend this directory to all paths (mandatory)\n"
626"    -o [no]rellinks	    transform absolute symlinks to relative\n");
627}
628
629static int subdir_opt_proc(void *data, const char *arg, int key,
630			   struct fuse_args *outargs)
631{
632	(void) data; (void) arg; (void) outargs;
633
634	if (!key) {
635		subdir_help();
636		return -1;
637	}
638
639	return 1;
640}
641
642static struct fuse_fs *subdir_new(struct fuse_args *args,
643				  struct fuse_fs *next[])
644{
645	struct fuse_fs *fs;
646	struct subdir *d;
647
648	d = calloc(1, sizeof(struct subdir));
649	if (d == NULL) {
650		fuse_log(FUSE_LOG_ERR, "fuse-subdir: memory allocation failed\n");
651		return NULL;
652	}
653
654	if (fuse_opt_parse(args, d, subdir_opts, subdir_opt_proc) == -1)
655		goto out_free;
656
657	if (!next[0] || next[1]) {
658		fuse_log(FUSE_LOG_ERR, "fuse-subdir: exactly one next filesystem required\n");
659		goto out_free;
660	}
661
662	if (!d->base) {
663		fuse_log(FUSE_LOG_ERR, "fuse-subdir: missing 'subdir' option\n");
664		goto out_free;
665	}
666
667	if (d->base[0] && d->base[strlen(d->base)-1] != '/') {
668		char *tmp = realloc(d->base, strlen(d->base) + 2);
669		if (!tmp) {
670			fuse_log(FUSE_LOG_ERR, "fuse-subdir: memory allocation failed\n");
671			goto out_free;
672		}
673		d->base = tmp;
674		strcat(d->base, "/");
675	}
676	d->baselen = strlen(d->base);
677	d->next = next[0];
678	fs = fuse_fs_new(&subdir_oper, sizeof(subdir_oper), d);
679	if (!fs)
680		goto out_free;
681	return fs;
682
683out_free:
684	free(d->base);
685	free(d);
686	return NULL;
687}
688
689FUSE_REGISTER_MODULE(subdir, subdir_new);
690