зеркало из https://github.com/github/libprojfs.git
projfs_new accepts FUSE args, and initial proj opt
We need to avoid wait_mount in tests so its stat(2) doesn't trigger projection and clear the projection bit.
This commit is contained in:
Родитель
f10aaaa41e
Коммит
566f250dfa
18
configure.ac
18
configure.ac
|
@ -39,24 +39,6 @@ AC_SYS_LARGEFILE
|
|||
|
||||
AX_COMPILER_FLAGS()
|
||||
|
||||
AC_ARG_ENABLE([debug],
|
||||
[AS_HELP_STRING([--enable-debug],
|
||||
[Enable libprojfs debugging output])]
|
||||
)dnl
|
||||
|
||||
AS_IF([test ":$enable_debug" = ":yes"],
|
||||
[AC_DEFINE(PROJFS_DEBUG, 1, [Enable libprojfs debugging output])]
|
||||
)dnl
|
||||
|
||||
AC_ARG_ENABLE([fuse_debug],
|
||||
[AS_HELP_STRING([--enable-fuse-debug],
|
||||
[Enable FUSE library debugging output])]
|
||||
)dnl
|
||||
|
||||
AS_IF([test ":$enable_fuse_debug" = ":yes"],
|
||||
[AC_DEFINE(PROJFS_FUSE_DEBUG, 1, [Enable FUSE library debugging output])]
|
||||
)dnl
|
||||
|
||||
AC_CHECK_PROGS([DIFF], [diff])
|
||||
AC_ARG_VAR([DIFF], [File comparison tool])
|
||||
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 3a2b9dfa9f66dec893c3e9fb08db25c1a546fee5
|
||||
Subproject commit 91fb51e235852e4c45b1fcf09ad1ab77d90c6058
|
|
@ -104,7 +104,8 @@ struct projfs_handlers {
|
|||
*/
|
||||
struct projfs *projfs_new(const char *lowerdir, const char *mountdir,
|
||||
const struct projfs_handlers *handlers,
|
||||
size_t handlers_size, void *user_data);
|
||||
size_t handlers_size, void *user_data,
|
||||
int argc, const char **argv);
|
||||
|
||||
/**
|
||||
* Retrieve the private user data from a projfs handle.
|
||||
|
|
135
lib/projfs.c
135
lib/projfs.c
|
@ -37,6 +37,7 @@
|
|||
#include <sys/syscall.h>
|
||||
#include <attr/xattr.h>
|
||||
#include <unistd.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include "fdtable.h"
|
||||
#include "projfs.h"
|
||||
|
@ -46,6 +47,17 @@
|
|||
// TODO: make this value configurable
|
||||
#define PROJ_WAIT_MSEC 5000
|
||||
|
||||
struct projfs_config {
|
||||
int initial;
|
||||
};
|
||||
|
||||
#define PROJFS_OPT(t, p, v) { t, offsetof(struct projfs_config, p), v }
|
||||
|
||||
static struct fuse_opt projfs_opts[] = {
|
||||
PROJFS_OPT("initial", initial, 1),
|
||||
FUSE_OPT_END
|
||||
};
|
||||
|
||||
struct projfs {
|
||||
char *lowerdir;
|
||||
char *mountdir;
|
||||
|
@ -57,6 +69,8 @@ struct projfs {
|
|||
pthread_t thread_id;
|
||||
struct fdtable *fdtable;
|
||||
int error;
|
||||
struct fuse_args args;
|
||||
struct projfs_config config;
|
||||
};
|
||||
|
||||
typedef int (*projfs_handler_t)(struct projfs_event *);
|
||||
|
@ -583,9 +597,11 @@ static int projfs_op_getattr(char const *path, struct stat *attr,
|
|||
res = fstat(fi->fh, attr);
|
||||
else {
|
||||
path = make_relative_path(path);
|
||||
res = project_dir("getattr", path, 1);
|
||||
if (res)
|
||||
return -res;
|
||||
if (strcmp(path, ".") != 0) {
|
||||
res = project_dir("getattr", path, 1);
|
||||
if (res)
|
||||
return -res;
|
||||
}
|
||||
res = fstatat(get_fuse_context_lowerdir_fd(), path, attr,
|
||||
AT_SYMLINK_NOFOLLOW);
|
||||
}
|
||||
|
@ -1319,14 +1335,6 @@ static struct fuse_operations projfs_ops = {
|
|||
// copy_file_range
|
||||
};
|
||||
|
||||
#ifdef PROJFS_DEBUG
|
||||
#define DEBUG_ARGV "--debug",
|
||||
#define DEBUG_ARGC 1
|
||||
#else
|
||||
#define DEBUG_ARGV
|
||||
#define DEBUG_ARGC 0
|
||||
#endif
|
||||
|
||||
static void projfs_set_session(struct projfs *fs, struct fuse_session *se)
|
||||
{
|
||||
if (fs == NULL)
|
||||
|
@ -1339,10 +1347,12 @@ static void projfs_set_session(struct projfs *fs, struct fuse_session *se)
|
|||
|
||||
struct projfs *projfs_new(const char *lowerdir, const char *mountdir,
|
||||
const struct projfs_handlers *handlers,
|
||||
size_t handlers_size, void *user_data)
|
||||
size_t handlers_size, void *user_data,
|
||||
int argc, const char **argv)
|
||||
{
|
||||
struct projfs *fs;
|
||||
size_t len;
|
||||
int i;
|
||||
|
||||
// TODO: prevent failure with relative lowerdir
|
||||
if (lowerdir == NULL) {
|
||||
|
@ -1401,8 +1411,30 @@ struct projfs *projfs_new(const char *lowerdir, const char *mountdir,
|
|||
goto out_mutex;
|
||||
}
|
||||
|
||||
if (fuse_opt_add_arg(&fs->args, "projfs") != 0) {
|
||||
fprintf(stderr, "projfs: failed to allocate argument\n");
|
||||
goto out_fdtable;
|
||||
}
|
||||
|
||||
for (i = 0; i < argc; ++i) {
|
||||
if (fuse_opt_add_arg(&fs->args, argv[i]) != 0) {
|
||||
fprintf(stderr, "projfs: failed to allocate "
|
||||
"argument\n");
|
||||
goto out_fdtable;
|
||||
}
|
||||
}
|
||||
|
||||
if (fuse_opt_parse(&fs->args, &fs->config, projfs_opts, NULL) == -1) {
|
||||
fprintf(stderr, "projfs: unable to parse arguments\n");
|
||||
goto out_fdtable;
|
||||
}
|
||||
|
||||
return fs;
|
||||
|
||||
out_fdtable:
|
||||
fuse_opt_free_args(&fs->args);
|
||||
fdtable_destroy(fs->fdtable);
|
||||
|
||||
out_mutex:
|
||||
pthread_mutex_destroy(&fs->mutex);
|
||||
out_mount:
|
||||
|
@ -1420,37 +1452,6 @@ void *projfs_get_user_data(struct projfs *fs)
|
|||
return fs->user_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 1 if dir is empty, 0 if not, -1 if an error occurred (errno set)
|
||||
*/
|
||||
static int check_dir_empty(const char *path)
|
||||
{
|
||||
int err, is_empty = 1;
|
||||
struct dirent *e;
|
||||
DIR *d = opendir(path);
|
||||
if (!d)
|
||||
return -1;
|
||||
|
||||
while (1) {
|
||||
errno = 0;
|
||||
e = readdir(d);
|
||||
if (e == NULL) {
|
||||
err = errno;
|
||||
closedir(d);
|
||||
if (err == 0)
|
||||
return is_empty;
|
||||
errno = err;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (strcmp(e->d_name, ".") == 0 ||
|
||||
strcmp(e->d_name, "..") == 0)
|
||||
; /* ignore */
|
||||
else
|
||||
is_empty = 0;
|
||||
}
|
||||
}
|
||||
|
||||
#define SPARSE_TEST_FILENAME ".libprojfs-sparse-test"
|
||||
#define SPARSE_TEST_SIZE_BYTES 1048576
|
||||
|
||||
|
@ -1509,9 +1510,6 @@ close:
|
|||
static void *projfs_loop(void *data)
|
||||
{
|
||||
struct projfs *fs = (struct projfs *)data;
|
||||
const char *argv[] = { "projfs", DEBUG_ARGV NULL };
|
||||
int argc = 1 + DEBUG_ARGC;
|
||||
struct fuse_args args = FUSE_ARGS_INIT(argc, (char **)argv);
|
||||
struct fuse_loop_config loop;
|
||||
struct fuse *fuse;
|
||||
struct fuse_session *se;
|
||||
|
@ -1541,20 +1539,20 @@ static void *projfs_loop(void *data)
|
|||
goto out_close;
|
||||
}
|
||||
|
||||
/* mark lowerdir as needing projection if it's empty (because the
|
||||
* provider creates it for us before we start running) -- probably want
|
||||
* to modify this behaviour in the future */
|
||||
res = check_dir_empty(fs->lowerdir);
|
||||
res = test_sparse_support(fs->lowerdir_fd);
|
||||
if (res == -1) {
|
||||
fprintf(stderr, "projfs: could not check if lowerdir "
|
||||
"is empty: %s: %s\n",
|
||||
fs->lowerdir, strerror(errno));
|
||||
fprintf(stderr, "projfs: unable to test sparse file support: "
|
||||
"%s/%s: %s\n",
|
||||
fs->lowerdir, SPARSE_TEST_FILENAME, strerror(errno));
|
||||
res = 3;
|
||||
goto out_close;
|
||||
}
|
||||
} else if (res == 0)
|
||||
fprintf(stderr, "projfs: sparse files may not be supported by "
|
||||
"lower filesystem: %s\n", fs->lowerdir);
|
||||
else if (res == 1)
|
||||
res = 0;
|
||||
|
||||
if (res == 1) {
|
||||
/* dir is empty */
|
||||
if (fs->config.initial == 1) {
|
||||
if (set_proj_state_xattr(fs->lowerdir_fd,
|
||||
PROJ_STATE_EMPTY, 0) == -1) {
|
||||
fprintf(stderr, "projfs: could not set projection "
|
||||
|
@ -1565,22 +1563,9 @@ static void *projfs_loop(void *data)
|
|||
}
|
||||
}
|
||||
|
||||
res = test_sparse_support(fs->lowerdir_fd);
|
||||
if (res == -1) {
|
||||
fprintf(stderr, "projfs: unable to test sparse file support: "
|
||||
"%s/%s: %s\n",
|
||||
fs->lowerdir, SPARSE_TEST_FILENAME, strerror(errno));
|
||||
res = 5;
|
||||
goto out_close;
|
||||
} else if (res == 0)
|
||||
fprintf(stderr, "projfs: sparse files may not be supported by "
|
||||
"lower filesystem: %s\n", fs->lowerdir);
|
||||
else if (res == 1)
|
||||
res = 0;
|
||||
|
||||
fuse = fuse_new(&args, &projfs_ops, sizeof(projfs_ops), fs);
|
||||
fuse = fuse_new(&fs->args, &projfs_ops, sizeof(projfs_ops), fs);
|
||||
if (fuse == NULL) {
|
||||
res = 6;
|
||||
res = 5;
|
||||
goto out_close;
|
||||
}
|
||||
|
||||
|
@ -1589,13 +1574,13 @@ static void *projfs_loop(void *data)
|
|||
|
||||
// TODO: defer all signal handling to user, once we remove FUSE
|
||||
if (fuse_set_signal_handlers(se) != 0) {
|
||||
res = 7;
|
||||
res = 6;
|
||||
goto out_session;
|
||||
}
|
||||
|
||||
// TODO: mount with x-gvfs-hide option and maybe others for KDE, etc.
|
||||
if (fuse_mount(fuse, fs->mountdir) != 0) {
|
||||
res = 8;
|
||||
res = 7;
|
||||
goto out_signal;
|
||||
}
|
||||
|
||||
|
@ -1607,7 +1592,7 @@ static void *projfs_loop(void *data)
|
|||
if ((err = fuse_loop_mt(fuse, &loop)) != 0) {
|
||||
if (err > 0)
|
||||
fprintf(stderr, "projfs: %s signal\n", strsignal(err));
|
||||
res = 9;
|
||||
res = 8;
|
||||
}
|
||||
|
||||
fuse_session_unmount(se);
|
||||
|
@ -1690,6 +1675,8 @@ void *projfs_stop(struct projfs *fs)
|
|||
fs->error);
|
||||
}
|
||||
|
||||
fuse_opt_free_args(&fs->args);
|
||||
|
||||
fdtable_destroy(fs->fdtable);
|
||||
|
||||
pthread_mutex_destroy(&fs->mutex);
|
||||
|
|
|
@ -27,12 +27,14 @@ test_common = test_common.c \
|
|||
$(top_srcdir)/include/projfs_notify.h
|
||||
|
||||
check_PROGRAMS = get_strerror \
|
||||
test_args \
|
||||
test_fdtable \
|
||||
test_handlers \
|
||||
test_simple \
|
||||
wait_mount
|
||||
|
||||
get_strerror_SOURCES = get_strerror.c $(test_common)
|
||||
test_args_SOURCES = test_args.c $(test_common)
|
||||
test_fdtable_SOURCES = test_fdtable.c $(test_common) \
|
||||
../lib/fdtable.c ../lib/fdtable.h
|
||||
test_handlers_SOURCES = test_handlers.c $(test_common)
|
||||
|
@ -53,7 +55,8 @@ TESTS = t000-mirror-read.t \
|
|||
t202-event-deny.t \
|
||||
t203-event-null.t \
|
||||
t204-event-allow.t \
|
||||
t205-event-locking.t
|
||||
t205-event-locking.t \
|
||||
t300-args-initial.t
|
||||
|
||||
EXTRA_DIST = README.md chainlint.sed clean_test_dirs.sh \
|
||||
test-lib.sh test-lib-event.sh test-lib-functions.sh $(TESTS)
|
||||
|
|
|
@ -25,9 +25,6 @@ given path.
|
|||
|
||||
projfs_start test_handlers source target --timeout 1 --lock-file lock || exit 1
|
||||
|
||||
# wait_mount will trigger a projection, so we need to reset it to empty
|
||||
setfattr -n user.projection.empty -v y source
|
||||
|
||||
test_expect_success 'test concurrent access does not trigger failure' '
|
||||
projfs_run_twice ls target
|
||||
'
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
#!/bin/sh
|
||||
#
|
||||
# Copyright (C) 2019 GitHub, Inc.
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see http://www.gnu.org/licenses/ .
|
||||
|
||||
test_description='projfs argument passthrough test
|
||||
|
||||
Check that arguments are interpeted and passed through correctly.
|
||||
'
|
||||
|
||||
. ./test-lib.sh
|
||||
|
||||
projfs_start test_args source target || exit 1
|
||||
|
||||
test_expect_success 'mount without args does not mark initial' '
|
||||
test_must_fail getfattr -n user.projection.empty source
|
||||
'
|
||||
|
||||
projfs_stop || exit 1
|
||||
|
||||
projfs_start test_args source target -o initial || exit 1
|
||||
|
||||
test_expect_success 'mount with initial arg does mark initial' '
|
||||
getfattr -n user.projection.empty source
|
||||
'
|
||||
|
||||
projfs_stop || exit 1
|
||||
|
||||
test_done
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
/* Linux Projected Filesystem
|
||||
Copyright (C) 2018-2019 GitHub, Inc.
|
||||
|
||||
See the NOTICE file distributed with this library for additional
|
||||
information regarding copyright ownership.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library, in the file COPYING; if not,
|
||||
see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "test_common.h"
|
||||
|
||||
int main(int argc, const char **argv)
|
||||
{
|
||||
struct projfs *fs;
|
||||
if (argc < 3) {
|
||||
fprintf(stderr, "usage: %s [OPTIONS ...] "
|
||||
"<lower-path> <mount-path>\n", argv[0]);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
fs = test_start_mount(argv[argc - 2], argv[argc - 1], NULL, 0, NULL,
|
||||
argc - 3, argv + 1);
|
||||
test_wait_signal();
|
||||
test_stop_mount(fs);
|
||||
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
|
@ -425,12 +425,13 @@ unsigned int test_get_opts(unsigned int opt_flags, ...)
|
|||
|
||||
struct projfs *test_start_mount(const char *lowerdir, const char *mountdir,
|
||||
const struct projfs_handlers *handlers,
|
||||
size_t handlers_size, void *user_data)
|
||||
size_t handlers_size, void *user_data,
|
||||
int argc, const char **argv)
|
||||
{
|
||||
struct projfs *fs;
|
||||
|
||||
fs = projfs_new(lowerdir, mountdir, handlers, handlers_size,
|
||||
user_data);
|
||||
user_data, argc, argv);
|
||||
|
||||
if (fs == NULL)
|
||||
errx(EXIT_FAILURE, "unable to create filesystem");
|
||||
|
|
|
@ -62,7 +62,8 @@ unsigned int test_get_opts(unsigned int opt_flags, ...);
|
|||
|
||||
struct projfs *test_start_mount(const char *lowerdir, const char *mountdir,
|
||||
const struct projfs_handlers *handlers,
|
||||
size_t handlers_size, void *user_data);
|
||||
size_t handlers_size, void *user_data,
|
||||
int argc, const char **argv);
|
||||
|
||||
void *test_stop_mount(struct projfs *fs);
|
||||
|
||||
|
|
|
@ -117,7 +117,7 @@ int main(int argc, char *const argv[])
|
|||
handlers.handle_perm_event = &test_perm_event;
|
||||
|
||||
fs = test_start_mount(lower_path, mount_path,
|
||||
&handlers, sizeof(handlers), NULL);
|
||||
&handlers, sizeof(handlers), NULL, 0, NULL);
|
||||
test_wait_signal();
|
||||
test_stop_mount(fs);
|
||||
|
||||
|
|
|
@ -32,7 +32,7 @@ int main(int argc, char *const argv[])
|
|||
test_parse_mount_opts(argc, argv, TEST_OPT_NONE,
|
||||
&lower_path, &mount_path);
|
||||
|
||||
fs = test_start_mount(lower_path, mount_path, NULL, 0, 0);
|
||||
fs = test_start_mount(lower_path, mount_path, NULL, 0, NULL, 0, NULL);
|
||||
test_wait_signal();
|
||||
test_stop_mount(fs);
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче