Logo Search packages:      
Sourcecode: fuse version File versions

iconv.c

/*
    fuse iconv module: file name charset conversion
    Copyright (C) 2007  Miklos Szeredi <miklos@szeredi.hu>

    This program can be distributed under the terms of the GNU LGPL.
    See the file COPYING.LIB
*/

#define FUSE_USE_VERSION 26

#include <fuse.h>
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
#include <errno.h>
#include <iconv.h>
#include <pthread.h>
#include <locale.h>
#include <langinfo.h>

struct iconv {
    struct fuse_fs *next;
    pthread_mutex_t lock;
    char *from_code;
    char *to_code;
    iconv_t tofs;
    iconv_t fromfs;
};

struct iconv_dh {
    struct iconv *ic;
    void *prev_buf;
    fuse_fill_dir_t prev_filler;
};

static struct iconv *iconv_get(void)
{
    return fuse_get_context()->private_data;
}

static int iconv_convpath(struct iconv *ic, const char *path, char **newpathp,
                          int fromfs)
{
    size_t pathlen = strlen(path);
    size_t newpathlen = pathlen * 4;
    char *newpath = malloc(newpathlen + 1);
    size_t plen = newpathlen;
    char *p = newpath;
    size_t res;
    int err;

    if (!newpath)
        return -ENOMEM;

    pthread_mutex_lock(&ic->lock);
    do {
        res = iconv(fromfs ? ic->fromfs : ic->tofs, (char **) &path, &pathlen,
                    &p, &plen);
        if (res == (size_t) -1) {
            char *tmp;
            size_t inc;

            err = -EILSEQ;
            if (errno != E2BIG)
                goto err;

            inc = (pathlen + 1) * 4;
            newpathlen += inc;
            tmp = realloc(newpath, newpathlen + 1);
            err = -ENOMEM;
            if (!tmp)
                goto err;

            p = tmp + (p - newpath);
            plen += inc;
            newpath = tmp;
        }
    } while (res == (size_t) -1);
    pthread_mutex_unlock(&ic->lock);
    *p = '\0';
    *newpathp = newpath;
    return 0;

 err:
    iconv(fromfs ? ic->fromfs : ic->tofs, NULL, NULL, NULL, NULL);
    pthread_mutex_unlock(&ic->lock);
    free(newpath);
    return err;
}

static int iconv_getattr(const char *path, struct stat *stbuf)
{
    struct iconv *ic = iconv_get();
    char *newpath;
    int err = iconv_convpath(ic, path, &newpath, 0);
    if (!err) {
        err = fuse_fs_getattr(ic->next, newpath, stbuf);
        free(newpath);
    }
    return err;
}

static int iconv_fgetattr(const char *path, struct stat *stbuf,
                          struct fuse_file_info *fi)
{
    struct iconv *ic = iconv_get();
    char *newpath;
    int err = iconv_convpath(ic, path, &newpath, 0);
    if (!err) {
        err = fuse_fs_fgetattr(ic->next, newpath, stbuf, fi);
        free(newpath);
    }
    return err;
}

static int iconv_access(const char *path, int mask)
{
    struct iconv *ic = iconv_get();
    char *newpath;
    int err = iconv_convpath(ic, path, &newpath, 0);
    if (!err) {
        err = fuse_fs_access(ic->next, newpath, mask);
        free(newpath);
    }
    return err;
}

static int iconv_readlink(const char *path, char *buf, size_t size)
{
    struct iconv *ic = iconv_get();
    char *newpath;
    int err = iconv_convpath(ic, path, &newpath, 0);
    if (!err) {
        err = fuse_fs_readlink(ic->next, newpath, buf, size);
        if (!err) {
            char *newlink;
            err = iconv_convpath(ic, buf, &newlink, 1);
            if (!err) {
                strncpy(buf, newlink, size - 1);
                buf[size - 1] = '\0';
                free(newlink);
            }
        }
        free(newpath);
    }
    return err;
}

