зеркало из https://github.com/github/libprojfs.git
test that we actually lock dirs on projection
This commit is contained in:
Родитель
98498eec62
Коммит
ffff2f2f46
|
@ -58,7 +58,8 @@ TESTS = t000-mirror-read.t \
|
||||||
t201-event-err.t \
|
t201-event-err.t \
|
||||||
t202-event-deny.t \
|
t202-event-deny.t \
|
||||||
t203-event-null.t \
|
t203-event-null.t \
|
||||||
t204-event-allow.t
|
t204-event-allow.t \
|
||||||
|
t205-event-locking.t
|
||||||
|
|
||||||
if ENABLE_VFSAPI
|
if ENABLE_VFSAPI
|
||||||
TESTS += t500-vfs-mirror-basic.t \
|
TESTS += t500-vfs-mirror-basic.t \
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program. If not, see http://www.gnu.org/licenses/ .
|
# along with this program. If not, see http://www.gnu.org/licenses/ .
|
||||||
|
|
||||||
test_description='projfs file operation permission denial tests
|
test_description='projfs file operation permission null-denial tests
|
||||||
|
|
||||||
Check that projfs file operation permission requests respond to
|
Check that projfs file operation permission requests respond to
|
||||||
denial responses caused by event handlers returning null.
|
denial responses caused by event handlers returning null.
|
||||||
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
#!/bin/sh
|
||||||
|
#
|
||||||
|
# Copyright (C) 2018-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 file operation locking tests
|
||||||
|
|
||||||
|
Check that projfs file operation notification events are issued serially for a
|
||||||
|
given path.
|
||||||
|
'
|
||||||
|
|
||||||
|
. ./test-lib.sh
|
||||||
|
. "$TEST_DIRECTORY"/test-lib-event.sh
|
||||||
|
|
||||||
|
rm lock >/dev/null 2>/dev/null
|
||||||
|
projfs_start test_projfs_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 0x01 source
|
||||||
|
|
||||||
|
test_expect_success 'test concurrent access does not trigger failure' '
|
||||||
|
projfs_run_twice ls target
|
||||||
|
'
|
||||||
|
|
||||||
|
projfs_stop || exit 1
|
||||||
|
|
||||||
|
test_expect_success 'check no event error messages' '
|
||||||
|
test_must_be_empty test_projfs_handlers.err
|
||||||
|
'
|
||||||
|
|
||||||
|
test_done
|
||||||
|
|
|
@ -754,6 +754,25 @@ projfs_start () {
|
||||||
trap projfs_stop EXIT
|
trap projfs_stop EXIT
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Run the given command twice in parallel, wait for both to complete, and
|
||||||
|
# return with 1 if at least one of the executions fails.
|
||||||
|
projfs_run_twice () {
|
||||||
|
"$@" &
|
||||||
|
pidA="$!"
|
||||||
|
"$@" &
|
||||||
|
pidB="$!"
|
||||||
|
|
||||||
|
ret=0
|
||||||
|
if ! wait $pidA; then
|
||||||
|
ret=1
|
||||||
|
fi
|
||||||
|
if ! wait $pidB; then
|
||||||
|
ret=1
|
||||||
|
fi
|
||||||
|
|
||||||
|
return $ret
|
||||||
|
}
|
||||||
|
|
||||||
# Stop the projected filesystem command that was started by projfs_start()
|
# Stop the projected filesystem command that was started by projfs_start()
|
||||||
# and wait for its umount operation to be completed.
|
# and wait for its umount operation to be completed.
|
||||||
projfs_stop () {
|
projfs_stop () {
|
||||||
|
|
|
@ -101,9 +101,10 @@ static const struct retval vfsapi_retvals[] = {
|
||||||
|
|
||||||
static const struct option all_long_opts[] = {
|
static const struct option all_long_opts[] = {
|
||||||
{ "help", no_argument, NULL, TEST_OPT_NUM_HELP },
|
{ "help", no_argument, NULL, TEST_OPT_NUM_HELP },
|
||||||
{ "retval", required_argument, NULL, TEST_OPT_NUM_RETVAL},
|
{ "retval", required_argument, NULL, TEST_OPT_NUM_RETVAL },
|
||||||
{ "retval-file", required_argument, NULL, TEST_OPT_NUM_RETFILE},
|
{ "retval-file", required_argument, NULL, TEST_OPT_NUM_RETFILE },
|
||||||
{ "timeout", required_argument, NULL, TEST_OPT_NUM_TIMEOUT}
|
{ "timeout", required_argument, NULL, TEST_OPT_NUM_TIMEOUT },
|
||||||
|
{ "lock-file", required_argument, NULL, TEST_OPT_NUM_LOCKFILE },
|
||||||
};
|
};
|
||||||
|
|
||||||
struct opt_usage {
|
struct opt_usage {
|
||||||
|
@ -115,13 +116,15 @@ static const struct opt_usage all_opts_usage[] = {
|
||||||
{ NULL, 1 },
|
{ NULL, 1 },
|
||||||
{ "allow|deny|null|<error>", 1 },
|
{ "allow|deny|null|<error>", 1 },
|
||||||
{ "<retval-file>", 1 },
|
{ "<retval-file>", 1 },
|
||||||
{ "<max-seconds>", 1 }
|
{ "<max-seconds>", 1 },
|
||||||
|
{ "<lock-file>", 1 },
|
||||||
};
|
};
|
||||||
|
|
||||||
/* option values */
|
/* option values */
|
||||||
static int optval_retval;
|
static int optval_retval;
|
||||||
static const char *optval_retfile;
|
static const char *optval_retfile;
|
||||||
static long int optval_timeout;
|
static long int optval_timeout;
|
||||||
|
static const char *optval_lockfile;
|
||||||
|
|
||||||
static unsigned int opt_set_flags = TEST_OPT_NONE;
|
static unsigned int opt_set_flags = TEST_OPT_NONE;
|
||||||
|
|
||||||
|
@ -351,6 +354,11 @@ void test_parse_opts(int argc, char *const argv[], unsigned int opt_flags,
|
||||||
opt_set_flags |= TEST_OPT_TIMEOUT;
|
opt_set_flags |= TEST_OPT_TIMEOUT;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case TEST_OPT_NUM_LOCKFILE:
|
||||||
|
optval_lockfile = optarg;
|
||||||
|
opt_set_flags |= TEST_OPT_LOCKFILE;
|
||||||
|
break;
|
||||||
|
|
||||||
case '?':
|
case '?':
|
||||||
if (optopt > 0)
|
if (optopt > 0)
|
||||||
test_exit_error(argv[0],
|
test_exit_error(argv[0],
|
||||||
|
@ -442,6 +450,12 @@ unsigned int test_get_opts(unsigned int opt_flags, ...)
|
||||||
*l = optval_timeout;
|
*l = optval_timeout;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case TEST_OPT_LOCKFILE:
|
||||||
|
s = va_arg(ap, const char**);
|
||||||
|
if (ret_flag != TEST_OPT_NONE)
|
||||||
|
*s = optval_lockfile;
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
errx(EXIT_FAILURE,
|
errx(EXIT_FAILURE,
|
||||||
"unknown option flag: %u", opt_flag);
|
"unknown option flag: %u", opt_flag);
|
||||||
|
|
|
@ -30,11 +30,13 @@
|
||||||
#define TEST_OPT_NUM_RETVAL 1
|
#define TEST_OPT_NUM_RETVAL 1
|
||||||
#define TEST_OPT_NUM_RETFILE 2
|
#define TEST_OPT_NUM_RETFILE 2
|
||||||
#define TEST_OPT_NUM_TIMEOUT 3
|
#define TEST_OPT_NUM_TIMEOUT 3
|
||||||
|
#define TEST_OPT_NUM_LOCKFILE 4
|
||||||
|
|
||||||
#define TEST_OPT_HELP (0x0001 << TEST_OPT_NUM_HELP)
|
#define TEST_OPT_HELP (0x0001 << TEST_OPT_NUM_HELP)
|
||||||
#define TEST_OPT_RETVAL (0x0001 << TEST_OPT_NUM_RETVAL)
|
#define TEST_OPT_RETVAL (0x0001 << TEST_OPT_NUM_RETVAL)
|
||||||
#define TEST_OPT_RETFILE (0x0001 << TEST_OPT_NUM_RETFILE)
|
#define TEST_OPT_RETFILE (0x0001 << TEST_OPT_NUM_RETFILE)
|
||||||
#define TEST_OPT_TIMEOUT (0x0001 << TEST_OPT_NUM_TIMEOUT)
|
#define TEST_OPT_TIMEOUT (0x0001 << TEST_OPT_NUM_TIMEOUT)
|
||||||
|
#define TEST_OPT_LOCKFILE (0x0001 << TEST_OPT_NUM_LOCKFILE)
|
||||||
|
|
||||||
#define TEST_OPT_NONE 0x0000
|
#define TEST_OPT_NONE 0x0000
|
||||||
#define TEST_OPT_VFSAPI 0x8000 // not a command-line option
|
#define TEST_OPT_VFSAPI 0x8000 // not a command-line option
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
#include "test_common.h"
|
#include "test_common.h"
|
||||||
|
|
||||||
|
@ -30,11 +31,13 @@ static int test_handle_event(struct projfs_event *event, const char *desc,
|
||||||
int proj, int perm)
|
int proj, int perm)
|
||||||
{
|
{
|
||||||
unsigned int opt_flags, ret_flags;
|
unsigned int opt_flags, ret_flags;
|
||||||
const char *retfile;
|
const char *retfile, *lockfile = NULL;
|
||||||
int ret;
|
int ret, timeout = 0, fd = 0, res;
|
||||||
|
|
||||||
opt_flags = test_get_opts((TEST_OPT_RETVAL | TEST_OPT_RETFILE),
|
opt_flags = test_get_opts((TEST_OPT_RETVAL | TEST_OPT_RETFILE |
|
||||||
&ret, &ret_flags, &retfile);
|
TEST_OPT_TIMEOUT | TEST_OPT_LOCKFILE),
|
||||||
|
&ret, &ret_flags, &retfile, &timeout,
|
||||||
|
&lockfile);
|
||||||
|
|
||||||
if ((opt_flags & TEST_OPT_RETFILE) == TEST_OPT_NONE ||
|
if ((opt_flags & TEST_OPT_RETFILE) == TEST_OPT_NONE ||
|
||||||
(ret_flags & TEST_FILE_EXIST) != TEST_FILE_NONE) {
|
(ret_flags & TEST_FILE_EXIST) != TEST_FILE_NONE) {
|
||||||
|
@ -57,9 +60,27 @@ static int test_handle_event(struct projfs_event *event, const char *desc,
|
||||||
// TODO: hydrate file/dir based on projection list
|
// TODO: hydrate file/dir based on projection list
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (lockfile) {
|
||||||
|
fd = open(lockfile, (O_CREAT | O_EXCL | O_RDWR), 0600);
|
||||||
|
if (fd == -1 && errno == EEXIST)
|
||||||
|
return -EEXIST;
|
||||||
|
else if (fd == -1)
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (timeout)
|
||||||
|
sleep(timeout);
|
||||||
|
|
||||||
|
if (lockfile) {
|
||||||
|
close(fd);
|
||||||
|
res = unlink(lockfile);
|
||||||
|
if (res == -1)
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
if ((ret_flags & TEST_VAL_SET) == TEST_VAL_UNSET)
|
if ((ret_flags & TEST_VAL_SET) == TEST_VAL_UNSET)
|
||||||
ret = perm ? PROJFS_ALLOW : 0;
|
ret = perm ? PROJFS_ALLOW : 0;
|
||||||
else if(!perm && ret > 0)
|
else if (!perm && ret > 0)
|
||||||
ret = 0;
|
ret = 0;
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -87,7 +108,8 @@ int main(int argc, char *const argv[])
|
||||||
struct projfs_handlers handlers = { 0 };
|
struct projfs_handlers handlers = { 0 };
|
||||||
|
|
||||||
test_parse_mount_opts(argc, argv,
|
test_parse_mount_opts(argc, argv,
|
||||||
(TEST_OPT_RETVAL | TEST_OPT_RETFILE),
|
(TEST_OPT_RETVAL | TEST_OPT_RETFILE |
|
||||||
|
TEST_OPT_TIMEOUT | TEST_OPT_LOCKFILE),
|
||||||
&lower_path, &mount_path);
|
&lower_path, &mount_path);
|
||||||
|
|
||||||
handlers.handle_proj_event = &test_proj_event;
|
handlers.handle_proj_event = &test_proj_event;
|
||||||
|
|
Загрузка…
Ссылка в новой задаче