• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2   fuse iconv module: file name charset conversion
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 #include <iconv.h>
18 #include <pthread.h>
19 #include <locale.h>
20 #include <langinfo.h>
21 
22 struct iconv {
23 	struct fuse_fs *next;
24 	pthread_mutex_t lock;
25 	char *from_code;
26 	char *to_code;
27 	iconv_t tofs;
28 	iconv_t fromfs;
29 };
30 
31 struct iconv_dh {
32 	struct iconv *ic;
33 	void *prev_buf;
34 	fuse_fill_dir_t prev_filler;
35 };
36 
iconv_get(void)37 static struct iconv *iconv_get(void)
38 {
39 	return fuse_get_context()->private_data;
40 }
41 
iconv_convpath(struct iconv * ic,const char * path,char ** newpathp,int fromfs)42 static int iconv_convpath(struct iconv *ic, const char *path, char **newpathp,
43 			  int fromfs)
44 {
45 	size_t pathlen;
46 	size_t newpathlen;
47 	char *newpath;
48 	size_t plen;
49 	char *p;
50 	size_t res;
51 	int err;
52 
53 	if (path == NULL) {
54 		*newpathp = NULL;
55 		return 0;
56 	}
57 
58 	pathlen = strlen(path);
59 	newpathlen = pathlen * 4;
60 	newpath = malloc(newpathlen + 1);
61 	if (!newpath)
62 		return -ENOMEM;
63 
64 	plen = newpathlen;
65 	p = newpath;
66 	pthread_mutex_lock(&ic->lock);
67 	do {
68 		res = iconv(fromfs ? ic->fromfs : ic->tofs, (char **) &path,
69 			    &pathlen, &p, &plen);
70 		if (res == (size_t) -1) {
71 			char *tmp;
72 			size_t inc;
73 
74 			err = -EILSEQ;
75 			if (errno != E2BIG)
76 				goto err;
77 
78 			inc = (pathlen + 1) * 4;
79 			newpathlen += inc;
80 			int dp = p - newpath;
81 			tmp = realloc(newpath, newpathlen + 1);
82 			err = -ENOMEM;
83 			if (!tmp)
84 				goto err;
85 
86 			p = tmp + dp;
87 			plen += inc;
88 			newpath = tmp;
89 		}
90 	} while (res == (size_t) -1);
91 	pthread_mutex_unlock(&ic->lock);
92 	*p = '\0';
93 	*newpathp = newpath;
94 	return 0;
95 
96 err:
97 	iconv(fromfs ? ic->fromfs : ic->tofs, NULL, NULL, NULL, NULL);
98 	pthread_mutex_unlock(&ic->lock);
99 	free(newpath);
100 	return err;
101 }
102 
iconv_getattr(const char * path,struct stat * stbuf,struct fuse_file_info * fi)103 static int iconv_getattr(const char *path, struct stat *stbuf,
104 			 struct fuse_file_info *fi)
105 {
106 	struct iconv *ic = iconv_get();
107 	char *newpath;
108 	int err = iconv_convpath(ic, path, &newpath, 0);
109 	if (!err) {
110 		err = fuse_fs_getattr(ic->next, newpath, stbuf, fi);
111 		free(newpath);
112 	}
113 	return err;
114 }
115 
iconv_access(const char * path,int mask)116 static int iconv_access(const char *path, int mask)
117 {
118 	struct iconv *ic = iconv_get();
119 	char *newpath;
120 	int err = iconv_convpath(ic, path, &newpath, 0);
121 	if (!err) {
122 		err = fuse_fs_access(ic->next, newpath, mask);
123 		free(newpath);
124 	}
125 	return err;
126 }
127 
iconv_readlink(const char * path,char * buf,size_t size)128 static int iconv_readlink(const char *path, char *buf, size_t size)
129 {
130 	struct iconv *ic = iconv_get();
131 	char *newpath;
132 	int err = iconv_convpath(ic, path, &newpath, 0);
133 	if (!err) {
134 		err = fuse_fs_readlink(ic->next, newpath, buf, size);
135 		if (!err) {
136 			char *newlink;
137 			err = iconv_convpath(ic, buf, &newlink, 1);
138 			if (!err) {
139 				strncpy(buf, newlink, size - 1);
140 				buf[size - 1] = '\0';
141 				free(newlink);
142 			}
143 		}
144 		free(newpath);
145 	}
146 	return err;
147 }
148 
iconv_opendir(const char * path,struct fuse_file_info * fi)149 static int iconv_opendir(const char *path, struct fuse_file_info *fi)
150 {
151 	struct iconv *ic = iconv_get();
152 	char *newpath;
153 	int err = iconv_convpath(ic, path, &newpath, 0);
154 	if (!err) {
155 		err = fuse_fs_opendir(ic->next, newpath, fi);
156 		free(newpath);
157 	}
158 	return err;
159 }
160 
iconv_dir_fill(void * buf,const char * name,const struct stat * stbuf,off_t off,enum fuse_fill_dir_flags flags)161 static int iconv_dir_fill(void *buf, const char *name,
162 			  const struct stat *stbuf, off_t off,
163 			  enum fuse_fill_dir_flags flags)
164 {
165 	struct iconv_dh *dh = buf;
166 	char *newname;
167 	int res = 0;
168 	if (iconv_convpath(dh->ic, name, &newname, 1) == 0) {
169 		res = dh->prev_filler(dh->prev_buf, newname, stbuf, off, flags);
170 		free(newname);
171 	}
172 	return res;
173 }
174 
iconv_readdir(const char * path,void * buf,fuse_fill_dir_t filler,off_t offset,struct fuse_file_info * fi,enum fuse_readdir_flags flags)175 static int iconv_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
176 			 off_t offset, struct fuse_file_info *fi,
177 			 enum fuse_readdir_flags flags)
178 {
179 	struct iconv *ic = iconv_get();
180 	char *newpath;
181 	int err = iconv_convpath(ic, path, &newpath, 0);
182 	if (!err) {
183 		struct iconv_dh dh;
184 		dh.ic = ic;
185 		dh.prev_buf = buf;
186 		dh.prev_filler = filler;
187 		err = fuse_fs_readdir(ic->next, newpath, &dh, iconv_dir_fill,
188 				      offset, fi, flags);
189 		free(newpath);
190 	}
191 	return err;
192 }
193 
iconv_releasedir(const char * path,struct fuse_file_info * fi)194 static int iconv_releasedir(const char *path, struct fuse_file_info *fi)
195 {
196 	struct iconv *ic = iconv_get();
197 	char *newpath;
198 	int err = iconv_convpath(ic, path, &newpath, 0);
199 	if (!err) {
200 		err = fuse_fs_releasedir(ic->next, newpath, fi);
201 		free(newpath);
202 	}
203 	return err;
204 }
205 
iconv_mknod(const char * path,mode_t mode,dev_t rdev)206 static int iconv_mknod(const char *path, mode_t mode, dev_t rdev)
207 {
208 	struct iconv *ic = iconv_get();
209 	char *newpath;
210 	int err = iconv_convpath(ic, path, &newpath, 0);
211 	if (!err) {
212 		err = fuse_fs_mknod(ic->next, newpath, mode, rdev);
213 		free(newpath);
214 	}
215 	return err;
216 }
217 
iconv_mkdir(const char * path,mode_t mode)218 static int iconv_mkdir(const char *path, mode_t mode)
219 {
220 	struct iconv *ic = iconv_get();
221 	char *newpath;
222 	int err = iconv_convpath(ic, path, &newpath, 0);
223 	if (!err) {
224 		err = fuse_fs_mkdir(ic->next, newpath, mode);
225 		free(newpath);
226 	}
227 	return err;
228 }
229 
iconv_unlink(const char * path)230 static int iconv_unlink(const char *path)
231 {
232 	struct iconv *ic = iconv_get();
233 	char *newpath;
234 	int err = iconv_convpath(ic, path, &newpath, 0);
235 	if (!err) {
236 		err = fuse_fs_unlink(ic->next, newpath);
237 		free(newpath);
238 	}
239 	return err;
240 }
241 
iconv_rmdir(const char * path)242 static int iconv_rmdir(const char *path)
243 {
244 	struct iconv *ic = iconv_get();
245 	char *newpath;
246 	int err = iconv_convpath(ic, path, &newpath, 0);
247 	if (!err) {
248 		err = fuse_fs_rmdir(ic->next, newpath);
249 		free(newpath);
250 	}
251 	return err;
252 }
253 
iconv_symlink(const char * from,const char * to)254 static int iconv_symlink(const char *from, const char *to)
255 {
256 	struct iconv *ic = iconv_get();
257 	char *newfrom;
258 	char *newto;
259 	int err = iconv_convpath(ic, from, &newfrom, 0);
260 	if (!err) {
261 		err = iconv_convpath(ic, to, &newto, 0);
262 		if (!err) {
263 			err = fuse_fs_symlink(ic->next, newfrom, newto);
264 			free(newto);
265 		}
266 		free(newfrom);
267 	}
268 	return err;
269 }
270 
iconv_rename(const char * from,const char * to,unsigned int flags)271 static int iconv_rename(const char *from, const char *to, unsigned int flags)
272 {
273 	struct iconv *ic = iconv_get();
274 	char *newfrom;
275 	char *newto;
276 	int err = iconv_convpath(ic, from, &newfrom, 0);
277 	if (!err) {
278 		err = iconv_convpath(ic, to, &newto, 0);
279 		if (!err) {
280 			err = fuse_fs_rename(ic->next, newfrom, newto, flags);
281 			free(newto);
282 		}
283 		free(newfrom);
284 	}
285 	return err;
286 }
287 
iconv_link(const char * from,const char * to)288 static int iconv_link(const char *from, const char *to)
289 {
290 	struct iconv *ic = iconv_get();
291 	char *newfrom;
292 	char *newto;
293 	int err = iconv_convpath(ic, from, &newfrom, 0);
294 	if (!err) {
295 		err = iconv_convpath(ic, to, &newto, 0);
296 		if (!err) {
297 			err = fuse_fs_link(ic->next, newfrom, newto);
298 			free(newto);
299 		}
300 		free(newfrom);
301 	}
302 	return err;
303 }
304 
iconv_chmod(const char * path,mode_t mode,struct fuse_file_info * fi)305 static int iconv_chmod(const char *path, mode_t mode,
306 		       struct fuse_file_info *fi)
307 {
308 	struct iconv *ic = iconv_get();
309 	char *newpath;
310 	int err = iconv_convpath(ic, path, &newpath, 0);
311 	if (!err) {
312 		err = fuse_fs_chmod(ic->next, newpath, mode, fi);
313 		free(newpath);
314 	}
315 	return err;
316 }
317 
iconv_chown(const char * path,uid_t uid,gid_t gid,struct fuse_file_info * fi)318 static int iconv_chown(const char *path, uid_t uid, gid_t gid,
319 		       struct fuse_file_info *fi)
320 {
321 	struct iconv *ic = iconv_get();
322 	char *newpath;
323 	int err = iconv_convpath(ic, path, &newpath, 0);
324 	if (!err) {
325 		err = fuse_fs_chown(ic->next, newpath, uid, gid, fi);
326 		free(newpath);
327 	}
328 	return err;
329 }
330 
iconv_truncate(const char * path,off_t size,struct fuse_file_info * fi)331 static int iconv_truncate(const char *path, off_t size,
332 			   struct fuse_file_info *fi)
333 {
334 	struct iconv *ic = iconv_get();
335 	char *newpath;
336 	int err = iconv_convpath(ic, path, &newpath, 0);
337 	if (!err) {
338 		err = fuse_fs_truncate(ic->next, newpath, size, fi);
339 		free(newpath);
340 	}
341 	return err;
342 }
343 
iconv_utimens(const char * path,const struct timespec ts[2],struct fuse_file_info * fi)344 static int iconv_utimens(const char *path, const struct timespec ts[2],
345 			 struct fuse_file_info *fi)
346 {
347 	struct iconv *ic = iconv_get();
348 	char *newpath;
349 	int err = iconv_convpath(ic, path, &newpath, 0);
350 	if (!err) {
351 		err = fuse_fs_utimens(ic->next, newpath, ts, fi);
352 		free(newpath);
353 	}
354 	return err;
355 }
356 
iconv_create(const char * path,mode_t mode,struct fuse_file_info * fi)357 static int iconv_create(const char *path, mode_t mode,
358 			struct fuse_file_info *fi)
359 {
360 	struct iconv *ic = iconv_get();
361 	char *newpath;
362 	int err = iconv_convpath(ic, path, &newpath, 0);
363 	if (!err) {
364 		err = fuse_fs_create(ic->next, newpath, mode, fi);
365 		free(newpath);
366 	}
367 	return err;
368 }
369 
iconv_open_file(const char * path,struct fuse_file_info * fi)370 static int iconv_open_file(const char *path, struct fuse_file_info *fi)
371 {
372 	struct iconv *ic = iconv_get();
373 	char *newpath;
374 	int err = iconv_convpath(ic, path, &newpath, 0);
375 	if (!err) {
376 		err = fuse_fs_open(ic->next, newpath, fi);
377 		free(newpath);
378 	}
379 	return err;
380 }
381 
iconv_read_buf(const char * path,struct fuse_bufvec ** bufp,size_t size,off_t offset,struct fuse_file_info * fi)382 static int iconv_read_buf(const char *path, struct fuse_bufvec **bufp,
383 			  size_t size, off_t offset, struct fuse_file_info *fi)
384 {
385 	struct iconv *ic = iconv_get();
386 	char *newpath;
387 	int err = iconv_convpath(ic, path, &newpath, 0);
388 	if (!err) {
389 		err = fuse_fs_read_buf(ic->next, newpath, bufp, size, offset, fi);
390 		free(newpath);
391 	}
392 	return err;
393 }
394 
iconv_write_buf(const char * path,struct fuse_bufvec * buf,off_t offset,struct fuse_file_info * fi)395 static int iconv_write_buf(const char *path, struct fuse_bufvec *buf,
396 			   off_t offset, struct fuse_file_info *fi)
397 {
398 	struct iconv *ic = iconv_get();
399 	char *newpath;
400 	int err = iconv_convpath(ic, path, &newpath, 0);
401 	if (!err) {
402 		err = fuse_fs_write_buf(ic->next, newpath, buf, offset, fi);
403 		free(newpath);
404 	}
405 	return err;
406 }
407 
iconv_statfs(const char * path,struct statvfs * stbuf)408 static int iconv_statfs(const char *path, struct statvfs *stbuf)
409 {
410 	struct iconv *ic = iconv_get();
411 	char *newpath;
412 	int err = iconv_convpath(ic, path, &newpath, 0);
413 	if (!err) {
414 		err = fuse_fs_statfs(ic->next, newpath, stbuf);
415 		free(newpath);
416 	}
417 	return err;
418 }
419 
iconv_flush(const char * path,struct fuse_file_info * fi)420 static int iconv_flush(const char *path, struct fuse_file_info *fi)
421 {
422 	struct iconv *ic = iconv_get();
423 	char *newpath;
424 	int err = iconv_convpath(ic, path, &newpath, 0);
425 	if (!err) {
426 		err = fuse_fs_flush(ic->next, newpath, fi);
427 		free(newpath);
428 	}
429 	return err;
430 }
431 
iconv_release(const char * path,struct fuse_file_info * fi)432 static int iconv_release(const char *path, struct fuse_file_info *fi)
433 {
434 	struct iconv *ic = iconv_get();
435 	char *newpath;
436 	int err = iconv_convpath(ic, path, &newpath, 0);
437 	if (!err) {
438 		err = fuse_fs_release(ic->next, newpath, fi);
439 		free(newpath);
440 	}
441 	return err;
442 }
443 
iconv_fsync(const char * path,int isdatasync,struct fuse_file_info * fi)444 static int iconv_fsync(const char *path, int isdatasync,
445 		       struct fuse_file_info *fi)
446 {
447 	struct iconv *ic = iconv_get();
448 	char *newpath;
449 	int err = iconv_convpath(ic, path, &newpath, 0);
450 	if (!err) {
451 		err = fuse_fs_fsync(ic->next, newpath, isdatasync, fi);
452 		free(newpath);
453 	}
454 	return err;
455 }
456 
iconv_fsyncdir(const char * path,int isdatasync,struct fuse_file_info * fi)457 static int iconv_fsyncdir(const char *path, int isdatasync,
458 			  struct fuse_file_info *fi)
459 {
460 	struct iconv *ic = iconv_get();
461 	char *newpath;
462 	int err = iconv_convpath(ic, path, &newpath, 0);
463 	if (!err) {
464 		err = fuse_fs_fsyncdir(ic->next, newpath, isdatasync, fi);
465 		free(newpath);
466 	}
467 	return err;
468 }
469 
iconv_setxattr(const char * path,const char * name,const char * value,size_t size,int flags)470 static int iconv_setxattr(const char *path, const char *name,
471 			  const char *value, size_t size, int flags)
472 {
473 	struct iconv *ic = iconv_get();
474 	char *newpath;
475 	int err = iconv_convpath(ic, path, &newpath, 0);
476 	if (!err) {
477 		err = fuse_fs_setxattr(ic->next, newpath, name, value, size,
478 				       flags);
479 		free(newpath);
480 	}
481 	return err;
482 }
483 
iconv_getxattr(const char * path,const char * name,char * value,size_t size)484 static int iconv_getxattr(const char *path, const char *name, char *value,
485 			  size_t size)
486 {
487 	struct iconv *ic = iconv_get();
488 	char *newpath;
489 	int err = iconv_convpath(ic, path, &newpath, 0);
490 	if (!err) {
491 		err = fuse_fs_getxattr(ic->next, newpath, name, value, size);
492 		free(newpath);
493 	}
494 	return err;
495 }
496 
iconv_listxattr(const char * path,char * list,size_t size)497 static int iconv_listxattr(const char *path, char *list, size_t size)
498 {
499 	struct iconv *ic = iconv_get();
500 	char *newpath;
501 	int err = iconv_convpath(ic, path, &newpath, 0);
502 	if (!err) {
503 		err = fuse_fs_listxattr(ic->next, newpath, list, size);
504 		free(newpath);
505 	}
506 	return err;
507 }
508 
iconv_removexattr(const char * path,const char * name)509 static int iconv_removexattr(const char *path, const char *name)
510 {
511 	struct iconv *ic = iconv_get();
512 	char *newpath;
513 	int err = iconv_convpath(ic, path, &newpath, 0);
514 	if (!err) {
515 		err = fuse_fs_removexattr(ic->next, newpath, name);
516 		free(newpath);
517 	}
518 	return err;
519 }
520 
iconv_lock(const char * path,struct fuse_file_info * fi,int cmd,struct flock * lock)521 static int iconv_lock(const char *path, struct fuse_file_info *fi, int cmd,
522 		      struct flock *lock)
523 {
524 	struct iconv *ic = iconv_get();
525 	char *newpath;
526 	int err = iconv_convpath(ic, path, &newpath, 0);
527 	if (!err) {
528 		err = fuse_fs_lock(ic->next, newpath, fi, cmd, lock);
529 		free(newpath);
530 	}
531 	return err;
532 }
533 
iconv_flock(const char * path,struct fuse_file_info * fi,int op)534 static int iconv_flock(const char *path, struct fuse_file_info *fi, int op)
535 {
536 	struct iconv *ic = iconv_get();
537 	char *newpath;
538 	int err = iconv_convpath(ic, path, &newpath, 0);
539 	if (!err) {
540 		err = fuse_fs_flock(ic->next, newpath, fi, op);
541 		free(newpath);
542 	}
543 	return err;
544 }
545 
iconv_bmap(const char * path,size_t blocksize,uint64_t * idx)546 static int iconv_bmap(const char *path, size_t blocksize, uint64_t *idx)
547 {
548 	struct iconv *ic = iconv_get();
549 	char *newpath;
550 	int err = iconv_convpath(ic, path, &newpath, 0);
551 	if (!err) {
552 		err = fuse_fs_bmap(ic->next, newpath, blocksize, idx);
553 		free(newpath);
554 	}
555 	return err;
556 }
557 
iconv_lseek(const char * path,off_t off,int whence,struct fuse_file_info * fi)558 static off_t iconv_lseek(const char *path, off_t off, int whence,
559 			 struct fuse_file_info *fi)
560 {
561 	struct iconv *ic = iconv_get();
562 	char *newpath;
563 	int res = iconv_convpath(ic, path, &newpath, 0);
564 	if (!res) {
565 		res = fuse_fs_lseek(ic->next, newpath, off, whence, fi);
566 		free(newpath);
567 	}
568 	return res;
569 }
570 
iconv_init(struct fuse_conn_info * conn,struct fuse_config * cfg)571 static void *iconv_init(struct fuse_conn_info *conn,
572 			struct fuse_config *cfg)
573 {
574 	struct iconv *ic = iconv_get();
575 	fuse_fs_init(ic->next, conn, cfg);
576 	/* Don't touch cfg->nullpath_ok, we can work with
577 	   either */
578 	return ic;
579 }
580 
iconv_destroy(void * data)581 static void iconv_destroy(void *data)
582 {
583 	struct iconv *ic = data;
584 	fuse_fs_destroy(ic->next);
585 	iconv_close(ic->tofs);
586 	iconv_close(ic->fromfs);
587 	pthread_mutex_destroy(&ic->lock);
588 	free(ic->from_code);
589 	free(ic->to_code);
590 	free(ic);
591 }
592 
593 static const struct fuse_operations iconv_oper = {
594 	.destroy	= iconv_destroy,
595 	.init		= iconv_init,
596 	.getattr	= iconv_getattr,
597 	.access		= iconv_access,
598 	.readlink	= iconv_readlink,
599 	.opendir	= iconv_opendir,
600 	.readdir	= iconv_readdir,
601 	.releasedir	= iconv_releasedir,
602 	.mknod		= iconv_mknod,
603 	.mkdir		= iconv_mkdir,
604 	.symlink	= iconv_symlink,
605 	.unlink		= iconv_unlink,
606 	.rmdir		= iconv_rmdir,
607 	.rename		= iconv_rename,
608 	.link		= iconv_link,
609 	.chmod		= iconv_chmod,
610 	.chown		= iconv_chown,
611 	.truncate	= iconv_truncate,
612 	.utimens	= iconv_utimens,
613 	.create		= iconv_create,
614 	.open		= iconv_open_file,
615 	.read_buf	= iconv_read_buf,
616 	.write_buf	= iconv_write_buf,
617 	.statfs		= iconv_statfs,
618 	.flush		= iconv_flush,
619 	.release	= iconv_release,
620 	.fsync		= iconv_fsync,
621 	.fsyncdir	= iconv_fsyncdir,
622 	.setxattr	= iconv_setxattr,
623 	.getxattr	= iconv_getxattr,
624 	.listxattr	= iconv_listxattr,
625 	.removexattr	= iconv_removexattr,
626 	.lock		= iconv_lock,
627 	.flock		= iconv_flock,
628 	.bmap		= iconv_bmap,
629 	.lseek		= iconv_lseek,
630 };
631 
632 static const struct fuse_opt iconv_opts[] = {
633 	FUSE_OPT_KEY("-h", 0),
634 	FUSE_OPT_KEY("--help", 0),
635 	{ "from_code=%s", offsetof(struct iconv, from_code), 0 },
636 	{ "to_code=%s", offsetof(struct iconv, to_code), 1 },
637 	FUSE_OPT_END
638 };
639 
iconv_help(void)640 static void iconv_help(void)
641 {
642 	char *charmap;
643 	const char *old = setlocale(LC_CTYPE, "");
644 
645 	charmap = strdup(nl_langinfo(CODESET));
646 	if (old)
647 		setlocale(LC_CTYPE, old);
648 	else
649 		perror("setlocale");
650 
651 	printf(
652 "    -o from_code=CHARSET   original encoding of file names (default: UTF-8)\n"
653 "    -o to_code=CHARSET     new encoding of the file names (default: %s)\n",
654 		charmap);
655 	free(charmap);
656 }
657 
iconv_opt_proc(void * data,const char * arg,int key,struct fuse_args * outargs)658 static int iconv_opt_proc(void *data, const char *arg, int key,
659 			  struct fuse_args *outargs)
660 {
661 	(void) data; (void) arg; (void) outargs;
662 
663 	if (!key) {
664 		iconv_help();
665 		return -1;
666 	}
667 
668 	return 1;
669 }
670 
iconv_new(struct fuse_args * args,struct fuse_fs * next[])671 static struct fuse_fs *iconv_new(struct fuse_args *args,
672 				 struct fuse_fs *next[])
673 {
674 	struct fuse_fs *fs;
675 	struct iconv *ic;
676 	const char *old = NULL;
677 	const char *from;
678 	const char *to;
679 
680 	ic = calloc(1, sizeof(struct iconv));
681 	if (ic == NULL) {
682 		fuse_log(FUSE_LOG_ERR, "fuse-iconv: memory allocation failed\n");
683 		return NULL;
684 	}
685 
686 	if (fuse_opt_parse(args, ic, iconv_opts, iconv_opt_proc) == -1)
687 		goto out_free;
688 
689 	if (!next[0] || next[1]) {
690 		fuse_log(FUSE_LOG_ERR, "fuse-iconv: exactly one next filesystem required\n");
691 		goto out_free;
692 	}
693 
694 	from = ic->from_code ? ic->from_code : "UTF-8";
695 	to = ic->to_code ? ic->to_code : "";
696 	/* FIXME: detect charset equivalence? */
697 	if (!to[0])
698 		old = setlocale(LC_CTYPE, "");
699 	ic->tofs = iconv_open(from, to);
700 	if (ic->tofs == (iconv_t) -1) {
701 		fuse_log(FUSE_LOG_ERR, "fuse-iconv: cannot convert from %s to %s\n",
702 			to, from);
703 		goto out_free;
704 	}
705 	ic->fromfs = iconv_open(to, from);
706 	if (ic->tofs == (iconv_t) -1) {
707 		fuse_log(FUSE_LOG_ERR, "fuse-iconv: cannot convert from %s to %s\n",
708 			from, to);
709 		goto out_iconv_close_to;
710 	}
711 	if (old) {
712 		setlocale(LC_CTYPE, old);
713 		old = NULL;
714 	}
715 
716 	ic->next = next[0];
717 	fs = fuse_fs_new(&iconv_oper, sizeof(iconv_oper), ic);
718 	if (!fs)
719 		goto out_iconv_close_from;
720 
721 	return fs;
722 
723 out_iconv_close_from:
724 	iconv_close(ic->fromfs);
725 out_iconv_close_to:
726 	iconv_close(ic->tofs);
727 out_free:
728 	free(ic->from_code);
729 	free(ic->to_code);
730 	free(ic);
731 	if (old) {
732 		setlocale(LC_CTYPE, old);
733 	}
734 	return NULL;
735 }
736 
737 FUSE_REGISTER_MODULE(iconv, iconv_new);
738