static int iconv_opendir(const char *path, struct fuse_file_info *fi)
{
    struct iconv *ic = iconv_get();
    char *newpath;
    int err = iconv_convpath(ic, path, &newpath, 0);
    if (!err) {
        err = fuse_fs_opendir(ic->next, newpath, fi);
        free(newpath);
    }
    return err;
}

static int iconv_dir_fill(void *buf, const char *name,
                          const struct stat *stbuf, off_t off)
{
    struct iconv_dh *dh = buf;
    char *newname;
    int res = 0;
    if (iconv_convpath(dh->ic, name, &newname, 1) == 0) {
        res = dh->prev_filler(dh->prev_buf, newname, stbuf, off);
        free(newname);
    }
    return res;
}

static int iconv_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
                         off_t offset, struct fuse_file_info *fi)
{
    struct iconv *ic = iconv_get();
    char *newpath;
    int err = iconv_convpath(ic, path, &newpath, 0);
    if (!err) {
        struct iconv_dh dh;
        dh.ic = ic;
        dh.prev_buf = buf;
        dh.prev_filler = filler;
        err = fuse_fs_readdir(ic->next, newpath, &dh, iconv_dir_fill, offset,
                              fi);
        free(newpath);
    }
    return err;
}

static int iconv_releasedir(const char *path, struct fuse_file_info *fi)
{
    struct iconv *ic = iconv_get();
    char *newpath;
    int err = iconv_convpath(ic, path, &newpath, 0);
    if (!err) {
        err = fuse_fs_releasedir(ic->next, newpath, fi);
        free(newpath);
    }
    return err;
}

static int iconv_mknod(const char *path, mode_t mode, dev_t rdev)
{
    struct iconv *ic = iconv_get();
    char *newpath;
    int err = iconv_convpath(ic, path, &newpath, 0);
    if (!err) {
        err = fuse_fs_mknod(ic->next, newpath, mode, rdev);
        free(newpath);
    }
    return err;
}

static int iconv_mkdir(const char *path, mode_t mode)
{
    struct iconv *ic = iconv_get();
    char *newpath;
    int err = iconv_convpath(ic, path, &newpath, 0);
    if (!err) {
        err = fuse_fs_mkdir(ic->next, newpath, mode);
        free(newpath);
    }
    return err;
}

static int iconv_unlink(const char *path)
{
    struct iconv *ic = iconv_get();
    char *newpath;
    int err = iconv_convpath(ic, path, &newpath, 0);
    if (!err) {
        err = fuse_fs_unlink(ic->next, newpath);
        free(newpath);
    }
    return err;
}

static int iconv_rmdir(const char *path)
{
    struct iconv *ic = iconv_get();
    char *newpath;
    int err = iconv_convpath(ic, path, &newpath, 0);
    if (!err) {
        err = fuse_fs_rmdir(ic->next, newpath);
        free(newpath);
    }
    return err;
}

static int iconv_symlink(const char *from, const char *to)
{
    struct iconv *ic = iconv_get();
    char *newfrom;
    char *newto;
    int err = iconv_convpath(ic, from, &newfrom, 0);
    if (!err) {
        err = iconv_convpath(ic, to, &newto, 0);
        if (!err) {
            err = fuse_fs_symlink(ic->next, newfrom, newto);
            free(newto);
        }
        free(newfrom);
    }
    return err;
}

static int iconv_rename(const char *from, const char *to)
{
    struct iconv *ic = iconv_get();
    char *newfrom;
    char *newto;
    int err = iconv_convpath(ic, from, &newfrom, 0);
    if (!err) {
        err = iconv_convpath(ic, to, &newto, 0);
        if (!err) {
            err = fuse_fs_rename(ic->next, newfrom, newto);
            free(newto);
        }
        free(newfrom);
    }
    return err;
}

static int iconv_link(const char *from, const char *to)
{
    struct iconv *ic = iconv_get();
    char *newfrom;
    char *newto;
    int err = iconv_convpath(ic, from, &newfrom, 0);
    if (!err) {
        err = iconv_convpath(ic, to, &newto, 0);
        if (!err) {
            err = fuse_fs_link(ic->next, newfrom, newto);
            free(newto);
        }
        free(newfrom);
    }
    return err;
}

static int iconv_chmod(const char *path, mode_t mode)
{
    struct iconv *ic = iconv_get();
    char *newpath;
    int err = iconv_convpath(ic, path, &newpath, 0);
    if (!err) {
        err = fuse_fs_chmod(ic->next, newpath, mode);
        free(newpath);
    }
    return err;
}

static int iconv_chown(const char *path, uid_t uid, gid_t gid)
{
    struct iconv *ic = iconv_get();
    char *newpath;
    int err = iconv_convpath(ic, path, &newpath, 0);
    if (!err) {
        err = fuse_fs_chown(ic->next, newpath, uid, gid);
        free(newpath);
    }
    return err;
}

static int iconv_truncate(const char *path, off_t size)
{
    struct iconv *ic = iconv_get();
    char *newpath;
    int err = iconv_convpath(ic, path, &newpath, 0);
    if (!err) {
        err = fuse_fs_truncate(ic->next, newpath, size);
        free(newpath);
    }
    return err;
}

static int iconv_ftruncate(const char *path, off_t size,
                           struct fuse_file_info *fi)
{
    struct iconv *ic = iconv_get();
    char *newpath;
    int err = iconv_convpath(ic, path, &newpath, 0);
    if (!err) {
        err = fuse_fs_ftruncate(ic->next, newpath, size, fi);
        free(newpath);
    }
    return err;
}

static int iconv_utimens(const char *path, const struct timespec ts[2])
{
    struct iconv *ic = iconv_get();
    char *newpath;
    int err = iconv_convpath(ic, path, &newpath, 0);
    if (!err) {
        err = fuse_fs_utimens(ic->next, newpath, ts);
        free(newpath);
    }
    return err;
}

static int iconv_create(const char *path, mode_t mode,
                        struct fuse_file_info *fi)
{
    struct iconv *ic = iconv_get();
    char *newpath;
    int err = iconv_convpath(ic, path, &newpath, 0);
    if (!err) {
        err = fuse_fs_create(ic->next, newpath, mode, fi);
        free(newpath);
    }
    return err;
}

static int iconv_open_file(const char *path, struct fuse_file_info *fi)
{
    struct iconv *ic = iconv_get();
    char *newpath;
    int err = iconv_convpath(ic, path, &newpath, 0);
    if (!err) {
        err = fuse_fs_open(ic->next, newpath, fi);
        free(newpath);
    }
    return err;
}

static int iconv_read(const char *path, char *buf, size_t size, off_t offset,
                    struct fuse_file_info *fi)
{
    struct iconv *ic = iconv_get();
    char *newpath;
    int err = iconv_convpath(ic, path, &newpath, 0);
    if (!err) {
        err = fuse_fs_read(ic->next, newpath, buf, size, offset, fi);
        free(newpath);
    }
    return err;
}

static int iconv_write(const char *path, const char *buf, size_t size,
                       off_t offset, struct fuse_file_info *fi)
{
    struct iconv *ic = iconv_get();
    char *newpath;
    int err = iconv_convpath(ic, path, &newpath, 0);
    if (!err) {
        err = fuse_fs_write(ic->next, newpath, buf, size, offset, fi);
        free(newpath);
    }
    return err;
}

static int iconv_statfs(const char *path, struct statvfs *stbuf)
{
    struct iconv *ic = iconv_get();
    char *newpath;
    int err = iconv_convpath(ic, path, &newpath, 0);
    if (!err) {
        err = fuse_fs_statfs(ic->next, newpath, stbuf);
        free(newpath);
    }
    return err;
}

static int iconv_flush(const char *path, struct fuse_file_info *fi)
{
    struct iconv *ic = iconv_get();
    char *newpath;
    int err = iconv_convpath(ic, path, &newpath, 0);
    if (!err) {
        err = fuse_fs_flush(ic->next, newpath, fi);
        free(newpath);
    }
    return err;
}

static int iconv_release(const char *path, struct fuse_file_info *fi)
{
    struct iconv *ic = iconv_get();
    char *newpath;
    int err = iconv_convpath(ic, path, &newpath, 0);
    if (!err) {
        err = fuse_fs_release(ic->next, newpath, fi);
        free(newpath);
    }
    return err;
}

static int iconv_fsync(const char *path, int isdatasync,
                       struct fuse_file_info *fi)
{
    struct iconv *ic = iconv_get();
    char *newpath;
    int err = iconv_convpath(ic, path, &newpath, 0);
    if (!err) {
        err = fuse_fs_fsync(ic->next, newpath, isdatasync, fi);
        free(newpath);
    }
    return err;
}

static int iconv_fsyncdir(const char *path, int isdatasync,
                          struct fuse_file_info *fi)
{
    struct iconv *ic = iconv_get();
    char *newpath;
    int err = iconv_convpath(ic, path, &newpath, 0);
    if (!err) {
        err = fuse_fs_fsyncdir(ic->next, newpath, isdatasync, fi);
        free(newpath);
    }
    return err;
}

static int iconv_setxattr(const char *path, const char *name,
                          const char *value, size_t size, int flags)
{
    struct iconv *ic = iconv_get();
    char *newpath;
    int err = iconv_convpath(ic, path, &newpath, 0);
    if (!err) {
        err = fuse_fs_setxattr(ic->next, newpath, name, value, size, flags);
        free(newpath);
    }
    return err;
}

static int iconv_getxattr(const char *path, const char *name, char *value,
                          size_t size)
{
    struct iconv *ic = iconv_get();
    char *newpath;
    int err = iconv_convpath(ic, path, &newpath, 0);
    if (!err) {
        err = fuse_fs_getxattr(ic->next, newpath, name, value, size);
        free(newpath);
    }
    return err;
}

static int iconv_listxattr(const char *path, char *list, size_t size)
{
    struct iconv *ic = iconv_get();
    char *newpath;
    int err = iconv_convpath(ic, path, &newpath, 0);
    if (!err) {
        err = fuse_fs_listxattr(ic->next, newpath, list, size);
        free(newpath);
    }
    return err;
}

static int iconv_removexattr(const char *path, const char *name)
{
    struct iconv *ic = iconv_get();
    char *newpath;
    int err = iconv_convpath(ic, path, &newpath, 0);
    if (!err) {
        err = fuse_fs_removexattr(ic->next, newpath, name);
        free(newpath);
    }
    return err;
}

static int iconv_lock(const char *path, struct fuse_file_info *fi, int cmd,
                      struct flock *lock)
{
    struct iconv *ic = iconv_get();
    char *newpath;
    int err = iconv_convpath(ic, path, &newpath, 0);
    if (!err) {
        err = fuse_fs_lock(ic->next, newpath, fi, cmd, lock);
        free(newpath);
    }
    return err;
}

static int iconv_bmap(const char *path, size_t blocksize, uint64_t *idx)
{
    struct iconv *ic = iconv_get();
    char *newpath;
    int err = iconv_convpath(ic, path, &newpath, 0);
    if (!err) {
        err = fuse_fs_bmap(ic->next, newpath, blocksize, idx);
        free(newpath);
    }
    return err;
}

static void *iconv_init(struct fuse_conn_info *conn)
{
    struct iconv *ic = iconv_get();
    fuse_fs_init(ic->next, conn);
    return ic;
}

static void iconv_destroy(void *data)
{
    struct iconv *ic = data;
    fuse_fs_destroy(ic->next);
    iconv_close(ic->tofs);
    iconv_close(ic->fromfs);
    pthread_mutex_destroy(&ic->lock);
    free(ic->from_code);
    free(ic->to_code);
    free(ic);
}

