test that we actually lock dirs on projection

This commit is contained in:
Ashe Connor 2019-02-19 17:07:25 +11:00
Родитель 98498eec62
Коммит ffff2f2f46
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 997E657B1C52B6C5
7 изменённых файлов: 114 добавлений и 12 удалений

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

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

44
t/t205-event-locking.t Executable file
Просмотреть файл

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

19
t/test-lib-functions.sh поставляемый
Просмотреть файл

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