16881f68fSopenharmony_ci/*
26881f68fSopenharmony_ci  fuse subdir module: offset paths with a base directory
36881f68fSopenharmony_ci  Copyright (C) 2007  Miklos Szeredi <miklos@szeredi.hu>
46881f68fSopenharmony_ci
56881f68fSopenharmony_ci  This program can be distributed under the terms of the GNU LGPLv2.
66881f68fSopenharmony_ci  See the file COPYING.LIB
76881f68fSopenharmony_ci*/
86881f68fSopenharmony_ci
96881f68fSopenharmony_ci#include <fuse_config.h>
106881f68fSopenharmony_ci
116881f68fSopenharmony_ci#include <fuse.h>
126881f68fSopenharmony_ci#include <stdio.h>
136881f68fSopenharmony_ci#include <stdlib.h>
146881f68fSopenharmony_ci#include <stddef.h>
156881f68fSopenharmony_ci#include <string.h>
166881f68fSopenharmony_ci#include <errno.h>
176881f68fSopenharmony_ci
186881f68fSopenharmony_cistruct subdir {
196881f68fSopenharmony_ci	char *base;
206881f68fSopenharmony_ci	size_t baselen;
216881f68fSopenharmony_ci	int rellinks;
226881f68fSopenharmony_ci	struct fuse_fs *next;
236881f68fSopenharmony_ci};
246881f68fSopenharmony_ci
256881f68fSopenharmony_cistatic struct subdir *subdir_get(void)
266881f68fSopenharmony_ci{
276881f68fSopenharmony_ci	return fuse_get_context()->private_data;
286881f68fSopenharmony_ci}
296881f68fSopenharmony_ci
306881f68fSopenharmony_cistatic int subdir_addpath(struct subdir *d, const char *path, char **newpathp)
316881f68fSopenharmony_ci{
326881f68fSopenharmony_ci	char *newpath = NULL;
336881f68fSopenharmony_ci
346881f68fSopenharmony_ci	if (path != NULL) {
356881f68fSopenharmony_ci		unsigned newlen = d->baselen + strlen(path);
366881f68fSopenharmony_ci
376881f68fSopenharmony_ci		newpath = malloc(newlen + 2);
386881f68fSopenharmony_ci		if (!newpath)
396881f68fSopenharmony_ci			return -ENOMEM;
406881f68fSopenharmony_ci
416881f68fSopenharmony_ci		if (path[0] == '/')
426881f68fSopenharmony_ci			path++;
436881f68fSopenharmony_ci		strcpy(newpath, d->base);
446881f68fSopenharmony_ci		strcpy(newpath + d->baselen, path);
456881f68fSopenharmony_ci		if (!newpath[0])
466881f68fSopenharmony_ci			strcpy(newpath, ".");
476881f68fSopenharmony_ci	}
486881f68fSopenharmony_ci	*newpathp = newpath;
496881f68fSopenharmony_ci
506881f68fSopenharmony_ci	return 0;
516881f68fSopenharmony_ci}
526881f68fSopenharmony_ci
536881f68fSopenharmony_cistatic int subdir_getattr(const char *path, struct stat *stbuf,
546881f68fSopenharmony_ci			  struct fuse_file_info *fi)
556881f68fSopenharmony_ci{
566881f68fSopenharmony_ci	struct subdir *d = subdir_get();
576881f68fSopenharmony_ci	char *newpath;
586881f68fSopenharmony_ci	int err = subdir_addpath(d, path, &newpath);
596881f68fSopenharmony_ci	if (!err) {
606881f68fSopenharmony_ci		err = fuse_fs_getattr(d->next, newpath, stbuf, fi);
616881f68fSopenharmony_ci		free(newpath);
626881f68fSopenharmony_ci	}
636881f68fSopenharmony_ci	return err;
646881f68fSopenharmony_ci}
656881f68fSopenharmony_ci
666881f68fSopenharmony_cistatic int subdir_access(const char *path, int mask)
676881f68fSopenharmony_ci{
686881f68fSopenharmony_ci	struct subdir *d = subdir_get();
696881f68fSopenharmony_ci	char *newpath;
706881f68fSopenharmony_ci	int err = subdir_addpath(d, path, &newpath);
716881f68fSopenharmony_ci	if (!err) {
726881f68fSopenharmony_ci		err = fuse_fs_access(d->next, newpath, mask);
736881f68fSopenharmony_ci		free(newpath);
746881f68fSopenharmony_ci	}
756881f68fSopenharmony_ci	return err;
766881f68fSopenharmony_ci}
776881f68fSopenharmony_ci
786881f68fSopenharmony_ci
796881f68fSopenharmony_cistatic int count_components(const char *p)
806881f68fSopenharmony_ci{
816881f68fSopenharmony_ci	int ctr;
826881f68fSopenharmony_ci
836881f68fSopenharmony_ci	for (; *p == '/'; p++);
846881f68fSopenharmony_ci	for (ctr = 0; *p; ctr++) {
856881f68fSopenharmony_ci		for (; *p && *p != '/'; p++);
866881f68fSopenharmony_ci		for (; *p == '/'; p++);
876881f68fSopenharmony_ci	}
886881f68fSopenharmony_ci	return ctr;
896881f68fSopenharmony_ci}
906881f68fSopenharmony_ci
916881f68fSopenharmony_cistatic void strip_common(const char **sp, const char **tp)
926881f68fSopenharmony_ci{
936881f68fSopenharmony_ci	const char *s = *sp;
946881f68fSopenharmony_ci	const char *t = *tp;
956881f68fSopenharmony_ci	do {
966881f68fSopenharmony_ci		for (; *s == '/'; s++);
976881f68fSopenharmony_ci		for (; *t == '/'; t++);
986881f68fSopenharmony_ci		*tp = t;
996881f68fSopenharmony_ci		*sp = s;
1006881f68fSopenharmony_ci		for (; *s == *t && *s && *s != '/'; s++, t++);
1016881f68fSopenharmony_ci	} while ((*s == *t && *s) || (!*s && *t == '/') || (*s == '/' && !*t));
1026881f68fSopenharmony_ci}
1036881f68fSopenharmony_ci
1046881f68fSopenharmony_cistatic void transform_symlink(struct subdir *d, const char *path,
1056881f68fSopenharmony_ci			      char *buf, size_t size)
1066881f68fSopenharmony_ci{
1076881f68fSopenharmony_ci	const char *l = buf;
1086881f68fSopenharmony_ci	size_t llen;
1096881f68fSopenharmony_ci	char *s;
1106881f68fSopenharmony_ci	int dotdots;
1116881f68fSopenharmony_ci	int i;
1126881f68fSopenharmony_ci
1136881f68fSopenharmony_ci	if (l[0] != '/' || d->base[0] != '/')
1146881f68fSopenharmony_ci		return;
1156881f68fSopenharmony_ci
1166881f68fSopenharmony_ci	strip_common(&l, &path);
1176881f68fSopenharmony_ci	if (l - buf < (long) d->baselen)
1186881f68fSopenharmony_ci		return;
1196881f68fSopenharmony_ci
1206881f68fSopenharmony_ci	dotdots = count_components(path);
1216881f68fSopenharmony_ci	if (!dotdots)
1226881f68fSopenharmony_ci		return;
1236881f68fSopenharmony_ci	dotdots--;
1246881f68fSopenharmony_ci
1256881f68fSopenharmony_ci	llen = strlen(l);
1266881f68fSopenharmony_ci	if (dotdots * 3 + llen + 2 > size)
1276881f68fSopenharmony_ci		return;
1286881f68fSopenharmony_ci
1296881f68fSopenharmony_ci	s = buf + dotdots * 3;
1306881f68fSopenharmony_ci	if (llen)
1316881f68fSopenharmony_ci		memmove(s, l, llen + 1);
1326881f68fSopenharmony_ci	else if (!dotdots)
1336881f68fSopenharmony_ci		strcpy(s, ".");
1346881f68fSopenharmony_ci	else
1356881f68fSopenharmony_ci		*s = '\0';
1366881f68fSopenharmony_ci
1376881f68fSopenharmony_ci	for (s = buf, i = 0; i < dotdots; i++, s += 3)
1386881f68fSopenharmony_ci		memcpy(s, "../", 3);
1396881f68fSopenharmony_ci}
1406881f68fSopenharmony_ci
1416881f68fSopenharmony_ci
1426881f68fSopenharmony_cistatic int subdir_readlink(const char *path, char *buf, size_t size)
1436881f68fSopenharmony_ci{
1446881f68fSopenharmony_ci	struct subdir *d = subdir_get();
1456881f68fSopenharmony_ci	char *newpath;
1466881f68fSopenharmony_ci	int err = subdir_addpath(d, path, &newpath);
1476881f68fSopenharmony_ci	if (!err) {
1486881f68fSopenharmony_ci		err = fuse_fs_readlink(d->next, newpath, buf, size);
1496881f68fSopenharmony_ci		if (!err && d->rellinks)
1506881f68fSopenharmony_ci			transform_symlink(d, newpath, buf, size);
1516881f68fSopenharmony_ci		free(newpath);
1526881f68fSopenharmony_ci	}
1536881f68fSopenharmony_ci	return err;
1546881f68fSopenharmony_ci}
1556881f68fSopenharmony_ci
1566881f68fSopenharmony_cistatic int subdir_opendir(const char *path, struct fuse_file_info *fi)
1576881f68fSopenharmony_ci{
1586881f68fSopenharmony_ci	struct subdir *d = subdir_get();
1596881f68fSopenharmony_ci	char *newpath;
1606881f68fSopenharmony_ci	int err = subdir_addpath(d, path, &newpath);
1616881f68fSopenharmony_ci	if (!err) {
1626881f68fSopenharmony_ci		err = fuse_fs_opendir(d->next, newpath, fi);
1636881f68fSopenharmony_ci		free(newpath);
1646881f68fSopenharmony_ci	}
1656881f68fSopenharmony_ci	return err;
1666881f68fSopenharmony_ci}
1676881f68fSopenharmony_ci
1686881f68fSopenharmony_cistatic int subdir_readdir(const char *path, void *buf,
1696881f68fSopenharmony_ci			  fuse_fill_dir_t filler, off_t offset,
1706881f68fSopenharmony_ci			  struct fuse_file_info *fi,
1716881f68fSopenharmony_ci			  enum fuse_readdir_flags flags)
1726881f68fSopenharmony_ci{
1736881f68fSopenharmony_ci	struct subdir *d = subdir_get();
1746881f68fSopenharmony_ci	char *newpath;
1756881f68fSopenharmony_ci	int err = subdir_addpath(d, path, &newpath);
1766881f68fSopenharmony_ci	if (!err) {
1776881f68fSopenharmony_ci		err = fuse_fs_readdir(d->next, newpath, buf, filler, offset,
1786881f68fSopenharmony_ci				      fi, flags);
1796881f68fSopenharmony_ci		free(newpath);
1806881f68fSopenharmony_ci	}
1816881f68fSopenharmony_ci	return err;
1826881f68fSopenharmony_ci}
1836881f68fSopenharmony_ci
1846881f68fSopenharmony_cistatic int subdir_releasedir(const char *path, struct fuse_file_info *fi)
1856881f68fSopenharmony_ci{
1866881f68fSopenharmony_ci	struct subdir *d = subdir_get();
1876881f68fSopenharmony_ci	char *newpath;
1886881f68fSopenharmony_ci	int err = subdir_addpath(d, path, &newpath);
1896881f68fSopenharmony_ci	if (!err) {
1906881f68fSopenharmony_ci		err = fuse_fs_releasedir(d->next, newpath, fi);
1916881f68fSopenharmony_ci		free(newpath);
1926881f68fSopenharmony_ci	}
1936881f68fSopenharmony_ci	return err;
1946881f68fSopenharmony_ci}
1956881f68fSopenharmony_ci
1966881f68fSopenharmony_cistatic int subdir_mknod(const char *path, mode_t mode, dev_t rdev)
1976881f68fSopenharmony_ci{
1986881f68fSopenharmony_ci	struct subdir *d = subdir_get();
1996881f68fSopenharmony_ci	char *newpath;
2006881f68fSopenharmony_ci	int err = subdir_addpath(d, path, &newpath);
2016881f68fSopenharmony_ci	if (!err) {
2026881f68fSopenharmony_ci		err = fuse_fs_mknod(d->next, newpath, mode, rdev);
2036881f68fSopenharmony_ci		free(newpath);
2046881f68fSopenharmony_ci	}
2056881f68fSopenharmony_ci	return err;
2066881f68fSopenharmony_ci}
2076881f68fSopenharmony_ci
2086881f68fSopenharmony_cistatic int subdir_mkdir(const char *path, mode_t mode)
2096881f68fSopenharmony_ci{
2106881f68fSopenharmony_ci	struct subdir *d = subdir_get();
2116881f68fSopenharmony_ci	char *newpath;
2126881f68fSopenharmony_ci	int err = subdir_addpath(d, path, &newpath);
2136881f68fSopenharmony_ci	if (!err) {
2146881f68fSopenharmony_ci		err = fuse_fs_mkdir(d->next, newpath, mode);
2156881f68fSopenharmony_ci		free(newpath);
2166881f68fSopenharmony_ci	}
2176881f68fSopenharmony_ci	return err;
2186881f68fSopenharmony_ci}
2196881f68fSopenharmony_ci
2206881f68fSopenharmony_cistatic int subdir_unlink(const char *path)
2216881f68fSopenharmony_ci{
2226881f68fSopenharmony_ci	struct subdir *d = subdir_get();
2236881f68fSopenharmony_ci	char *newpath;
2246881f68fSopenharmony_ci	int err = subdir_addpath(d, path, &newpath);
2256881f68fSopenharmony_ci	if (!err) {
2266881f68fSopenharmony_ci		err = fuse_fs_unlink(d->next, newpath);
2276881f68fSopenharmony_ci		free(newpath);
2286881f68fSopenharmony_ci	}
2296881f68fSopenharmony_ci	return err;
2306881f68fSopenharmony_ci}
2316881f68fSopenharmony_ci
2326881f68fSopenharmony_cistatic int subdir_rmdir(const char *path)
2336881f68fSopenharmony_ci{
2346881f68fSopenharmony_ci	struct subdir *d = subdir_get();
2356881f68fSopenharmony_ci	char *newpath;
2366881f68fSopenharmony_ci	int err = subdir_addpath(d, path, &newpath);
2376881f68fSopenharmony_ci	if (!err) {
2386881f68fSopenharmony_ci		err = fuse_fs_rmdir(d->next, newpath);
2396881f68fSopenharmony_ci		free(newpath);
2406881f68fSopenharmony_ci	}
2416881f68fSopenharmony_ci	return err;
2426881f68fSopenharmony_ci}
2436881f68fSopenharmony_ci
2446881f68fSopenharmony_cistatic int subdir_symlink(const char *from, const char *path)
2456881f68fSopenharmony_ci{
2466881f68fSopenharmony_ci	struct subdir *d = subdir_get();
2476881f68fSopenharmony_ci	char *newpath;
2486881f68fSopenharmony_ci	int err = subdir_addpath(d, path, &newpath);
2496881f68fSopenharmony_ci	if (!err) {
2506881f68fSopenharmony_ci		err = fuse_fs_symlink(d->next, from, newpath);
2516881f68fSopenharmony_ci		free(newpath);
2526881f68fSopenharmony_ci	}
2536881f68fSopenharmony_ci	return err;
2546881f68fSopenharmony_ci}
2556881f68fSopenharmony_ci
2566881f68fSopenharmony_cistatic int subdir_rename(const char *from, const char *to, unsigned int flags)
2576881f68fSopenharmony_ci{
2586881f68fSopenharmony_ci	struct subdir *d = subdir_get();
2596881f68fSopenharmony_ci	char *newfrom;
2606881f68fSopenharmony_ci	char *newto;
2616881f68fSopenharmony_ci	int err = subdir_addpath(d, from, &newfrom);
2626881f68fSopenharmony_ci	if (!err) {
2636881f68fSopenharmony_ci		err = subdir_addpath(d, to, &newto);
2646881f68fSopenharmony_ci		if (!err) {
2656881f68fSopenharmony_ci			err = fuse_fs_rename(d->next, newfrom, newto, flags);
2666881f68fSopenharmony_ci			free(newto);
2676881f68fSopenharmony_ci		}
2686881f68fSopenharmony_ci		free(newfrom);
2696881f68fSopenharmony_ci	}
2706881f68fSopenharmony_ci	return err;
2716881f68fSopenharmony_ci}
2726881f68fSopenharmony_ci
2736881f68fSopenharmony_cistatic int subdir_link(const char *from, const char *to)
2746881f68fSopenharmony_ci{
2756881f68fSopenharmony_ci	struct subdir *d = subdir_get();
2766881f68fSopenharmony_ci	char *newfrom;
2776881f68fSopenharmony_ci	char *newto;
2786881f68fSopenharmony_ci	int err = subdir_addpath(d, from, &newfrom);
2796881f68fSopenharmony_ci	if (!err) {
2806881f68fSopenharmony_ci		err = subdir_addpath(d, to, &newto);
2816881f68fSopenharmony_ci		if (!err) {
2826881f68fSopenharmony_ci			err = fuse_fs_link(d->next, newfrom, newto);
2836881f68fSopenharmony_ci			free(newto);
2846881f68fSopenharmony_ci		}
2856881f68fSopenharmony_ci		free(newfrom);
2866881f68fSopenharmony_ci	}
2876881f68fSopenharmony_ci	return err;
2886881f68fSopenharmony_ci}
2896881f68fSopenharmony_ci
2906881f68fSopenharmony_cistatic int subdir_chmod(const char *path, mode_t mode,
2916881f68fSopenharmony_ci			struct fuse_file_info *fi)
2926881f68fSopenharmony_ci{
2936881f68fSopenharmony_ci	struct subdir *d = subdir_get();
2946881f68fSopenharmony_ci	char *newpath;
2956881f68fSopenharmony_ci	int err = subdir_addpath(d, path, &newpath);
2966881f68fSopenharmony_ci	if (!err) {
2976881f68fSopenharmony_ci		err = fuse_fs_chmod(d->next, newpath, mode, fi);
2986881f68fSopenharmony_ci		free(newpath);
2996881f68fSopenharmony_ci	}
3006881f68fSopenharmony_ci	return err;
3016881f68fSopenharmony_ci}
3026881f68fSopenharmony_ci
3036881f68fSopenharmony_cistatic int subdir_chown(const char *path, uid_t uid, gid_t gid,
3046881f68fSopenharmony_ci			struct fuse_file_info *fi)
3056881f68fSopenharmony_ci{
3066881f68fSopenharmony_ci	struct subdir *d = subdir_get();
3076881f68fSopenharmony_ci	char *newpath;
3086881f68fSopenharmony_ci	int err = subdir_addpath(d, path, &newpath);
3096881f68fSopenharmony_ci	if (!err) {
3106881f68fSopenharmony_ci		err = fuse_fs_chown(d->next, newpath, uid, gid, fi);
3116881f68fSopenharmony_ci		free(newpath);
3126881f68fSopenharmony_ci	}
3136881f68fSopenharmony_ci	return err;
3146881f68fSopenharmony_ci}
3156881f68fSopenharmony_ci
3166881f68fSopenharmony_cistatic int subdir_truncate(const char *path, off_t size,
3176881f68fSopenharmony_ci			   struct fuse_file_info *fi)
3186881f68fSopenharmony_ci{
3196881f68fSopenharmony_ci	struct subdir *d = subdir_get();
3206881f68fSopenharmony_ci	char *newpath;
3216881f68fSopenharmony_ci	int err = subdir_addpath(d, path, &newpath);
3226881f68fSopenharmony_ci	if (!err) {
3236881f68fSopenharmony_ci		err = fuse_fs_truncate(d->next, newpath, size, fi);
3246881f68fSopenharmony_ci		free(newpath);
3256881f68fSopenharmony_ci	}
3266881f68fSopenharmony_ci	return err;
3276881f68fSopenharmony_ci}
3286881f68fSopenharmony_ci
3296881f68fSopenharmony_cistatic int subdir_utimens(const char *path, const struct timespec ts[2],
3306881f68fSopenharmony_ci			  struct fuse_file_info *fi)
3316881f68fSopenharmony_ci{
3326881f68fSopenharmony_ci	struct subdir *d = subdir_get();
3336881f68fSopenharmony_ci	char *newpath;
3346881f68fSopenharmony_ci	int err = subdir_addpath(d, path, &newpath);
3356881f68fSopenharmony_ci	if (!err) {
3366881f68fSopenharmony_ci		err = fuse_fs_utimens(d->next, newpath, ts, fi);
3376881f68fSopenharmony_ci		free(newpath);
3386881f68fSopenharmony_ci	}
3396881f68fSopenharmony_ci	return err;
3406881f68fSopenharmony_ci}
3416881f68fSopenharmony_ci
3426881f68fSopenharmony_cistatic int subdir_create(const char *path, mode_t mode,
3436881f68fSopenharmony_ci			 struct fuse_file_info *fi)
3446881f68fSopenharmony_ci{
3456881f68fSopenharmony_ci	struct subdir *d = subdir_get();
3466881f68fSopenharmony_ci	char *newpath;
3476881f68fSopenharmony_ci	int err = subdir_addpath(d, path, &newpath);
3486881f68fSopenharmony_ci	if (!err) {
3496881f68fSopenharmony_ci		err = fuse_fs_create(d->next, newpath, mode, fi);
3506881f68fSopenharmony_ci		free(newpath);
3516881f68fSopenharmony_ci	}
3526881f68fSopenharmony_ci	return err;
3536881f68fSopenharmony_ci}
3546881f68fSopenharmony_ci
3556881f68fSopenharmony_cistatic int subdir_open(const char *path, struct fuse_file_info *fi)
3566881f68fSopenharmony_ci{
3576881f68fSopenharmony_ci	struct subdir *d = subdir_get();
3586881f68fSopenharmony_ci	char *newpath;
3596881f68fSopenharmony_ci	int err = subdir_addpath(d, path, &newpath);
3606881f68fSopenharmony_ci	if (!err) {
3616881f68fSopenharmony_ci		err = fuse_fs_open(d->next, newpath, fi);
3626881f68fSopenharmony_ci		free(newpath);
3636881f68fSopenharmony_ci	}
3646881f68fSopenharmony_ci	return err;
3656881f68fSopenharmony_ci}
3666881f68fSopenharmony_ci
3676881f68fSopenharmony_cistatic int subdir_read_buf(const char *path, struct fuse_bufvec **bufp,
3686881f68fSopenharmony_ci			   size_t size, off_t offset, struct fuse_file_info *fi)
3696881f68fSopenharmony_ci{
3706881f68fSopenharmony_ci	struct subdir *d = subdir_get();
3716881f68fSopenharmony_ci	char *newpath;
3726881f68fSopenharmony_ci	int err = subdir_addpath(d, path, &newpath);
3736881f68fSopenharmony_ci	if (!err) {
3746881f68fSopenharmony_ci		err = fuse_fs_read_buf(d->next, newpath, bufp, size, offset, fi);
3756881f68fSopenharmony_ci		free(newpath);
3766881f68fSopenharmony_ci	}
3776881f68fSopenharmony_ci	return err;
3786881f68fSopenharmony_ci}
3796881f68fSopenharmony_ci
3806881f68fSopenharmony_cistatic int subdir_write_buf(const char *path, struct fuse_bufvec *buf,
3816881f68fSopenharmony_ci			off_t offset, struct fuse_file_info *fi)
3826881f68fSopenharmony_ci{
3836881f68fSopenharmony_ci	struct subdir *d = subdir_get();
3846881f68fSopenharmony_ci	char *newpath;
3856881f68fSopenharmony_ci	int err = subdir_addpath(d, path, &newpath);
3866881f68fSopenharmony_ci	if (!err) {
3876881f68fSopenharmony_ci		err = fuse_fs_write_buf(d->next, newpath, buf, offset, fi);
3886881f68fSopenharmony_ci		free(newpath);
3896881f68fSopenharmony_ci	}
3906881f68fSopenharmony_ci	return err;
3916881f68fSopenharmony_ci}
3926881f68fSopenharmony_ci
3936881f68fSopenharmony_cistatic int subdir_statfs(const char *path, struct statvfs *stbuf)
3946881f68fSopenharmony_ci{
3956881f68fSopenharmony_ci	struct subdir *d = subdir_get();
3966881f68fSopenharmony_ci	char *newpath;
3976881f68fSopenharmony_ci	int err = subdir_addpath(d, path, &newpath);
3986881f68fSopenharmony_ci	if (!err) {
3996881f68fSopenharmony_ci		err = fuse_fs_statfs(d->next, newpath, stbuf);
4006881f68fSopenharmony_ci		free(newpath);
4016881f68fSopenharmony_ci	}
4026881f68fSopenharmony_ci	return err;
4036881f68fSopenharmony_ci}
4046881f68fSopenharmony_ci
4056881f68fSopenharmony_cistatic int subdir_flush(const char *path, struct fuse_file_info *fi)
4066881f68fSopenharmony_ci{
4076881f68fSopenharmony_ci	struct subdir *d = subdir_get();
4086881f68fSopenharmony_ci	char *newpath;
4096881f68fSopenharmony_ci	int err = subdir_addpath(d, path, &newpath);
4106881f68fSopenharmony_ci	if (!err) {
4116881f68fSopenharmony_ci		err = fuse_fs_flush(d->next, newpath, fi);
4126881f68fSopenharmony_ci		free(newpath);
4136881f68fSopenharmony_ci	}
4146881f68fSopenharmony_ci	return err;
4156881f68fSopenharmony_ci}
4166881f68fSopenharmony_ci
4176881f68fSopenharmony_cistatic int subdir_release(const char *path, struct fuse_file_info *fi)
4186881f68fSopenharmony_ci{
4196881f68fSopenharmony_ci	struct subdir *d = subdir_get();
4206881f68fSopenharmony_ci	char *newpath;
4216881f68fSopenharmony_ci	int err = subdir_addpath(d, path, &newpath);
4226881f68fSopenharmony_ci	if (!err) {
4236881f68fSopenharmony_ci		err = fuse_fs_release(d->next, newpath, fi);
4246881f68fSopenharmony_ci		free(newpath);
4256881f68fSopenharmony_ci	}
4266881f68fSopenharmony_ci	return err;
4276881f68fSopenharmony_ci}
4286881f68fSopenharmony_ci
4296881f68fSopenharmony_cistatic int subdir_fsync(const char *path, int isdatasync,
4306881f68fSopenharmony_ci			struct fuse_file_info *fi)
4316881f68fSopenharmony_ci{
4326881f68fSopenharmony_ci	struct subdir *d = subdir_get();
4336881f68fSopenharmony_ci	char *newpath;
4346881f68fSopenharmony_ci	int err = subdir_addpath(d, path, &newpath);
4356881f68fSopenharmony_ci	if (!err) {
4366881f68fSopenharmony_ci		err = fuse_fs_fsync(d->next, newpath, isdatasync, fi);
4376881f68fSopenharmony_ci		free(newpath);
4386881f68fSopenharmony_ci	}
4396881f68fSopenharmony_ci	return err;
4406881f68fSopenharmony_ci}
4416881f68fSopenharmony_ci
4426881f68fSopenharmony_cistatic int subdir_fsyncdir(const char *path, int isdatasync,
4436881f68fSopenharmony_ci			   struct fuse_file_info *fi)
4446881f68fSopenharmony_ci{
4456881f68fSopenharmony_ci	struct subdir *d = subdir_get();
4466881f68fSopenharmony_ci	char *newpath;
4476881f68fSopenharmony_ci	int err = subdir_addpath(d, path, &newpath);
4486881f68fSopenharmony_ci	if (!err) {
4496881f68fSopenharmony_ci		err = fuse_fs_fsyncdir(d->next, newpath, isdatasync, fi);
4506881f68fSopenharmony_ci		free(newpath);
4516881f68fSopenharmony_ci	}
4526881f68fSopenharmony_ci	return err;
4536881f68fSopenharmony_ci}
4546881f68fSopenharmony_ci
4556881f68fSopenharmony_cistatic int subdir_setxattr(const char *path, const char *name,
4566881f68fSopenharmony_ci			   const char *value, size_t size, int flags)
4576881f68fSopenharmony_ci{
4586881f68fSopenharmony_ci	struct subdir *d = subdir_get();
4596881f68fSopenharmony_ci	char *newpath;
4606881f68fSopenharmony_ci	int err = subdir_addpath(d, path, &newpath);
4616881f68fSopenharmony_ci	if (!err) {
4626881f68fSopenharmony_ci		err = fuse_fs_setxattr(d->next, newpath, name, value, size,
4636881f68fSopenharmony_ci				       flags);
4646881f68fSopenharmony_ci		free(newpath);
4656881f68fSopenharmony_ci	}
4666881f68fSopenharmony_ci	return err;
4676881f68fSopenharmony_ci}
4686881f68fSopenharmony_ci
4696881f68fSopenharmony_cistatic int subdir_getxattr(const char *path, const char *name, char *value,
4706881f68fSopenharmony_ci			   size_t size)
4716881f68fSopenharmony_ci{
4726881f68fSopenharmony_ci	struct subdir *d = subdir_get();
4736881f68fSopenharmony_ci	char *newpath;
4746881f68fSopenharmony_ci	int err = subdir_addpath(d, path, &newpath);
4756881f68fSopenharmony_ci	if (!err) {
4766881f68fSopenharmony_ci		err = fuse_fs_getxattr(d->next, newpath, name, value, size);
4776881f68fSopenharmony_ci		free(newpath);
4786881f68fSopenharmony_ci	}
4796881f68fSopenharmony_ci	return err;
4806881f68fSopenharmony_ci}
4816881f68fSopenharmony_ci
4826881f68fSopenharmony_cistatic int subdir_listxattr(const char *path, char *list, size_t size)
4836881f68fSopenharmony_ci{
4846881f68fSopenharmony_ci	struct subdir *d = subdir_get();
4856881f68fSopenharmony_ci	char *newpath;
4866881f68fSopenharmony_ci	int err = subdir_addpath(d, path, &newpath);
4876881f68fSopenharmony_ci	if (!err) {
4886881f68fSopenharmony_ci		err = fuse_fs_listxattr(d->next, newpath, list, size);
4896881f68fSopenharmony_ci		free(newpath);
4906881f68fSopenharmony_ci	}
4916881f68fSopenharmony_ci	return err;
4926881f68fSopenharmony_ci}
4936881f68fSopenharmony_ci
4946881f68fSopenharmony_cistatic int subdir_removexattr(const char *path, const char *name)
4956881f68fSopenharmony_ci{
4966881f68fSopenharmony_ci	struct subdir *d = subdir_get();
4976881f68fSopenharmony_ci	char *newpath;
4986881f68fSopenharmony_ci	int err = subdir_addpath(d, path, &newpath);
4996881f68fSopenharmony_ci	if (!err) {
5006881f68fSopenharmony_ci		err = fuse_fs_removexattr(d->next, newpath, name);
5016881f68fSopenharmony_ci		free(newpath);
5026881f68fSopenharmony_ci	}
5036881f68fSopenharmony_ci	return err;
5046881f68fSopenharmony_ci}
5056881f68fSopenharmony_ci
5066881f68fSopenharmony_cistatic int subdir_lock(const char *path, struct fuse_file_info *fi, int cmd,
5076881f68fSopenharmony_ci		       struct flock *lock)
5086881f68fSopenharmony_ci{
5096881f68fSopenharmony_ci	struct subdir *d = subdir_get();
5106881f68fSopenharmony_ci	char *newpath;
5116881f68fSopenharmony_ci	int err = subdir_addpath(d, path, &newpath);
5126881f68fSopenharmony_ci	if (!err) {
5136881f68fSopenharmony_ci		err = fuse_fs_lock(d->next, newpath, fi, cmd, lock);
5146881f68fSopenharmony_ci		free(newpath);
5156881f68fSopenharmony_ci	}
5166881f68fSopenharmony_ci	return err;
5176881f68fSopenharmony_ci}
5186881f68fSopenharmony_ci
5196881f68fSopenharmony_cistatic int subdir_flock(const char *path, struct fuse_file_info *fi, int op)
5206881f68fSopenharmony_ci{
5216881f68fSopenharmony_ci	struct subdir *d = subdir_get();
5226881f68fSopenharmony_ci	char *newpath;
5236881f68fSopenharmony_ci	int err = subdir_addpath(d, path, &newpath);
5246881f68fSopenharmony_ci	if (!err) {
5256881f68fSopenharmony_ci		err = fuse_fs_flock(d->next, newpath, fi, op);
5266881f68fSopenharmony_ci		free(newpath);
5276881f68fSopenharmony_ci	}
5286881f68fSopenharmony_ci	return err;
5296881f68fSopenharmony_ci}
5306881f68fSopenharmony_ci
5316881f68fSopenharmony_cistatic int subdir_bmap(const char *path, size_t blocksize, uint64_t *idx)
5326881f68fSopenharmony_ci{
5336881f68fSopenharmony_ci	struct subdir *d = subdir_get();
5346881f68fSopenharmony_ci	char *newpath;
5356881f68fSopenharmony_ci	int err = subdir_addpath(d, path, &newpath);
5366881f68fSopenharmony_ci	if (!err) {
5376881f68fSopenharmony_ci		err = fuse_fs_bmap(d->next, newpath, blocksize, idx);
5386881f68fSopenharmony_ci		free(newpath);
5396881f68fSopenharmony_ci	}
5406881f68fSopenharmony_ci	return err;
5416881f68fSopenharmony_ci}
5426881f68fSopenharmony_ci
5436881f68fSopenharmony_cistatic off_t subdir_lseek(const char *path, off_t off, int whence,
5446881f68fSopenharmony_ci			  struct fuse_file_info *fi)
5456881f68fSopenharmony_ci{
5466881f68fSopenharmony_ci	struct subdir *ic = subdir_get();
5476881f68fSopenharmony_ci	char *newpath;
5486881f68fSopenharmony_ci	int res = subdir_addpath(ic, path, &newpath);
5496881f68fSopenharmony_ci	if (!res) {
5506881f68fSopenharmony_ci		res = fuse_fs_lseek(ic->next, newpath, off, whence, fi);
5516881f68fSopenharmony_ci		free(newpath);
5526881f68fSopenharmony_ci	}
5536881f68fSopenharmony_ci	return res;
5546881f68fSopenharmony_ci}
5556881f68fSopenharmony_ci
5566881f68fSopenharmony_cistatic void *subdir_init(struct fuse_conn_info *conn,
5576881f68fSopenharmony_ci			 struct fuse_config *cfg)
5586881f68fSopenharmony_ci{
5596881f68fSopenharmony_ci	struct subdir *d = subdir_get();
5606881f68fSopenharmony_ci	fuse_fs_init(d->next, conn, cfg);
5616881f68fSopenharmony_ci	/* Don't touch cfg->nullpath_ok, we can work with
5626881f68fSopenharmony_ci	   either */
5636881f68fSopenharmony_ci	return d;
5646881f68fSopenharmony_ci}
5656881f68fSopenharmony_ci
5666881f68fSopenharmony_cistatic void subdir_destroy(void *data)
5676881f68fSopenharmony_ci{
5686881f68fSopenharmony_ci	struct subdir *d = data;
5696881f68fSopenharmony_ci	fuse_fs_destroy(d->next);
5706881f68fSopenharmony_ci	free(d->base);
5716881f68fSopenharmony_ci	free(d);
5726881f68fSopenharmony_ci}
5736881f68fSopenharmony_ci
5746881f68fSopenharmony_cistatic const struct fuse_operations subdir_oper = {
5756881f68fSopenharmony_ci	.destroy	= subdir_destroy,
5766881f68fSopenharmony_ci	.init		= subdir_init,
5776881f68fSopenharmony_ci	.getattr	= subdir_getattr,
5786881f68fSopenharmony_ci	.access		= subdir_access,
5796881f68fSopenharmony_ci	.readlink	= subdir_readlink,
5806881f68fSopenharmony_ci	.opendir	= subdir_opendir,
5816881f68fSopenharmony_ci	.readdir	= subdir_readdir,
5826881f68fSopenharmony_ci	.releasedir	= subdir_releasedir,
5836881f68fSopenharmony_ci	.mknod		= subdir_mknod,
5846881f68fSopenharmony_ci	.mkdir		= subdir_mkdir,
5856881f68fSopenharmony_ci	.symlink	= subdir_symlink,
5866881f68fSopenharmony_ci	.unlink		= subdir_unlink,
5876881f68fSopenharmony_ci	.rmdir		= subdir_rmdir,
5886881f68fSopenharmony_ci	.rename		= subdir_rename,
5896881f68fSopenharmony_ci	.link		= subdir_link,
5906881f68fSopenharmony_ci	.chmod		= subdir_chmod,
5916881f68fSopenharmony_ci	.chown		= subdir_chown,
5926881f68fSopenharmony_ci	.truncate	= subdir_truncate,
5936881f68fSopenharmony_ci	.utimens	= subdir_utimens,
5946881f68fSopenharmony_ci	.create		= subdir_create,
5956881f68fSopenharmony_ci	.open		= subdir_open,
5966881f68fSopenharmony_ci	.read_buf	= subdir_read_buf,
5976881f68fSopenharmony_ci	.write_buf	= subdir_write_buf,
5986881f68fSopenharmony_ci	.statfs		= subdir_statfs,
5996881f68fSopenharmony_ci	.flush		= subdir_flush,
6006881f68fSopenharmony_ci	.release	= subdir_release,
6016881f68fSopenharmony_ci	.fsync		= subdir_fsync,
6026881f68fSopenharmony_ci	.fsyncdir	= subdir_fsyncdir,
6036881f68fSopenharmony_ci	.setxattr	= subdir_setxattr,
6046881f68fSopenharmony_ci	.getxattr	= subdir_getxattr,
6056881f68fSopenharmony_ci	.listxattr	= subdir_listxattr,
6066881f68fSopenharmony_ci	.removexattr	= subdir_removexattr,
6076881f68fSopenharmony_ci	.lock		= subdir_lock,
6086881f68fSopenharmony_ci	.flock		= subdir_flock,
6096881f68fSopenharmony_ci	.bmap		= subdir_bmap,
6106881f68fSopenharmony_ci	.lseek		= subdir_lseek,
6116881f68fSopenharmony_ci};
6126881f68fSopenharmony_ci
6136881f68fSopenharmony_cistatic const struct fuse_opt subdir_opts[] = {
6146881f68fSopenharmony_ci	FUSE_OPT_KEY("-h", 0),
6156881f68fSopenharmony_ci	FUSE_OPT_KEY("--help", 0),
6166881f68fSopenharmony_ci	{ "subdir=%s", offsetof(struct subdir, base), 0 },
6176881f68fSopenharmony_ci	{ "rellinks", offsetof(struct subdir, rellinks), 1 },
6186881f68fSopenharmony_ci	{ "norellinks", offsetof(struct subdir, rellinks), 0 },
6196881f68fSopenharmony_ci	FUSE_OPT_END
6206881f68fSopenharmony_ci};
6216881f68fSopenharmony_ci
6226881f68fSopenharmony_cistatic void subdir_help(void)
6236881f68fSopenharmony_ci{
6246881f68fSopenharmony_ci	printf(
6256881f68fSopenharmony_ci"    -o subdir=DIR	    prepend this directory to all paths (mandatory)\n"
6266881f68fSopenharmony_ci"    -o [no]rellinks	    transform absolute symlinks to relative\n");
6276881f68fSopenharmony_ci}
6286881f68fSopenharmony_ci
6296881f68fSopenharmony_cistatic int subdir_opt_proc(void *data, const char *arg, int key,
6306881f68fSopenharmony_ci			   struct fuse_args *outargs)
6316881f68fSopenharmony_ci{
6326881f68fSopenharmony_ci	(void) data; (void) arg; (void) outargs;
6336881f68fSopenharmony_ci
6346881f68fSopenharmony_ci	if (!key) {
6356881f68fSopenharmony_ci		subdir_help();
6366881f68fSopenharmony_ci		return -1;
6376881f68fSopenharmony_ci	}
6386881f68fSopenharmony_ci
6396881f68fSopenharmony_ci	return 1;
6406881f68fSopenharmony_ci}
6416881f68fSopenharmony_ci
6426881f68fSopenharmony_cistatic struct fuse_fs *subdir_new(struct fuse_args *args,
6436881f68fSopenharmony_ci				  struct fuse_fs *next[])
6446881f68fSopenharmony_ci{
6456881f68fSopenharmony_ci	struct fuse_fs *fs;
6466881f68fSopenharmony_ci	struct subdir *d;
6476881f68fSopenharmony_ci
6486881f68fSopenharmony_ci	d = calloc(1, sizeof(struct subdir));
6496881f68fSopenharmony_ci	if (d == NULL) {
6506881f68fSopenharmony_ci		fuse_log(FUSE_LOG_ERR, "fuse-subdir: memory allocation failed\n");
6516881f68fSopenharmony_ci		return NULL;
6526881f68fSopenharmony_ci	}
6536881f68fSopenharmony_ci
6546881f68fSopenharmony_ci	if (fuse_opt_parse(args, d, subdir_opts, subdir_opt_proc) == -1)
6556881f68fSopenharmony_ci		goto out_free;
6566881f68fSopenharmony_ci
6576881f68fSopenharmony_ci	if (!next[0] || next[1]) {
6586881f68fSopenharmony_ci		fuse_log(FUSE_LOG_ERR, "fuse-subdir: exactly one next filesystem required\n");
6596881f68fSopenharmony_ci		goto out_free;
6606881f68fSopenharmony_ci	}
6616881f68fSopenharmony_ci
6626881f68fSopenharmony_ci	if (!d->base) {
6636881f68fSopenharmony_ci		fuse_log(FUSE_LOG_ERR, "fuse-subdir: missing 'subdir' option\n");
6646881f68fSopenharmony_ci		goto out_free;
6656881f68fSopenharmony_ci	}
6666881f68fSopenharmony_ci
6676881f68fSopenharmony_ci	if (d->base[0] && d->base[strlen(d->base)-1] != '/') {
6686881f68fSopenharmony_ci		char *tmp = realloc(d->base, strlen(d->base) + 2);
6696881f68fSopenharmony_ci		if (!tmp) {
6706881f68fSopenharmony_ci			fuse_log(FUSE_LOG_ERR, "fuse-subdir: memory allocation failed\n");
6716881f68fSopenharmony_ci			goto out_free;
6726881f68fSopenharmony_ci		}
6736881f68fSopenharmony_ci		d->base = tmp;
6746881f68fSopenharmony_ci		strcat(d->base, "/");
6756881f68fSopenharmony_ci	}
6766881f68fSopenharmony_ci	d->baselen = strlen(d->base);
6776881f68fSopenharmony_ci	d->next = next[0];
6786881f68fSopenharmony_ci	fs = fuse_fs_new(&subdir_oper, sizeof(subdir_oper), d);
6796881f68fSopenharmony_ci	if (!fs)
6806881f68fSopenharmony_ci		goto out_free;
6816881f68fSopenharmony_ci	return fs;
6826881f68fSopenharmony_ci
6836881f68fSopenharmony_ciout_free:
6846881f68fSopenharmony_ci	free(d->base);
6856881f68fSopenharmony_ci	free(d);
6866881f68fSopenharmony_ci	return NULL;
6876881f68fSopenharmony_ci}
6886881f68fSopenharmony_ci
6896881f68fSopenharmony_ciFUSE_REGISTER_MODULE(subdir, subdir_new);
690