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:
Ashe Connor 2019-05-20 16:17:53 +10:00
Родитель f10aaaa41e
Коммит 566f250dfa
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 997E657B1C52B6C5
12 изменённых файлов: 160 добавлений и 103 удалений

Просмотреть файл

@ -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.

Просмотреть файл

@ -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
'

42
t/t300-args-initial.t Executable file
Просмотреть файл

@ -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

43
t/test_args.c Normal file
Просмотреть файл

@ -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);