static struct fuse_operations iconv_oper = {
    .destroy      = iconv_destroy,
    .init   = iconv_init,
    .getattr      = iconv_getattr,
    .fgetattr     = iconv_fgetattr,
    .access = iconv_access,
    .readlink     = iconv_readlink,
    .opendir      = iconv_opendir,
    .readdir      = iconv_readdir,
    .releasedir   = iconv_releasedir,
    .mknod  = iconv_mknod,
    .mkdir  = iconv_mkdir,
    .symlink      = iconv_symlink,
    .unlink = iconv_unlink,
    .rmdir  = iconv_rmdir,
    .rename = iconv_rename,
    .link   = iconv_link,
    .chmod  = iconv_chmod,
    .chown  = iconv_chown,
    .truncate     = iconv_truncate,
    .ftruncate    = iconv_ftruncate,
    .utimens      = iconv_utimens,
    .create = iconv_create,
    .open   = iconv_open_file,
    .read   = iconv_read,
    .write  = iconv_write,
    .statfs = iconv_statfs,
    .flush  = iconv_flush,
    .release      = iconv_release,
    .fsync  = iconv_fsync,
    .fsyncdir     = iconv_fsyncdir,
    .setxattr     = iconv_setxattr,
    .getxattr     = iconv_getxattr,
    .listxattr    = iconv_listxattr,
    .removexattr= iconv_removexattr,
    .lock   = iconv_lock,
    .bmap   = iconv_bmap,
};

static struct fuse_opt iconv_opts[] = {
    FUSE_OPT_KEY("-h", 0),
    FUSE_OPT_KEY("--help", 0),
    { "from_code=%s", offsetof(struct iconv, from_code), 0 },
    { "to_code=%s", offsetof(struct iconv, to_code), 1 },
    FUSE_OPT_END
};

static void iconv_help(void)
{
    char *old = strdup(setlocale(LC_CTYPE, ""));
    char *charmap = strdup(nl_langinfo(CODESET));
    setlocale(LC_CTYPE, old);
    free(old);
    fprintf(stderr,
"    -o from_code=CHARSET   original encoding of file names (default: UTF-8)\n"
"    -o to_code=CHARSET     new encoding of the file names (default: %s)\n",
            charmap);
    free(charmap);
}

static int iconv_opt_proc(void *data, const char *arg, int key,
                           struct fuse_args *outargs)
{
    (void) data; (void) arg; (void) outargs;

    if (!key) {
        iconv_help();
        return -1;
    }

    return 1;
}

static struct fuse_fs *iconv_new(struct fuse_args *args,
                                  struct fuse_fs *next[])
{
    struct fuse_fs *fs;
    struct iconv *ic;
    char *old = NULL;
    const char *from;
    const char *to;

    ic = calloc(1, sizeof(struct iconv));
    if (ic == NULL) {
        fprintf(stderr, "fuse-iconv: memory allocation failed\n");
        return NULL;
    }

    if (fuse_opt_parse(args, ic, iconv_opts, iconv_opt_proc) == -1)
        goto out_free;

    if (!next[0] || next[1]) {
        fprintf(stderr, "fuse-iconv: exactly one next filesystem required\n");
        goto out_free;
    }

    from = ic->from_code ? ic->from_code : "UTF-8";
    to = ic->to_code ? ic->to_code : "";
    /* FIXME: detect charset equivalence? */
    if (!to[0])
        old = strdup(setlocale(LC_CTYPE, ""));
    ic->tofs = iconv_open(from, to);
    if (ic->tofs == (iconv_t) -1) {
        fprintf(stderr, "fuse-iconv: cannot convert from %s to %s\n",
                to, from);
        goto out_free;
    }
    ic->fromfs = iconv_open(to, from);
    if (ic->tofs == (iconv_t) -1) {
        fprintf(stderr, "fuse-iconv: cannot convert from %s to %s\n",
                from, to);
        goto out_iconv_close_to;
    }
    if (old) {
        setlocale(LC_CTYPE, old);
        free(old);
    }

    ic->next = next[0];
    fs = fuse_fs_new(&iconv_oper, sizeof(iconv_oper), ic);
    if (!fs)
        goto out_iconv_close_from;

    return fs;

 out_iconv_close_from:
    iconv_close(ic->fromfs);
 out_iconv_close_to:
    iconv_close(ic->tofs);
 out_free:
    free(ic->from_code);
    free(ic->to_code);
    free(ic);
    return NULL;
}

FUSE_REGISTER_MODULE(iconv, iconv_new);

Generated by  Doxygen 1.6.0   Back to index