зеркало из 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 \
|
||||
t202-event-deny.t \
|
||||
t203-event-null.t \
|
||||
t204-event-allow.t
|
||||
t204-event-allow.t \
|
||||
t205-event-locking.t
|
||||
|
||||
if ENABLE_VFSAPI
|
||||
TESTS += t500-vfs-mirror-basic.t \
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
# 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 permission denial tests
|
||||
test_description='projfs file operation permission null-denial tests
|
||||
|
||||
Check that projfs file operation permission requests respond to
|
||||
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
|
||||
}
|
||||
|
||||
# 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()
|
||||
# and wait for its umount operation to be completed.
|
||||
projfs_stop () {
|
||||
|
|
|
@ -101,9 +101,10 @@ static const struct retval vfsapi_retvals[] = {
|
|||
|
||||
static const struct option all_long_opts[] = {
|
||||
{ "help", no_argument, NULL, TEST_OPT_NUM_HELP },
|
||||
{ "retval", required_argument, NULL, TEST_OPT_NUM_RETVAL},
|
||||
{ "retval-file", required_argument, NULL, TEST_OPT_NUM_RETFILE},
|
||||
{ "timeout", required_argument, NULL, TEST_OPT_NUM_TIMEOUT}
|
||||
{ "retval", required_argument, NULL, TEST_OPT_NUM_RETVAL },
|
||||
{ "retval-file", required_argument, NULL, TEST_OPT_NUM_RETFILE },
|
||||
{ "timeout", required_argument, NULL, TEST_OPT_NUM_TIMEOUT },
|
||||
{ "lock-file", required_argument, NULL, TEST_OPT_NUM_LOCKFILE },
|
||||
};
|
||||
|
||||
struct opt_usage {
|
||||
|
@ -115,13 +116,15 @@ static const struct opt_usage all_opts_usage[] = {
|
|||
{ NULL, 1 },
|
||||
{ "allow|deny|null|<error>", 1 },
|
||||
{ "<retval-file>", 1 },
|
||||
{ "<max-seconds>", 1 }
|
||||
{ "<max-seconds>", 1 },
|
||||
{ "<lock-file>", 1 },
|
||||
};
|
||||
|
||||
/* option values */
|
||||
static int optval_retval;
|
||||
static const char *optval_retfile;
|
||||
static long int optval_timeout;
|
||||
static const char *optval_lockfile;
|
||||
|
||||
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;
|
||||
break;
|
||||
|
||||
case TEST_OPT_NUM_LOCKFILE:
|
||||
optval_lockfile = optarg;
|
||||
opt_set_flags |= TEST_OPT_LOCKFILE;
|
||||
break;
|
||||
|
||||
case '?':
|
||||
if (optopt > 0)
|
||||
test_exit_error(argv[0],
|
||||
|
@ -442,6 +450,12 @@ unsigned int test_get_opts(unsigned int opt_flags, ...)
|
|||
*l = optval_timeout;
|
||||
break;
|
||||
|
||||
case TEST_OPT_LOCKFILE:
|
||||
s = va_arg(ap, const char**);
|
||||
if (ret_flag != TEST_OPT_NONE)
|
||||
*s = optval_lockfile;
|
||||
break;
|
||||
|
||||
default:
|
||||
errx(EXIT_FAILURE,
|
||||
"unknown option flag: %u", opt_flag);
|
||||
|
|
|
@ -30,11 +30,13 @@
|
|||
#define TEST_OPT_NUM_RETVAL 1
|
||||
#define TEST_OPT_NUM_RETFILE 2
|
||||
#define TEST_OPT_NUM_TIMEOUT 3
|
||||
#define TEST_OPT_NUM_LOCKFILE 4
|
||||
|
||||
#define TEST_OPT_HELP (0x0001 << TEST_OPT_NUM_HELP)
|
||||
#define TEST_OPT_RETVAL (0x0001 << TEST_OPT_NUM_RETVAL)
|
||||
#define TEST_OPT_RETFILE (0x0001 << TEST_OPT_NUM_RETFILE)
|
||||
#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_VFSAPI 0x8000 // not a command-line option
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include <inttypes.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.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)
|
||||
{
|
||||
unsigned int opt_flags, ret_flags;
|
||||
const char *retfile;
|
||||
int ret;
|
||||
const char *retfile, *lockfile = NULL;
|
||||
int ret, timeout = 0, fd = 0, res;
|
||||
|
||||
opt_flags = test_get_opts((TEST_OPT_RETVAL | TEST_OPT_RETFILE),
|
||||
&ret, &ret_flags, &retfile);
|
||||
opt_flags = test_get_opts((TEST_OPT_RETVAL | TEST_OPT_RETFILE |
|
||||
TEST_OPT_TIMEOUT | TEST_OPT_LOCKFILE),
|
||||
&ret, &ret_flags, &retfile, &timeout,
|
||||
&lockfile);
|
||||
|
||||
if ((opt_flags & TEST_OPT_RETFILE) == TEST_OPT_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
|
||||
}
|
||||
|
||||
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)
|
||||
ret = perm ? PROJFS_ALLOW : 0;
|
||||
else if(!perm && ret > 0)
|
||||
else if (!perm && ret > 0)
|
||||
ret = 0;
|
||||
|
||||
return ret;
|
||||
|
@ -87,7 +108,8 @@ int main(int argc, char *const argv[])
|
|||
struct projfs_handlers handlers = { 0 };
|
||||
|
||||
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);
|
||||
|
||||
handlers.handle_proj_event = &test_proj_event;
|
||||
|
|
Загрузка…
Ссылка в новой задаче