mirror of
https://github.com/libfuse/libfuse.git
synced 2024-11-23 12:14:15 +08:00
db35a37def
This addresses: https://github.com/libfuse/libfuse/issues/724 HAVE_LIBC_VERSIONED_SYMBOLS configures the library if to use versioned symbols and is set at meson configuration time. External filesystems (the main target, actually) include fuse headers and the preprocessor then acts on HAVE_LIBC_VERSIONED_SYMBOLS. Problem was now that 'config.h' was not distributed with libfuse and so HAVE_LIBC_VERSIONED_SYMBOLS was never defined with external tools and the preprocessor did the wrong decision. This commit also increases the the minimal meson version, as this depends on meson feature only available in 0.50 <quote 'meson' > WARNING: Project specifies a minimum meson_ version '>= 0.42' but uses features which were added in newer versions: * 0.50.0: {'install arg in configure_file'} </quote> Additionally the config file has been renamed to "fuse_config.h" to avoid clashes - 'config.h' is not very specific.
690 lines
15 KiB
C
690 lines
15 KiB
C
/*
|
|
fuse subdir module: offset paths with a base directory
|
|
Copyright (C) 2007 Miklos Szeredi <miklos@szeredi.hu>
|
|
|
|
This program can be distributed under the terms of the GNU LGPLv2.
|
|
See the file COPYING.LIB
|
|
*/
|
|
|
|
#include <fuse_config.h>
|
|
|
|
#include <fuse.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <stddef.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
|
|
struct subdir {
|
|
char *base;
|
|
size_t baselen;
|
|
int rellinks;
|
|
struct fuse_fs *next;
|
|
};
|
|
|
|
static struct subdir *subdir_get(void)
|
|
{
|
|
return fuse_get_context()->private_data;
|
|
}
|
|
|
|
static int subdir_addpath(struct subdir *d, const char *path, char **newpathp)
|
|
{
|
|
char *newpath = NULL;
|
|
|
|
if (path != NULL) {
|
|
unsigned newlen = d->baselen + strlen(path);
|
|
|
|
newpath = malloc(newlen + 2);
|
|
if (!newpath)
|
|
return -ENOMEM;
|
|
|
|
if (path[0] == '/')
|
|
path++;
|
|
strcpy(newpath, d->base);
|
|
strcpy(newpath + d->baselen, path);
|
|
if (!newpath[0])
|
|
strcpy(newpath, ".");
|
|
}
|
|
*newpathp = newpath;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int subdir_getattr(const char *path, struct stat *stbuf,
|
|
struct fuse_file_info *fi)
|
|
{
|
|
struct subdir *d = subdir_get();
|
|
char *newpath;
|
|
int err = subdir_addpath(d, path, &newpath);
|
|
if (!err) {
|
|
err = fuse_fs_getattr(d->next, newpath, stbuf, fi);
|
|
free(newpath);
|
|
}
|
|
return err;
|
|
}
|
|
|
|
static int subdir_access(const char *path, int mask)
|
|
{
|
|
struct subdir *d = subdir_get();
|
|
char *newpath;
|
|
int err = subdir_addpath(d, path, &newpath);
|
|
if (!err) {
|
|
err = fuse_fs_access(d->next, newpath, mask);
|
|
free(newpath);
|
|
}
|
|
return err;
|
|
}
|
|
|
|
|
|
static int count_components(const char *p)
|
|
{
|
|
int ctr;
|
|
|
|
for (; *p == '/'; p++);
|
|
for (ctr = 0; *p; ctr++) {
|
|
for (; *p && *p != '/'; p++);
|
|
for (; *p == '/'; p++);
|
|
}
|
|
return ctr;
|
|
}
|
|
|
|
static void strip_common(const char **sp, const char **tp)
|
|
{
|
|
const char *s = *sp;
|
|
const char *t = *tp;
|
|
do {
|
|
for (; *s == '/'; s++);
|
|
for (; *t == '/'; t++);
|
|
*tp = t;
|
|
*sp = s;
|
|
for (; *s == *t && *s && *s != '/'; s++, t++);
|
|
} while ((*s == *t && *s) || (!*s && *t == '/') || (*s == '/' && !*t));
|
|
}
|
|
|
|
static void transform_symlink(struct subdir *d, const char *path,
|
|
char *buf, size_t size)
|
|
{
|
|
const char *l = buf;
|
|
size_t llen;
|
|
char *s;
|
|
int dotdots;
|
|
int i;
|
|
|
|
if (l[0] != '/' || d->base[0] != '/')
|
|
return;
|
|
|
|
strip_common(&l, &path);
|
|
if (l - buf < (long) d->baselen)
|
|
return;
|
|
|
|
dotdots = count_components(path);
|
|
if (!dotdots)
|
|
return;
|
|
dotdots--;
|
|
|
|
llen = strlen(l);
|
|
if (dotdots * 3 + llen + 2 > size)
|
|
return;
|
|
|
|
s = buf + dotdots * 3;
|
|
if (llen)
|
|
memmove(s, l, llen + 1);
|
|
else if (!dotdots)
|
|
strcpy(s, ".");
|
|
else
|
|
*s = '\0';
|
|
|
|
for (s = buf, i = 0; i < dotdots; i++, s += 3)
|
|
memcpy(s, "../", 3);
|
|
}
|
|
|
|
|
|
static int subdir_readlink(const char *path, char *buf, size_t size)
|
|
{
|
|
struct subdir *d = subdir_get();
|
|
char *newpath;
|
|
int err = subdir_addpath(d, path, &newpath);
|
|
if (!err) {
|
|
err = fuse_fs_readlink(d->next, newpath, buf, size);
|
|
if (!err && d->rellinks)
|
|
transform_symlink(d, newpath, buf, size);
|
|
free(newpath);
|
|
}
|
|
return err;
|
|
}
|
|
|
|
static int subdir_opendir(const char *path, struct fuse_file_info *fi)
|
|
{
|
|
struct subdir *d = subdir_get();
|
|
char *newpath;
|
|
int err = subdir_addpath(d, path, &newpath);
|
|
if (!err) {
|
|
err = fuse_fs_opendir(d->next, newpath, fi);
|
|
free(newpath);
|
|
}
|
|
return err;
|
|
}
|
|
|
|
static int subdir_readdir(const char *path, void *buf,
|
|
fuse_fill_dir_t filler, off_t offset,
|
|
struct fuse_file_info *fi,
|
|
enum fuse_readdir_flags flags)
|
|
{
|
|
struct subdir *d = subdir_get();
|
|
char *newpath;
|
|
int err = subdir_addpath(d, path, &newpath);
|
|
if (!err) {
|
|
err = fuse_fs_readdir(d->next, newpath, buf, filler, offset,
|
|
fi, flags);
|
|
free(newpath);
|
|
}
|
|
return err;
|
|
}
|
|
|
|
static int subdir_releasedir(const char *path, struct fuse_file_info *fi)
|
|
{
|
|
struct subdir *d = subdir_get();
|
|
char *newpath;
|
|
int err = subdir_addpath(d, path, &newpath);
|
|
if (!err) {
|
|
err = fuse_fs_releasedir(d->next, newpath, fi);
|
|
free(newpath);
|
|
}
|
|
return err;
|
|
}
|
|
|
|
static int subdir_mknod(const char *path, mode_t mode, dev_t rdev)
|
|
{
|
|
struct subdir *d = subdir_get();
|
|
char *newpath;
|
|
int err = subdir_addpath(d, path, &newpath);
|
|
if (!err) {
|
|
err = fuse_fs_mknod(d->next, newpath, mode, rdev);
|
|
free(newpath);
|
|
}
|
|
return err;
|
|
}
|
|
|
|
static int subdir_mkdir(const char *path, mode_t mode)
|
|
{
|
|
struct subdir *d = subdir_get();
|
|
char *newpath;
|
|
int err = subdir_addpath(d, path, &newpath);
|
|
if (!err) {
|
|
err = fuse_fs_mkdir(d->next, newpath, mode);
|
|
free(newpath);
|
|
}
|
|
return err;
|
|
}
|
|
|
|
static int subdir_unlink(const char *path)
|
|
{
|
|
struct subdir *d = subdir_get();
|
|
char *newpath;
|
|
int err = subdir_addpath(d, path, &newpath);
|
|
if (!err) {
|
|
err = fuse_fs_unlink(d->next, newpath);
|
|
free(newpath);
|
|
}
|
|
return err;
|
|
}
|
|
|
|
static int subdir_rmdir(const char *path)
|
|
{
|
|
struct subdir *d = subdir_get();
|
|
char *newpath;
|
|
int err = subdir_addpath(d, path, &newpath);
|
|
if (!err) {
|
|
err = fuse_fs_rmdir(d->next, newpath);
|
|
free(newpath);
|
|
}
|
|
return err;
|
|
}
|
|
|
|
static int subdir_symlink(const char *from, const char *path)
|
|
{
|
|
struct subdir *d = subdir_get();
|
|
char *newpath;
|
|
int err = subdir_addpath(d, path, &newpath);
|
|
if (!err) {
|
|
err = fuse_fs_symlink(d->next, from, newpath);
|
|
free(newpath);
|
|
}
|
|
return err;
|
|
}
|
|
|
|
static int subdir_rename(const char *from, const char *to, unsigned int flags)
|
|
{
|
|
struct subdir *d = subdir_get();
|
|
char *newfrom;
|
|
char *newto;
|
|
int err = subdir_addpath(d, from, &newfrom);
|
|
if (!err) {
|
|
err = subdir_addpath(d, to, &newto);
|
|
if (!err) {
|
|
err = fuse_fs_rename(d->next, newfrom, newto, flags);
|
|
free(newto);
|
|
}
|
|
free(newfrom);
|
|
}
|
|
return err;
|
|
}
|
|
|
|
static int subdir_link(const char *from, const char *to)
|
|
{
|
|
struct subdir *d = subdir_get();
|
|
char *newfrom;
|
|
char *newto;
|
|
int err = subdir_addpath(d, from, &newfrom);
|
|
if (!err) {
|
|
err = subdir_addpath(d, to, &newto);
|
|
if (!err) {
|
|
err = fuse_fs_link(d->next, newfrom, newto);
|
|
free(newto);
|
|
}
|
|
free(newfrom);
|
|
}
|
|
return err;
|
|
}
|
|
|
|
static int subdir_chmod(const char *path, mode_t mode,
|
|
struct fuse_file_info *fi)
|
|
{
|
|
struct subdir *d = subdir_get();
|
|
char *newpath;
|
|
int err = subdir_addpath(d, path, &newpath);
|
|
if (!err) {
|
|
err = fuse_fs_chmod(d->next, newpath, mode, fi);
|
|
free(newpath);
|
|
}
|
|
return err;
|
|
}
|
|
|
|
static int subdir_chown(const char *path, uid_t uid, gid_t gid,
|
|
struct fuse_file_info *fi)
|
|
{
|
|
struct subdir *d = subdir_get();
|
|
char *newpath;
|
|
int err = subdir_addpath(d, path, &newpath);
|
|
if (!err) {
|
|
err = fuse_fs_chown(d->next, newpath, uid, gid, fi);
|
|
free(newpath);
|
|
}
|
|
return err;
|
|
}
|
|
|
|
static int subdir_truncate(const char *path, off_t size,
|
|
struct fuse_file_info *fi)
|
|
{
|
|
struct subdir *d = subdir_get();
|
|
char *newpath;
|
|
int err = subdir_addpath(d, path, &newpath);
|
|
if (!err) {
|
|
err = fuse_fs_truncate(d->next, newpath, size, fi);
|
|
free(newpath);
|
|
}
|
|
return err;
|
|
}
|
|
|
|
static int subdir_utimens(const char *path, const struct timespec ts[2],
|
|
struct fuse_file_info *fi)
|
|
{
|
|
struct subdir *d = subdir_get();
|
|
char *newpath;
|
|
int err = subdir_addpath(d, path, &newpath);
|
|
if (!err) {
|
|
err = fuse_fs_utimens(d->next, newpath, ts, fi);
|
|
free(newpath);
|
|
}
|
|
return err;
|
|
}
|
|
|
|
static int subdir_create(const char *path, mode_t mode,
|
|
struct fuse_file_info *fi)
|
|
{
|
|
struct subdir *d = subdir_get();
|
|
char *newpath;
|
|
int err = subdir_addpath(d, path, &newpath);
|
|
if (!err) {
|
|
err = fuse_fs_create(d->next, newpath, mode, fi);
|
|
free(newpath);
|
|
}
|
|
return err;
|
|
}
|
|
|
|
static int subdir_open(const char *path, struct fuse_file_info *fi)
|
|
{
|
|
struct subdir *d = subdir_get();
|
|
char *newpath;
|
|
int err = subdir_addpath(d, path, &newpath);
|
|
if (!err) {
|
|
err = fuse_fs_open(d->next, newpath, fi);
|
|
free(newpath);
|
|
}
|
|
return err;
|
|
}
|
|
|
|
static int subdir_read_buf(const char *path, struct fuse_bufvec **bufp,
|
|
size_t size, off_t offset, struct fuse_file_info *fi)
|
|
{
|
|
struct subdir *d = subdir_get();
|
|
char *newpath;
|
|
int err = subdir_addpath(d, path, &newpath);
|
|
if (!err) {
|
|
err = fuse_fs_read_buf(d->next, newpath, bufp, size, offset, fi);
|
|
free(newpath);
|
|
}
|
|
return err;
|
|
}
|
|
|
|
static int subdir_write_buf(const char *path, struct fuse_bufvec *buf,
|
|
off_t offset, struct fuse_file_info *fi)
|
|
{
|
|
struct subdir *d = subdir_get();
|
|
char *newpath;
|
|
int err = subdir_addpath(d, path, &newpath);
|
|
if (!err) {
|
|
err = fuse_fs_write_buf(d->next, newpath, buf, offset, fi);
|
|
free(newpath);
|
|
}
|
|
return err;
|
|
}
|
|
|
|
static int subdir_statfs(const char *path, struct statvfs *stbuf)
|
|
{
|
|
struct subdir *d = subdir_get();
|
|
char *newpath;
|
|
int err = subdir_addpath(d, path, &newpath);
|
|
if (!err) {
|
|
err = fuse_fs_statfs(d->next, newpath, stbuf);
|
|
free(newpath);
|
|
}
|
|
return err;
|
|
}
|
|
|
|
static int subdir_flush(const char *path, struct fuse_file_info *fi)
|
|
{
|
|
struct subdir *d = subdir_get();
|
|
char *newpath;
|
|
int err = subdir_addpath(d, path, &newpath);
|
|
if (!err) {
|
|
err = fuse_fs_flush(d->next, newpath, fi);
|
|
free(newpath);
|
|
}
|
|
return err;
|
|
}
|
|
|
|
static int subdir_release(const char *path, struct fuse_file_info *fi)
|
|
{
|
|
struct subdir *d = subdir_get();
|
|
char *newpath;
|
|
int err = subdir_addpath(d, path, &newpath);
|
|
if (!err) {
|
|
err = fuse_fs_release(d->next, newpath, fi);
|
|
free(newpath);
|
|
}
|
|
return err;
|
|
}
|
|
|
|
static int subdir_fsync(const char *path, int isdatasync,
|
|
struct fuse_file_info *fi)
|
|
{
|
|
struct subdir *d = subdir_get();
|
|
char *newpath;
|
|
int err = subdir_addpath(d, path, &newpath);
|
|
if (!err) {
|
|
err = fuse_fs_fsync(d->next, newpath, isdatasync, fi);
|
|
free(newpath);
|
|
}
|
|
return err;
|
|
}
|
|
|
|
static int subdir_fsyncdir(const char *path, int isdatasync,
|
|
struct fuse_file_info *fi)
|
|
{
|
|
struct subdir *d = subdir_get();
|
|
char *newpath;
|
|
int err = subdir_addpath(d, path, &newpath);
|
|
if (!err) {
|
|
err = fuse_fs_fsyncdir(d->next, newpath, isdatasync, fi);
|
|
free(newpath);
|
|
}
|
|
return err;
|
|
}
|
|
|
|
static int subdir_setxattr(const char *path, const char *name,
|
|
const char *value, size_t size, int flags)
|
|
{
|
|
struct subdir *d = subdir_get();
|
|
char *newpath;
|
|
int err = subdir_addpath(d, path, &newpath);
|
|
if (!err) {
|
|
err = fuse_fs_setxattr(d->next, newpath, name, value, size,
|
|
flags);
|
|
free(newpath);
|
|
}
|
|
return err;
|
|
}
|
|
|
|
static int subdir_getxattr(const char *path, const char *name, char *value,
|
|
size_t size)
|
|
{
|
|
struct subdir *d = subdir_get();
|
|
char *newpath;
|
|
int err = subdir_addpath(d, path, &newpath);
|
|
if (!err) {
|
|
err = fuse_fs_getxattr(d->next, newpath, name, value, size);
|
|
free(newpath);
|
|
}
|
|
return err;
|
|
}
|
|
|
|
static int subdir_listxattr(const char *path, char *list, size_t size)
|
|
{
|
|
struct subdir *d = subdir_get();
|
|
char *newpath;
|
|
int err = subdir_addpath(d, path, &newpath);
|
|
if (!err) {
|
|
err = fuse_fs_listxattr(d->next, newpath, list, size);
|
|
free(newpath);
|
|
}
|
|
return err;
|
|
}
|
|
|
|
static int subdir_removexattr(const char *path, const char *name)
|
|
{
|
|
struct subdir *d = subdir_get();
|
|
char *newpath;
|
|
int err = subdir_addpath(d, path, &newpath);
|
|
if (!err) {
|
|
err = fuse_fs_removexattr(d->next, newpath, name);
|
|
free(newpath);
|
|
}
|
|
return err;
|
|
}
|
|
|
|
static int subdir_lock(const char *path, struct fuse_file_info *fi, int cmd,
|
|
struct flock *lock)
|
|
{
|
|
struct subdir *d = subdir_get();
|
|
char *newpath;
|
|
int err = subdir_addpath(d, path, &newpath);
|
|
if (!err) {
|
|
err = fuse_fs_lock(d->next, newpath, fi, cmd, lock);
|
|
free(newpath);
|
|
}
|
|
return err;
|
|
}
|
|
|
|
static int subdir_flock(const char *path, struct fuse_file_info *fi, int op)
|
|
{
|
|
struct subdir *d = subdir_get();
|
|
char *newpath;
|
|
int err = subdir_addpath(d, path, &newpath);
|
|
if (!err) {
|
|
err = fuse_fs_flock(d->next, newpath, fi, op);
|
|
free(newpath);
|
|
}
|
|
return err;
|
|
}
|
|
|
|
static int subdir_bmap(const char *path, size_t blocksize, uint64_t *idx)
|
|
{
|
|
struct subdir *d = subdir_get();
|
|
char *newpath;
|
|
int err = subdir_addpath(d, path, &newpath);
|
|
if (!err) {
|
|
err = fuse_fs_bmap(d->next, newpath, blocksize, idx);
|
|
free(newpath);
|
|
}
|
|
return err;
|
|
}
|
|
|
|
static off_t subdir_lseek(const char *path, off_t off, int whence,
|
|
struct fuse_file_info *fi)
|
|
{
|
|
struct subdir *ic = subdir_get();
|
|
char *newpath;
|
|
int res = subdir_addpath(ic, path, &newpath);
|
|
if (!res) {
|
|
res = fuse_fs_lseek(ic->next, newpath, off, whence, fi);
|
|
free(newpath);
|
|
}
|
|
return res;
|
|
}
|
|
|
|
static void *subdir_init(struct fuse_conn_info *conn,
|
|
struct fuse_config *cfg)
|
|
{
|
|
struct subdir *d = subdir_get();
|
|
fuse_fs_init(d->next, conn, cfg);
|
|
/* Don't touch cfg->nullpath_ok, we can work with
|
|
either */
|
|
return d;
|
|
}
|
|
|
|
static void subdir_destroy(void *data)
|
|
{
|
|
struct subdir *d = data;
|
|
fuse_fs_destroy(d->next);
|
|
free(d->base);
|
|
free(d);
|
|
}
|
|
|
|
static const struct fuse_operations subdir_oper = {
|
|
.destroy = subdir_destroy,
|
|
.init = subdir_init,
|
|
.getattr = subdir_getattr,
|
|
.access = subdir_access,
|
|
.readlink = subdir_readlink,
|
|
.opendir = subdir_opendir,
|
|
.readdir = subdir_readdir,
|
|
.releasedir = subdir_releasedir,
|
|
.mknod = subdir_mknod,
|
|
.mkdir = subdir_mkdir,
|
|
.symlink = subdir_symlink,
|
|
.unlink = subdir_unlink,
|
|
.rmdir = subdir_rmdir,
|
|
.rename = subdir_rename,
|
|
.link = subdir_link,
|
|
.chmod = subdir_chmod,
|
|
.chown = subdir_chown,
|
|
.truncate = subdir_truncate,
|
|
.utimens = subdir_utimens,
|
|
.create = subdir_create,
|
|
.open = subdir_open,
|
|
.read_buf = subdir_read_buf,
|
|
.write_buf = subdir_write_buf,
|
|
.statfs = subdir_statfs,
|
|
.flush = subdir_flush,
|
|
.release = subdir_release,
|
|
.fsync = subdir_fsync,
|
|
.fsyncdir = subdir_fsyncdir,
|
|
.setxattr = subdir_setxattr,
|
|
.getxattr = subdir_getxattr,
|
|
.listxattr = subdir_listxattr,
|
|
.removexattr = subdir_removexattr,
|
|
.lock = subdir_lock,
|
|
.flock = subdir_flock,
|
|
.bmap = subdir_bmap,
|
|
.lseek = subdir_lseek,
|
|
};
|
|
|
|
static const struct fuse_opt subdir_opts[] = {
|
|
FUSE_OPT_KEY("-h", 0),
|
|
FUSE_OPT_KEY("--help", 0),
|
|
{ "subdir=%s", offsetof(struct subdir, base), 0 },
|
|
{ "rellinks", offsetof(struct subdir, rellinks), 1 },
|
|
{ "norellinks", offsetof(struct subdir, rellinks), 0 },
|
|
FUSE_OPT_END
|
|
};
|
|
|
|
static void subdir_help(void)
|
|
{
|
|
printf(
|
|
" -o subdir=DIR prepend this directory to all paths (mandatory)\n"
|
|
" -o [no]rellinks transform absolute symlinks to relative\n");
|
|
}
|
|
|
|
static int subdir_opt_proc(void *data, const char *arg, int key,
|
|
struct fuse_args *outargs)
|
|
{
|
|
(void) data; (void) arg; (void) outargs;
|
|
|
|
if (!key) {
|
|
subdir_help();
|
|
return -1;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
static struct fuse_fs *subdir_new(struct fuse_args *args,
|
|
struct fuse_fs *next[])
|
|
{
|
|
struct fuse_fs *fs;
|
|
struct subdir *d;
|
|
|
|
d = calloc(1, sizeof(struct subdir));
|
|
if (d == NULL) {
|
|
fuse_log(FUSE_LOG_ERR, "fuse-subdir: memory allocation failed\n");
|
|
return NULL;
|
|
}
|
|
|
|
if (fuse_opt_parse(args, d, subdir_opts, subdir_opt_proc) == -1)
|
|
goto out_free;
|
|
|
|
if (!next[0] || next[1]) {
|
|
fuse_log(FUSE_LOG_ERR, "fuse-subdir: exactly one next filesystem required\n");
|
|
goto out_free;
|
|
}
|
|
|
|
if (!d->base) {
|
|
fuse_log(FUSE_LOG_ERR, "fuse-subdir: missing 'subdir' option\n");
|
|
goto out_free;
|
|
}
|
|
|
|
if (d->base[0] && d->base[strlen(d->base)-1] != '/') {
|
|
char *tmp = realloc(d->base, strlen(d->base) + 2);
|
|
if (!tmp) {
|
|
fuse_log(FUSE_LOG_ERR, "fuse-subdir: memory allocation failed\n");
|
|
goto out_free;
|
|
}
|
|
d->base = tmp;
|
|
strcat(d->base, "/");
|
|
}
|
|
d->baselen = strlen(d->base);
|
|
d->next = next[0];
|
|
fs = fuse_fs_new(&subdir_oper, sizeof(subdir_oper), d);
|
|
if (!fs)
|
|
goto out_free;
|
|
return fs;
|
|
|
|
out_free:
|
|
free(d->base);
|
|
free(d);
|
|
return NULL;
|
|
}
|
|
|
|
FUSE_REGISTER_MODULE(subdir, subdir_new);
|