xref: /third_party/libfuse/lib/modules/iconv.c (revision 6881f68f)
16881f68fSopenharmony_ci/*
26881f68fSopenharmony_ci  fuse iconv module: file name charset conversion
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#include <iconv.h>
186881f68fSopenharmony_ci#include <pthread.h>
196881f68fSopenharmony_ci#include <locale.h>
206881f68fSopenharmony_ci#include <langinfo.h>
216881f68fSopenharmony_ci
226881f68fSopenharmony_cistruct iconv {
236881f68fSopenharmony_ci	struct fuse_fs *next;
246881f68fSopenharmony_ci	pthread_mutex_t lock;
256881f68fSopenharmony_ci	char *from_code;
266881f68fSopenharmony_ci	char *to_code;
276881f68fSopenharmony_ci	iconv_t tofs;
286881f68fSopenharmony_ci	iconv_t fromfs;
296881f68fSopenharmony_ci};
306881f68fSopenharmony_ci
316881f68fSopenharmony_cistruct iconv_dh {
326881f68fSopenharmony_ci	struct iconv *ic;
336881f68fSopenharmony_ci	void *prev_buf;
346881f68fSopenharmony_ci	fuse_fill_dir_t prev_filler;
356881f68fSopenharmony_ci};
366881f68fSopenharmony_ci
376881f68fSopenharmony_cistatic struct iconv *iconv_get(void)
386881f68fSopenharmony_ci{
396881f68fSopenharmony_ci	return fuse_get_context()->private_data;
406881f68fSopenharmony_ci}
416881f68fSopenharmony_ci
426881f68fSopenharmony_cistatic int iconv_convpath(struct iconv *ic, const char *path, char **newpathp,
436881f68fSopenharmony_ci			  int fromfs)
446881f68fSopenharmony_ci{
456881f68fSopenharmony_ci	size_t pathlen;
466881f68fSopenharmony_ci	size_t newpathlen;
476881f68fSopenharmony_ci	char *newpath;
486881f68fSopenharmony_ci	size_t plen;
496881f68fSopenharmony_ci	char *p;
506881f68fSopenharmony_ci	size_t res;
516881f68fSopenharmony_ci	int err;
526881f68fSopenharmony_ci
536881f68fSopenharmony_ci	if (path == NULL) {
546881f68fSopenharmony_ci		*newpathp = NULL;
556881f68fSopenharmony_ci		return 0;
566881f68fSopenharmony_ci	}
576881f68fSopenharmony_ci
586881f68fSopenharmony_ci	pathlen = strlen(path);
596881f68fSopenharmony_ci	newpathlen = pathlen * 4;
606881f68fSopenharmony_ci	newpath = malloc(newpathlen + 1);
616881f68fSopenharmony_ci	if (!newpath)
626881f68fSopenharmony_ci		return -ENOMEM;
636881f68fSopenharmony_ci
646881f68fSopenharmony_ci	plen = newpathlen;
656881f68fSopenharmony_ci	p = newpath;
666881f68fSopenharmony_ci	pthread_mutex_lock(&ic->lock);
676881f68fSopenharmony_ci	do {
686881f68fSopenharmony_ci		res = iconv(fromfs ? ic->fromfs : ic->tofs, (char **) &path,
696881f68fSopenharmony_ci			    &pathlen, &p, &plen);
706881f68fSopenharmony_ci		if (res == (size_t) -1) {
716881f68fSopenharmony_ci			char *tmp;
726881f68fSopenharmony_ci			size_t inc;
736881f68fSopenharmony_ci
746881f68fSopenharmony_ci			err = -EILSEQ;
756881f68fSopenharmony_ci			if (errno != E2BIG)
766881f68fSopenharmony_ci				goto err;
776881f68fSopenharmony_ci
786881f68fSopenharmony_ci			inc = (pathlen + 1) * 4;
796881f68fSopenharmony_ci			newpathlen += inc;
806881f68fSopenharmony_ci			int dp = p - newpath;
816881f68fSopenharmony_ci			tmp = realloc(newpath, newpathlen + 1);
826881f68fSopenharmony_ci			err = -ENOMEM;
836881f68fSopenharmony_ci			if (!tmp)
846881f68fSopenharmony_ci				goto err;
856881f68fSopenharmony_ci
866881f68fSopenharmony_ci			p = tmp + dp;
876881f68fSopenharmony_ci			plen += inc;
886881f68fSopenharmony_ci			newpath = tmp;
896881f68fSopenharmony_ci		}
906881f68fSopenharmony_ci	} while (res == (size_t) -1);
916881f68fSopenharmony_ci	pthread_mutex_unlock(&ic->lock);
926881f68fSopenharmony_ci	*p = '\0';
936881f68fSopenharmony_ci	*newpathp = newpath;
946881f68fSopenharmony_ci	return 0;
956881f68fSopenharmony_ci
966881f68fSopenharmony_cierr:
976881f68fSopenharmony_ci	iconv(fromfs ? ic->fromfs : ic->tofs, NULL, NULL, NULL, NULL);
986881f68fSopenharmony_ci	pthread_mutex_unlock(&ic->lock);
996881f68fSopenharmony_ci	free(newpath);
1006881f68fSopenharmony_ci	return err;
1016881f68fSopenharmony_ci}
1026881f68fSopenharmony_ci
1036881f68fSopenharmony_cistatic int iconv_getattr(const char *path, struct stat *stbuf,
1046881f68fSopenharmony_ci			 struct fuse_file_info *fi)
1056881f68fSopenharmony_ci{
1066881f68fSopenharmony_ci	struct iconv *ic = iconv_get();
1076881f68fSopenharmony_ci	char *newpath;
1086881f68fSopenharmony_ci	int err = iconv_convpath(ic, path, &newpath, 0);
1096881f68fSopenharmony_ci	if (!err) {
1106881f68fSopenharmony_ci		err = fuse_fs_getattr(ic->next, newpath, stbuf, fi);
1116881f68fSopenharmony_ci		free(newpath);
1126881f68fSopenharmony_ci	}
1136881f68fSopenharmony_ci	return err;
1146881f68fSopenharmony_ci}
1156881f68fSopenharmony_ci
1166881f68fSopenharmony_cistatic int iconv_access(const char *path, int mask)
1176881f68fSopenharmony_ci{
1186881f68fSopenharmony_ci	struct iconv *ic = iconv_get();
1196881f68fSopenharmony_ci	char *newpath;
1206881f68fSopenharmony_ci	int err = iconv_convpath(ic, path, &newpath, 0);
1216881f68fSopenharmony_ci	if (!err) {
1226881f68fSopenharmony_ci		err = fuse_fs_access(ic->next, newpath, mask);
1236881f68fSopenharmony_ci		free(newpath);
1246881f68fSopenharmony_ci	}
1256881f68fSopenharmony_ci	return err;
1266881f68fSopenharmony_ci}
1276881f68fSopenharmony_ci
1286881f68fSopenharmony_cistatic int iconv_readlink(const char *path, char *buf, size_t size)
1296881f68fSopenharmony_ci{
1306881f68fSopenharmony_ci	struct iconv *ic = iconv_get();
1316881f68fSopenharmony_ci	char *newpath;
1326881f68fSopenharmony_ci	int err = iconv_convpath(ic, path, &newpath, 0);
1336881f68fSopenharmony_ci	if (!err) {
1346881f68fSopenharmony_ci		err = fuse_fs_readlink(ic->next, newpath, buf, size);
1356881f68fSopenharmony_ci		if (!err) {
1366881f68fSopenharmony_ci			char *newlink;
1376881f68fSopenharmony_ci			err = iconv_convpath(ic, buf, &newlink, 1);
1386881f68fSopenharmony_ci			if (!err) {
1396881f68fSopenharmony_ci				strncpy(buf, newlink, size - 1);
1406881f68fSopenharmony_ci				buf[size - 1] = '\0';
1416881f68fSopenharmony_ci				free(newlink);
1426881f68fSopenharmony_ci			}
1436881f68fSopenharmony_ci		}
1446881f68fSopenharmony_ci		free(newpath);
1456881f68fSopenharmony_ci	}
1466881f68fSopenharmony_ci	return err;
1476881f68fSopenharmony_ci}
1486881f68fSopenharmony_ci
1496881f68fSopenharmony_cistatic int iconv_opendir(const char *path, struct fuse_file_info *fi)
1506881f68fSopenharmony_ci{
1516881f68fSopenharmony_ci	struct iconv *ic = iconv_get();
1526881f68fSopenharmony_ci	char *newpath;
1536881f68fSopenharmony_ci	int err = iconv_convpath(ic, path, &newpath, 0);
1546881f68fSopenharmony_ci	if (!err) {
1556881f68fSopenharmony_ci		err = fuse_fs_opendir(ic->next, newpath, fi);
1566881f68fSopenharmony_ci		free(newpath);
1576881f68fSopenharmony_ci	}
1586881f68fSopenharmony_ci	return err;
1596881f68fSopenharmony_ci}
1606881f68fSopenharmony_ci
1616881f68fSopenharmony_cistatic int iconv_dir_fill(void *buf, const char *name,
1626881f68fSopenharmony_ci			  const struct stat *stbuf, off_t off,
1636881f68fSopenharmony_ci			  enum fuse_fill_dir_flags flags)
1646881f68fSopenharmony_ci{
1656881f68fSopenharmony_ci	struct iconv_dh *dh = buf;
1666881f68fSopenharmony_ci	char *newname;
1676881f68fSopenharmony_ci	int res = 0;
1686881f68fSopenharmony_ci	if (iconv_convpath(dh->ic, name, &newname, 1) == 0) {
1696881f68fSopenharmony_ci		res = dh->prev_filler(dh->prev_buf, newname, stbuf, off, flags);
1706881f68fSopenharmony_ci		free(newname);
1716881f68fSopenharmony_ci	}
1726881f68fSopenharmony_ci	return res;
1736881f68fSopenharmony_ci}
1746881f68fSopenharmony_ci
1756881f68fSopenharmony_cistatic int iconv_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
1766881f68fSopenharmony_ci			 off_t offset, struct fuse_file_info *fi,
1776881f68fSopenharmony_ci			 enum fuse_readdir_flags flags)
1786881f68fSopenharmony_ci{
1796881f68fSopenharmony_ci	struct iconv *ic = iconv_get();
1806881f68fSopenharmony_ci	char *newpath;
1816881f68fSopenharmony_ci	int err = iconv_convpath(ic, path, &newpath, 0);
1826881f68fSopenharmony_ci	if (!err) {
1836881f68fSopenharmony_ci		struct iconv_dh dh;
1846881f68fSopenharmony_ci		dh.ic = ic;
1856881f68fSopenharmony_ci		dh.prev_buf = buf;
1866881f68fSopenharmony_ci		dh.prev_filler = filler;
1876881f68fSopenharmony_ci		err = fuse_fs_readdir(ic->next, newpath, &dh, iconv_dir_fill,
1886881f68fSopenharmony_ci				      offset, fi, flags);
1896881f68fSopenharmony_ci		free(newpath);
1906881f68fSopenharmony_ci	}
1916881f68fSopenharmony_ci	return err;
1926881f68fSopenharmony_ci}
1936881f68fSopenharmony_ci
1946881f68fSopenharmony_cistatic int iconv_releasedir(const char *path, struct fuse_file_info *fi)
1956881f68fSopenharmony_ci{
1966881f68fSopenharmony_ci	struct iconv *ic = iconv_get();
1976881f68fSopenharmony_ci	char *newpath;
1986881f68fSopenharmony_ci	int err = iconv_convpath(ic, path, &newpath, 0);
1996881f68fSopenharmony_ci	if (!err) {
2006881f68fSopenharmony_ci		err = fuse_fs_releasedir(ic->next, newpath, fi);
2016881f68fSopenharmony_ci		free(newpath);
2026881f68fSopenharmony_ci	}
2036881f68fSopenharmony_ci	return err;
2046881f68fSopenharmony_ci}
2056881f68fSopenharmony_ci
2066881f68fSopenharmony_cistatic int iconv_mknod(const char *path, mode_t mode, dev_t rdev)
2076881f68fSopenharmony_ci{
2086881f68fSopenharmony_ci	struct iconv *ic = iconv_get();
2096881f68fSopenharmony_ci	char *newpath;
2106881f68fSopenharmony_ci	int err = iconv_convpath(ic, path, &newpath, 0);
2116881f68fSopenharmony_ci	if (!err) {
2126881f68fSopenharmony_ci		err = fuse_fs_mknod(ic->next, newpath, mode, rdev);
2136881f68fSopenharmony_ci		free(newpath);
2146881f68fSopenharmony_ci	}
2156881f68fSopenharmony_ci	return err;
2166881f68fSopenharmony_ci}
2176881f68fSopenharmony_ci
2186881f68fSopenharmony_cistatic int iconv_mkdir(const char *path, mode_t mode)
2196881f68fSopenharmony_ci{
2206881f68fSopenharmony_ci	struct iconv *ic = iconv_get();
2216881f68fSopenharmony_ci	char *newpath;
2226881f68fSopenharmony_ci	int err = iconv_convpath(ic, path, &newpath, 0);
2236881f68fSopenharmony_ci	if (!err) {
2246881f68fSopenharmony_ci		err = fuse_fs_mkdir(ic->next, newpath, mode);
2256881f68fSopenharmony_ci		free(newpath);
2266881f68fSopenharmony_ci	}
2276881f68fSopenharmony_ci	return err;
2286881f68fSopenharmony_ci}
2296881f68fSopenharmony_ci
2306881f68fSopenharmony_cistatic int iconv_unlink(const char *path)
2316881f68fSopenharmony_ci{
2326881f68fSopenharmony_ci	struct iconv *ic = iconv_get();
2336881f68fSopenharmony_ci	char *newpath;
2346881f68fSopenharmony_ci	int err = iconv_convpath(ic, path, &newpath, 0);
2356881f68fSopenharmony_ci	if (!err) {
2366881f68fSopenharmony_ci		err = fuse_fs_unlink(ic->next, newpath);
2376881f68fSopenharmony_ci		free(newpath);
2386881f68fSopenharmony_ci	}
2396881f68fSopenharmony_ci	return err;
2406881f68fSopenharmony_ci}
2416881f68fSopenharmony_ci
2426881f68fSopenharmony_cistatic int iconv_rmdir(const char *path)
2436881f68fSopenharmony_ci{
2446881f68fSopenharmony_ci	struct iconv *ic = iconv_get();
2456881f68fSopenharmony_ci	char *newpath;
2466881f68fSopenharmony_ci	int err = iconv_convpath(ic, path, &newpath, 0);
2476881f68fSopenharmony_ci	if (!err) {
2486881f68fSopenharmony_ci		err = fuse_fs_rmdir(ic->next, newpath);
2496881f68fSopenharmony_ci		free(newpath);
2506881f68fSopenharmony_ci	}
2516881f68fSopenharmony_ci	return err;
2526881f68fSopenharmony_ci}
2536881f68fSopenharmony_ci
2546881f68fSopenharmony_cistatic int iconv_symlink(const char *from, const char *to)
2556881f68fSopenharmony_ci{
2566881f68fSopenharmony_ci	struct iconv *ic = iconv_get();
2576881f68fSopenharmony_ci	char *newfrom;
2586881f68fSopenharmony_ci	char *newto;
2596881f68fSopenharmony_ci	int err = iconv_convpath(ic, from, &newfrom, 0);
2606881f68fSopenharmony_ci	if (!err) {
2616881f68fSopenharmony_ci		err = iconv_convpath(ic, to, &newto, 0);
2626881f68fSopenharmony_ci		if (!err) {
2636881f68fSopenharmony_ci			err = fuse_fs_symlink(ic->next, newfrom, newto);
2646881f68fSopenharmony_ci			free(newto);
2656881f68fSopenharmony_ci		}
2666881f68fSopenharmony_ci		free(newfrom);
2676881f68fSopenharmony_ci	}
2686881f68fSopenharmony_ci	return err;
2696881f68fSopenharmony_ci}
2706881f68fSopenharmony_ci
2716881f68fSopenharmony_cistatic int iconv_rename(const char *from, const char *to, unsigned int flags)
2726881f68fSopenharmony_ci{
2736881f68fSopenharmony_ci	struct iconv *ic = iconv_get();
2746881f68fSopenharmony_ci	char *newfrom;
2756881f68fSopenharmony_ci	char *newto;
2766881f68fSopenharmony_ci	int err = iconv_convpath(ic, from, &newfrom, 0);
2776881f68fSopenharmony_ci	if (!err) {
2786881f68fSopenharmony_ci		err = iconv_convpath(ic, to, &newto, 0);
2796881f68fSopenharmony_ci		if (!err) {
2806881f68fSopenharmony_ci			err = fuse_fs_rename(ic->next, newfrom, newto, flags);
2816881f68fSopenharmony_ci			free(newto);
2826881f68fSopenharmony_ci		}
2836881f68fSopenharmony_ci		free(newfrom);
2846881f68fSopenharmony_ci	}
2856881f68fSopenharmony_ci	return err;
2866881f68fSopenharmony_ci}
2876881f68fSopenharmony_ci
2886881f68fSopenharmony_cistatic int iconv_link(const char *from, const char *to)
2896881f68fSopenharmony_ci{
2906881f68fSopenharmony_ci	struct iconv *ic = iconv_get();
2916881f68fSopenharmony_ci	char *newfrom;
2926881f68fSopenharmony_ci	char *newto;
2936881f68fSopenharmony_ci	int err = iconv_convpath(ic, from, &newfrom, 0);
2946881f68fSopenharmony_ci	if (!err) {
2956881f68fSopenharmony_ci		err = iconv_convpath(ic, to, &newto, 0);
2966881f68fSopenharmony_ci		if (!err) {
2976881f68fSopenharmony_ci			err = fuse_fs_link(ic->next, newfrom, newto);
2986881f68fSopenharmony_ci			free(newto);
2996881f68fSopenharmony_ci		}
3006881f68fSopenharmony_ci		free(newfrom);
3016881f68fSopenharmony_ci	}
3026881f68fSopenharmony_ci	return err;
3036881f68fSopenharmony_ci}
3046881f68fSopenharmony_ci
3056881f68fSopenharmony_cistatic int iconv_chmod(const char *path, mode_t mode,
3066881f68fSopenharmony_ci		       struct fuse_file_info *fi)
3076881f68fSopenharmony_ci{
3086881f68fSopenharmony_ci	struct iconv *ic = iconv_get();
3096881f68fSopenharmony_ci	char *newpath;
3106881f68fSopenharmony_ci	int err = iconv_convpath(ic, path, &newpath, 0);
3116881f68fSopenharmony_ci	if (!err) {
3126881f68fSopenharmony_ci		err = fuse_fs_chmod(ic->next, newpath, mode, fi);
3136881f68fSopenharmony_ci		free(newpath);
3146881f68fSopenharmony_ci	}
3156881f68fSopenharmony_ci	return err;
3166881f68fSopenharmony_ci}
3176881f68fSopenharmony_ci
3186881f68fSopenharmony_cistatic int iconv_chown(const char *path, uid_t uid, gid_t gid,
3196881f68fSopenharmony_ci		       struct fuse_file_info *fi)
3206881f68fSopenharmony_ci{
3216881f68fSopenharmony_ci	struct iconv *ic = iconv_get();
3226881f68fSopenharmony_ci	char *newpath;
3236881f68fSopenharmony_ci	int err = iconv_convpath(ic, path, &newpath, 0);
3246881f68fSopenharmony_ci	if (!err) {
3256881f68fSopenharmony_ci		err = fuse_fs_chown(ic->next, newpath, uid, gid, fi);
3266881f68fSopenharmony_ci		free(newpath);
3276881f68fSopenharmony_ci	}
3286881f68fSopenharmony_ci	return err;
3296881f68fSopenharmony_ci}
3306881f68fSopenharmony_ci
3316881f68fSopenharmony_cistatic int iconv_truncate(const char *path, off_t size,
3326881f68fSopenharmony_ci			   struct fuse_file_info *fi)
3336881f68fSopenharmony_ci{
3346881f68fSopenharmony_ci	struct iconv *ic = iconv_get();
3356881f68fSopenharmony_ci	char *newpath;
3366881f68fSopenharmony_ci	int err = iconv_convpath(ic, path, &newpath, 0);
3376881f68fSopenharmony_ci	if (!err) {
3386881f68fSopenharmony_ci		err = fuse_fs_truncate(ic->next, newpath, size, fi);
3396881f68fSopenharmony_ci		free(newpath);
3406881f68fSopenharmony_ci	}
3416881f68fSopenharmony_ci	return err;
3426881f68fSopenharmony_ci}
3436881f68fSopenharmony_ci
3446881f68fSopenharmony_cistatic int iconv_utimens(const char *path, const struct timespec ts[2],
3456881f68fSopenharmony_ci			 struct fuse_file_info *fi)
3466881f68fSopenharmony_ci{
3476881f68fSopenharmony_ci	struct iconv *ic = iconv_get();
3486881f68fSopenharmony_ci	char *newpath;
3496881f68fSopenharmony_ci	int err = iconv_convpath(ic, path, &newpath, 0);
3506881f68fSopenharmony_ci	if (!err) {
3516881f68fSopenharmony_ci		err = fuse_fs_utimens(ic->next, newpath, ts, fi);
3526881f68fSopenharmony_ci		free(newpath);
3536881f68fSopenharmony_ci	}
3546881f68fSopenharmony_ci	return err;
3556881f68fSopenharmony_ci}
3566881f68fSopenharmony_ci
3576881f68fSopenharmony_cistatic int iconv_create(const char *path, mode_t mode,
3586881f68fSopenharmony_ci			struct fuse_file_info *fi)
3596881f68fSopenharmony_ci{
3606881f68fSopenharmony_ci	struct iconv *ic = iconv_get();
3616881f68fSopenharmony_ci	char *newpath;
3626881f68fSopenharmony_ci	int err = iconv_convpath(ic, path, &newpath, 0);
3636881f68fSopenharmony_ci	if (!err) {
3646881f68fSopenharmony_ci		err = fuse_fs_create(ic->next, newpath, mode, fi);
3656881f68fSopenharmony_ci		free(newpath);
3666881f68fSopenharmony_ci	}
3676881f68fSopenharmony_ci	return err;
3686881f68fSopenharmony_ci}
3696881f68fSopenharmony_ci
3706881f68fSopenharmony_cistatic int iconv_open_file(const char *path, struct fuse_file_info *fi)
3716881f68fSopenharmony_ci{
3726881f68fSopenharmony_ci	struct iconv *ic = iconv_get();
3736881f68fSopenharmony_ci	char *newpath;
3746881f68fSopenharmony_ci	int err = iconv_convpath(ic, path, &newpath, 0);
3756881f68fSopenharmony_ci	if (!err) {
3766881f68fSopenharmony_ci		err = fuse_fs_open(ic->next, newpath, fi);
3776881f68fSopenharmony_ci		free(newpath);
3786881f68fSopenharmony_ci	}
3796881f68fSopenharmony_ci	return err;
3806881f68fSopenharmony_ci}
3816881f68fSopenharmony_ci
3826881f68fSopenharmony_cistatic int iconv_read_buf(const char *path, struct fuse_bufvec **bufp,
3836881f68fSopenharmony_ci			  size_t size, off_t offset, struct fuse_file_info *fi)
3846881f68fSopenharmony_ci{
3856881f68fSopenharmony_ci	struct iconv *ic = iconv_get();
3866881f68fSopenharmony_ci	char *newpath;
3876881f68fSopenharmony_ci	int err = iconv_convpath(ic, path, &newpath, 0);
3886881f68fSopenharmony_ci	if (!err) {
3896881f68fSopenharmony_ci		err = fuse_fs_read_buf(ic->next, newpath, bufp, size, offset, fi);
3906881f68fSopenharmony_ci		free(newpath);
3916881f68fSopenharmony_ci	}
3926881f68fSopenharmony_ci	return err;
3936881f68fSopenharmony_ci}
3946881f68fSopenharmony_ci
3956881f68fSopenharmony_cistatic int iconv_write_buf(const char *path, struct fuse_bufvec *buf,
3966881f68fSopenharmony_ci			   off_t offset, struct fuse_file_info *fi)
3976881f68fSopenharmony_ci{
3986881f68fSopenharmony_ci	struct iconv *ic = iconv_get();
3996881f68fSopenharmony_ci	char *newpath;
4006881f68fSopenharmony_ci	int err = iconv_convpath(ic, path, &newpath, 0);
4016881f68fSopenharmony_ci	if (!err) {
4026881f68fSopenharmony_ci		err = fuse_fs_write_buf(ic->next, newpath, buf, offset, fi);
4036881f68fSopenharmony_ci		free(newpath);
4046881f68fSopenharmony_ci	}
4056881f68fSopenharmony_ci	return err;
4066881f68fSopenharmony_ci}
4076881f68fSopenharmony_ci
4086881f68fSopenharmony_cistatic int iconv_statfs(const char *path, struct statvfs *stbuf)
4096881f68fSopenharmony_ci{
4106881f68fSopenharmony_ci	struct iconv *ic = iconv_get();
4116881f68fSopenharmony_ci	char *newpath;
4126881f68fSopenharmony_ci	int err = iconv_convpath(ic, path, &newpath, 0);
4136881f68fSopenharmony_ci	if (!err) {
4146881f68fSopenharmony_ci		err = fuse_fs_statfs(ic->next, newpath, stbuf);
4156881f68fSopenharmony_ci		free(newpath);
4166881f68fSopenharmony_ci	}
4176881f68fSopenharmony_ci	return err;
4186881f68fSopenharmony_ci}
4196881f68fSopenharmony_ci
4206881f68fSopenharmony_cistatic int iconv_flush(const char *path, struct fuse_file_info *fi)
4216881f68fSopenharmony_ci{
4226881f68fSopenharmony_ci	struct iconv *ic = iconv_get();
4236881f68fSopenharmony_ci	char *newpath;
4246881f68fSopenharmony_ci	int err = iconv_convpath(ic, path, &newpath, 0);
4256881f68fSopenharmony_ci	if (!err) {
4266881f68fSopenharmony_ci		err = fuse_fs_flush(ic->next, newpath, fi);
4276881f68fSopenharmony_ci		free(newpath);
4286881f68fSopenharmony_ci	}
4296881f68fSopenharmony_ci	return err;
4306881f68fSopenharmony_ci}
4316881f68fSopenharmony_ci
4326881f68fSopenharmony_cistatic int iconv_release(const char *path, struct fuse_file_info *fi)
4336881f68fSopenharmony_ci{
4346881f68fSopenharmony_ci	struct iconv *ic = iconv_get();
4356881f68fSopenharmony_ci	char *newpath;
4366881f68fSopenharmony_ci	int err = iconv_convpath(ic, path, &newpath, 0);
4376881f68fSopenharmony_ci	if (!err) {
4386881f68fSopenharmony_ci		err = fuse_fs_release(ic->next, newpath, fi);
4396881f68fSopenharmony_ci		free(newpath);
4406881f68fSopenharmony_ci	}
4416881f68fSopenharmony_ci	return err;
4426881f68fSopenharmony_ci}
4436881f68fSopenharmony_ci
4446881f68fSopenharmony_cistatic int iconv_fsync(const char *path, int isdatasync,
4456881f68fSopenharmony_ci		       struct fuse_file_info *fi)
4466881f68fSopenharmony_ci{
4476881f68fSopenharmony_ci	struct iconv *ic = iconv_get();
4486881f68fSopenharmony_ci	char *newpath;
4496881f68fSopenharmony_ci	int err = iconv_convpath(ic, path, &newpath, 0);
4506881f68fSopenharmony_ci	if (!err) {
4516881f68fSopenharmony_ci		err = fuse_fs_fsync(ic->next, newpath, isdatasync, fi);
4526881f68fSopenharmony_ci		free(newpath);
4536881f68fSopenharmony_ci	}
4546881f68fSopenharmony_ci	return err;
4556881f68fSopenharmony_ci}
4566881f68fSopenharmony_ci
4576881f68fSopenharmony_cistatic int iconv_fsyncdir(const char *path, int isdatasync,
4586881f68fSopenharmony_ci			  struct fuse_file_info *fi)
4596881f68fSopenharmony_ci{
4606881f68fSopenharmony_ci	struct iconv *ic = iconv_get();
4616881f68fSopenharmony_ci	char *newpath;
4626881f68fSopenharmony_ci	int err = iconv_convpath(ic, path, &newpath, 0);
4636881f68fSopenharmony_ci	if (!err) {
4646881f68fSopenharmony_ci		err = fuse_fs_fsyncdir(ic->next, newpath, isdatasync, fi);
4656881f68fSopenharmony_ci		free(newpath);
4666881f68fSopenharmony_ci	}
4676881f68fSopenharmony_ci	return err;
4686881f68fSopenharmony_ci}
4696881f68fSopenharmony_ci
4706881f68fSopenharmony_cistatic int iconv_setxattr(const char *path, const char *name,
4716881f68fSopenharmony_ci			  const char *value, size_t size, int flags)
4726881f68fSopenharmony_ci{
4736881f68fSopenharmony_ci	struct iconv *ic = iconv_get();
4746881f68fSopenharmony_ci	char *newpath;
4756881f68fSopenharmony_ci	int err = iconv_convpath(ic, path, &newpath, 0);
4766881f68fSopenharmony_ci	if (!err) {
4776881f68fSopenharmony_ci		err = fuse_fs_setxattr(ic->next, newpath, name, value, size,
4786881f68fSopenharmony_ci				       flags);
4796881f68fSopenharmony_ci		free(newpath);
4806881f68fSopenharmony_ci	}
4816881f68fSopenharmony_ci	return err;
4826881f68fSopenharmony_ci}
4836881f68fSopenharmony_ci
4846881f68fSopenharmony_cistatic int iconv_getxattr(const char *path, const char *name, char *value,
4856881f68fSopenharmony_ci			  size_t size)
4866881f68fSopenharmony_ci{
4876881f68fSopenharmony_ci	struct iconv *ic = iconv_get();
4886881f68fSopenharmony_ci	char *newpath;
4896881f68fSopenharmony_ci	int err = iconv_convpath(ic, path, &newpath, 0);
4906881f68fSopenharmony_ci	if (!err) {
4916881f68fSopenharmony_ci		err = fuse_fs_getxattr(ic->next, newpath, name, value, size);
4926881f68fSopenharmony_ci		free(newpath);
4936881f68fSopenharmony_ci	}
4946881f68fSopenharmony_ci	return err;
4956881f68fSopenharmony_ci}
4966881f68fSopenharmony_ci
4976881f68fSopenharmony_cistatic int iconv_listxattr(const char *path, char *list, size_t size)
4986881f68fSopenharmony_ci{
4996881f68fSopenharmony_ci	struct iconv *ic = iconv_get();
5006881f68fSopenharmony_ci	char *newpath;
5016881f68fSopenharmony_ci	int err = iconv_convpath(ic, path, &newpath, 0);
5026881f68fSopenharmony_ci	if (!err) {
5036881f68fSopenharmony_ci		err = fuse_fs_listxattr(ic->next, newpath, list, size);
5046881f68fSopenharmony_ci		free(newpath);
5056881f68fSopenharmony_ci	}
5066881f68fSopenharmony_ci	return err;
5076881f68fSopenharmony_ci}
5086881f68fSopenharmony_ci
5096881f68fSopenharmony_cistatic int iconv_removexattr(const char *path, const char *name)
5106881f68fSopenharmony_ci{
5116881f68fSopenharmony_ci	struct iconv *ic = iconv_get();
5126881f68fSopenharmony_ci	char *newpath;
5136881f68fSopenharmony_ci	int err = iconv_convpath(ic, path, &newpath, 0);
5146881f68fSopenharmony_ci	if (!err) {
5156881f68fSopenharmony_ci		err = fuse_fs_removexattr(ic->next, newpath, name);
5166881f68fSopenharmony_ci		free(newpath);
5176881f68fSopenharmony_ci	}
5186881f68fSopenharmony_ci	return err;
5196881f68fSopenharmony_ci}
5206881f68fSopenharmony_ci
5216881f68fSopenharmony_cistatic int iconv_lock(const char *path, struct fuse_file_info *fi, int cmd,
5226881f68fSopenharmony_ci		      struct flock *lock)
5236881f68fSopenharmony_ci{
5246881f68fSopenharmony_ci	struct iconv *ic = iconv_get();
5256881f68fSopenharmony_ci	char *newpath;
5266881f68fSopenharmony_ci	int err = iconv_convpath(ic, path, &newpath, 0);
5276881f68fSopenharmony_ci	if (!err) {
5286881f68fSopenharmony_ci		err = fuse_fs_lock(ic->next, newpath, fi, cmd, lock);
5296881f68fSopenharmony_ci		free(newpath);
5306881f68fSopenharmony_ci	}
5316881f68fSopenharmony_ci	return err;
5326881f68fSopenharmony_ci}
5336881f68fSopenharmony_ci
5346881f68fSopenharmony_cistatic int iconv_flock(const char *path, struct fuse_file_info *fi, int op)
5356881f68fSopenharmony_ci{
5366881f68fSopenharmony_ci	struct iconv *ic = iconv_get();
5376881f68fSopenharmony_ci	char *newpath;
5386881f68fSopenharmony_ci	int err = iconv_convpath(ic, path, &newpath, 0);
5396881f68fSopenharmony_ci	if (!err) {
5406881f68fSopenharmony_ci		err = fuse_fs_flock(ic->next, newpath, fi, op);
5416881f68fSopenharmony_ci		free(newpath);
5426881f68fSopenharmony_ci	}
5436881f68fSopenharmony_ci	return err;
5446881f68fSopenharmony_ci}
5456881f68fSopenharmony_ci
5466881f68fSopenharmony_cistatic int iconv_bmap(const char *path, size_t blocksize, uint64_t *idx)
5476881f68fSopenharmony_ci{
5486881f68fSopenharmony_ci	struct iconv *ic = iconv_get();
5496881f68fSopenharmony_ci	char *newpath;
5506881f68fSopenharmony_ci	int err = iconv_convpath(ic, path, &newpath, 0);
5516881f68fSopenharmony_ci	if (!err) {
5526881f68fSopenharmony_ci		err = fuse_fs_bmap(ic->next, newpath, blocksize, idx);
5536881f68fSopenharmony_ci		free(newpath);
5546881f68fSopenharmony_ci	}
5556881f68fSopenharmony_ci	return err;
5566881f68fSopenharmony_ci}
5576881f68fSopenharmony_ci
5586881f68fSopenharmony_cistatic off_t iconv_lseek(const char *path, off_t off, int whence,
5596881f68fSopenharmony_ci			 struct fuse_file_info *fi)
5606881f68fSopenharmony_ci{
5616881f68fSopenharmony_ci	struct iconv *ic = iconv_get();
5626881f68fSopenharmony_ci	char *newpath;
5636881f68fSopenharmony_ci	int res = iconv_convpath(ic, path, &newpath, 0);
5646881f68fSopenharmony_ci	if (!res) {
5656881f68fSopenharmony_ci		res = fuse_fs_lseek(ic->next, newpath, off, whence, fi);
5666881f68fSopenharmony_ci		free(newpath);
5676881f68fSopenharmony_ci	}
5686881f68fSopenharmony_ci	return res;
5696881f68fSopenharmony_ci}
5706881f68fSopenharmony_ci
5716881f68fSopenharmony_cistatic void *iconv_init(struct fuse_conn_info *conn,
5726881f68fSopenharmony_ci			struct fuse_config *cfg)
5736881f68fSopenharmony_ci{
5746881f68fSopenharmony_ci	struct iconv *ic = iconv_get();
5756881f68fSopenharmony_ci	fuse_fs_init(ic->next, conn, cfg);
5766881f68fSopenharmony_ci	/* Don't touch cfg->nullpath_ok, we can work with
5776881f68fSopenharmony_ci	   either */
5786881f68fSopenharmony_ci	return ic;
5796881f68fSopenharmony_ci}
5806881f68fSopenharmony_ci
5816881f68fSopenharmony_cistatic void iconv_destroy(void *data)
5826881f68fSopenharmony_ci{
5836881f68fSopenharmony_ci	struct iconv *ic = data;
5846881f68fSopenharmony_ci	fuse_fs_destroy(ic->next);
5856881f68fSopenharmony_ci	iconv_close(ic->tofs);
5866881f68fSopenharmony_ci	iconv_close(ic->fromfs);
5876881f68fSopenharmony_ci	pthread_mutex_destroy(&ic->lock);
5886881f68fSopenharmony_ci	free(ic->from_code);
5896881f68fSopenharmony_ci	free(ic->to_code);
5906881f68fSopenharmony_ci	free(ic);
5916881f68fSopenharmony_ci}
5926881f68fSopenharmony_ci
5936881f68fSopenharmony_cistatic const struct fuse_operations iconv_oper = {
5946881f68fSopenharmony_ci	.destroy	= iconv_destroy,
5956881f68fSopenharmony_ci	.init		= iconv_init,
5966881f68fSopenharmony_ci	.getattr	= iconv_getattr,
5976881f68fSopenharmony_ci	.access		= iconv_access,
5986881f68fSopenharmony_ci	.readlink	= iconv_readlink,
5996881f68fSopenharmony_ci	.opendir	= iconv_opendir,
6006881f68fSopenharmony_ci	.readdir	= iconv_readdir,
6016881f68fSopenharmony_ci	.releasedir	= iconv_releasedir,
6026881f68fSopenharmony_ci	.mknod		= iconv_mknod,
6036881f68fSopenharmony_ci	.mkdir		= iconv_mkdir,
6046881f68fSopenharmony_ci	.symlink	= iconv_symlink,
6056881f68fSopenharmony_ci	.unlink		= iconv_unlink,
6066881f68fSopenharmony_ci	.rmdir		= iconv_rmdir,
6076881f68fSopenharmony_ci	.rename		= iconv_rename,
6086881f68fSopenharmony_ci	.link		= iconv_link,
6096881f68fSopenharmony_ci	.chmod		= iconv_chmod,
6106881f68fSopenharmony_ci	.chown		= iconv_chown,
6116881f68fSopenharmony_ci	.truncate	= iconv_truncate,
6126881f68fSopenharmony_ci	.utimens	= iconv_utimens,
6136881f68fSopenharmony_ci	.create		= iconv_create,
6146881f68fSopenharmony_ci	.open		= iconv_open_file,
6156881f68fSopenharmony_ci	.read_buf	= iconv_read_buf,
6166881f68fSopenharmony_ci	.write_buf	= iconv_write_buf,
6176881f68fSopenharmony_ci	.statfs		= iconv_statfs,
6186881f68fSopenharmony_ci	.flush		= iconv_flush,
6196881f68fSopenharmony_ci	.release	= iconv_release,
6206881f68fSopenharmony_ci	.fsync		= iconv_fsync,
6216881f68fSopenharmony_ci	.fsyncdir	= iconv_fsyncdir,
6226881f68fSopenharmony_ci	.setxattr	= iconv_setxattr,
6236881f68fSopenharmony_ci	.getxattr	= iconv_getxattr,
6246881f68fSopenharmony_ci	.listxattr	= iconv_listxattr,
6256881f68fSopenharmony_ci	.removexattr	= iconv_removexattr,
6266881f68fSopenharmony_ci	.lock		= iconv_lock,
6276881f68fSopenharmony_ci	.flock		= iconv_flock,
6286881f68fSopenharmony_ci	.bmap		= iconv_bmap,
6296881f68fSopenharmony_ci	.lseek		= iconv_lseek,
6306881f68fSopenharmony_ci};
6316881f68fSopenharmony_ci
6326881f68fSopenharmony_cistatic const struct fuse_opt iconv_opts[] = {
6336881f68fSopenharmony_ci	FUSE_OPT_KEY("-h", 0),
6346881f68fSopenharmony_ci	FUSE_OPT_KEY("--help", 0),
6356881f68fSopenharmony_ci	{ "from_code=%s", offsetof(struct iconv, from_code), 0 },
6366881f68fSopenharmony_ci	{ "to_code=%s", offsetof(struct iconv, to_code), 1 },
6376881f68fSopenharmony_ci	FUSE_OPT_END
6386881f68fSopenharmony_ci};
6396881f68fSopenharmony_ci
6406881f68fSopenharmony_cistatic void iconv_help(void)
6416881f68fSopenharmony_ci{
6426881f68fSopenharmony_ci	char *charmap;
6436881f68fSopenharmony_ci	const char *old = setlocale(LC_CTYPE, "");
6446881f68fSopenharmony_ci
6456881f68fSopenharmony_ci	charmap = strdup(nl_langinfo(CODESET));
6466881f68fSopenharmony_ci	if (old)
6476881f68fSopenharmony_ci		setlocale(LC_CTYPE, old);
6486881f68fSopenharmony_ci	else
6496881f68fSopenharmony_ci		perror("setlocale");
6506881f68fSopenharmony_ci
6516881f68fSopenharmony_ci	printf(
6526881f68fSopenharmony_ci"    -o from_code=CHARSET   original encoding of file names (default: UTF-8)\n"
6536881f68fSopenharmony_ci"    -o to_code=CHARSET     new encoding of the file names (default: %s)\n",
6546881f68fSopenharmony_ci		charmap);
6556881f68fSopenharmony_ci	free(charmap);
6566881f68fSopenharmony_ci}
6576881f68fSopenharmony_ci
6586881f68fSopenharmony_cistatic int iconv_opt_proc(void *data, const char *arg, int key,
6596881f68fSopenharmony_ci			  struct fuse_args *outargs)
6606881f68fSopenharmony_ci{
6616881f68fSopenharmony_ci	(void) data; (void) arg; (void) outargs;
6626881f68fSopenharmony_ci
6636881f68fSopenharmony_ci	if (!key) {
6646881f68fSopenharmony_ci		iconv_help();
6656881f68fSopenharmony_ci		return -1;
6666881f68fSopenharmony_ci	}
6676881f68fSopenharmony_ci
6686881f68fSopenharmony_ci	return 1;
6696881f68fSopenharmony_ci}
6706881f68fSopenharmony_ci
6716881f68fSopenharmony_cistatic struct fuse_fs *iconv_new(struct fuse_args *args,
6726881f68fSopenharmony_ci				 struct fuse_fs *next[])
6736881f68fSopenharmony_ci{
6746881f68fSopenharmony_ci	struct fuse_fs *fs;
6756881f68fSopenharmony_ci	struct iconv *ic;
6766881f68fSopenharmony_ci	const char *old = NULL;
6776881f68fSopenharmony_ci	const char *from;
6786881f68fSopenharmony_ci	const char *to;
6796881f68fSopenharmony_ci
6806881f68fSopenharmony_ci	ic = calloc(1, sizeof(struct iconv));
6816881f68fSopenharmony_ci	if (ic == NULL) {
6826881f68fSopenharmony_ci		fuse_log(FUSE_LOG_ERR, "fuse-iconv: memory allocation failed\n");
6836881f68fSopenharmony_ci		return NULL;
6846881f68fSopenharmony_ci	}
6856881f68fSopenharmony_ci
6866881f68fSopenharmony_ci	if (fuse_opt_parse(args, ic, iconv_opts, iconv_opt_proc) == -1)
6876881f68fSopenharmony_ci		goto out_free;
6886881f68fSopenharmony_ci
6896881f68fSopenharmony_ci	if (!next[0] || next[1]) {
6906881f68fSopenharmony_ci		fuse_log(FUSE_LOG_ERR, "fuse-iconv: exactly one next filesystem required\n");
6916881f68fSopenharmony_ci		goto out_free;
6926881f68fSopenharmony_ci	}
6936881f68fSopenharmony_ci
6946881f68fSopenharmony_ci	from = ic->from_code ? ic->from_code : "UTF-8";
6956881f68fSopenharmony_ci	to = ic->to_code ? ic->to_code : "";
6966881f68fSopenharmony_ci	/* FIXME: detect charset equivalence? */
6976881f68fSopenharmony_ci	if (!to[0])
6986881f68fSopenharmony_ci		old = setlocale(LC_CTYPE, "");
6996881f68fSopenharmony_ci	ic->tofs = iconv_open(from, to);
7006881f68fSopenharmony_ci	if (ic->tofs == (iconv_t) -1) {
7016881f68fSopenharmony_ci		fuse_log(FUSE_LOG_ERR, "fuse-iconv: cannot convert from %s to %s\n",
7026881f68fSopenharmony_ci			to, from);
7036881f68fSopenharmony_ci		goto out_free;
7046881f68fSopenharmony_ci	}
7056881f68fSopenharmony_ci	ic->fromfs = iconv_open(to, from);
7066881f68fSopenharmony_ci	if (ic->tofs == (iconv_t) -1) {
7076881f68fSopenharmony_ci		fuse_log(FUSE_LOG_ERR, "fuse-iconv: cannot convert from %s to %s\n",
7086881f68fSopenharmony_ci			from, to);
7096881f68fSopenharmony_ci		goto out_iconv_close_to;
7106881f68fSopenharmony_ci	}
7116881f68fSopenharmony_ci	if (old) {
7126881f68fSopenharmony_ci		setlocale(LC_CTYPE, old);
7136881f68fSopenharmony_ci		old = NULL;
7146881f68fSopenharmony_ci	}
7156881f68fSopenharmony_ci
7166881f68fSopenharmony_ci	ic->next = next[0];
7176881f68fSopenharmony_ci	fs = fuse_fs_new(&iconv_oper, sizeof(iconv_oper), ic);
7186881f68fSopenharmony_ci	if (!fs)
7196881f68fSopenharmony_ci		goto out_iconv_close_from;
7206881f68fSopenharmony_ci
7216881f68fSopenharmony_ci	return fs;
7226881f68fSopenharmony_ci
7236881f68fSopenharmony_ciout_iconv_close_from:
7246881f68fSopenharmony_ci	iconv_close(ic->fromfs);
7256881f68fSopenharmony_ciout_iconv_close_to:
7266881f68fSopenharmony_ci	iconv_close(ic->tofs);
7276881f68fSopenharmony_ciout_free:
7286881f68fSopenharmony_ci	free(ic->from_code);
7296881f68fSopenharmony_ci	free(ic->to_code);
7306881f68fSopenharmony_ci	free(ic);
7316881f68fSopenharmony_ci	if (old) {
7326881f68fSopenharmony_ci		setlocale(LC_CTYPE, old);
7336881f68fSopenharmony_ci	}
7346881f68fSopenharmony_ci	return NULL;
7356881f68fSopenharmony_ci}
7366881f68fSopenharmony_ci
7376881f68fSopenharmony_ciFUSE_REGISTER_MODULE(iconv, iconv_new);
738