Skip to content
This repository was archived by the owner on Jul 8, 2024. It is now read-only.

Commit

Permalink
Merge pull request #39 from github/projection-test-handlers
Browse files Browse the repository at this point in the history
add projection test handlers to test suite
Ashe Connor authored Feb 12, 2019

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
2 parents cd496d1 + 76946dd commit 27772ea
Showing 14 changed files with 198 additions and 47 deletions.
4 changes: 3 additions & 1 deletion t/t200-event-ok.t
Original file line number Diff line number Diff line change
@@ -24,7 +24,8 @@ events are received and handled correctly.
. ./test-lib.sh
. "$TEST_DIRECTORY"/test-lib-event.sh

projfs_start test_projfs_handlers source target || exit 1
projfs_start test_projfs_handlers source target --retval-file retval || exit 1
touch retval

projfs_event_printf notify create_dir d1
test_expect_success 'test event handler on parent directory creation' '
@@ -76,6 +77,7 @@ test_expect_success 'test permission granted on parent directory deletion' '
test_path_is_missing target/d1
'

rm retval
projfs_stop || exit 1

test_expect_success 'check all event notifications' '
4 changes: 3 additions & 1 deletion t/t201-event-err.t
Original file line number Diff line number Diff line change
@@ -24,7 +24,8 @@ events respond to handler errors.
. ./test-lib.sh
. "$TEST_DIRECTORY"/test-lib-event.sh

projfs_start test_projfs_handlers source target --retval ENOMEM || exit 1
projfs_start test_projfs_handlers source target --retval-file retval || exit 1
echo ENOMEM > retval

# TODO: we expect mkdir to create a dir despite the handler error and
# regardless of mkdir's failure exit code
@@ -54,6 +55,7 @@ test_expect_success 'test event handler error on directory deletion' '
test_path_is_dir target/d1
'

rm retval
projfs_stop || exit 1

test_expect_success 'check all event notifications' '
4 changes: 3 additions & 1 deletion t/t202-event-deny.t
Original file line number Diff line number Diff line change
@@ -24,7 +24,8 @@ denial responses from event handlers.
. ./test-lib.sh
. "$TEST_DIRECTORY"/test-lib-event.sh

projfs_start test_projfs_handlers source target --retval deny || exit 1
projfs_start test_projfs_handlers source target --retval-file retval || exit 1
echo deny > retval

projfs_event_printf notify create_dir d1
test_expect_success 'test event handler on directory creation' '
@@ -50,6 +51,7 @@ test_expect_success 'test permission request denied on directory deletion' '
test_path_is_dir target/d1
'

rm retval
projfs_stop || exit 1

test_expect_success 'check all event notifications' '
4 changes: 3 additions & 1 deletion t/t203-event-null.t
Original file line number Diff line number Diff line change
@@ -24,7 +24,8 @@ denial responses caused by event handlers returning null.
. ./test-lib.sh
. "$TEST_DIRECTORY"/test-lib-event.sh

projfs_start test_projfs_handlers source target --retval null || exit 1
projfs_start test_projfs_handlers source target --retval-file retval || exit 1
echo null > retval

projfs_event_printf notify create_dir d1
test_expect_success 'test event handler on directory creation' '
@@ -50,6 +51,7 @@ test_expect_success 'test permission request denied on directory deletion' '
test_path_is_dir target/d1
'

rm retval
projfs_stop || exit 1

test_expect_success 'check all event notifications' '
4 changes: 3 additions & 1 deletion t/t204-event-allow.t
Original file line number Diff line number Diff line change
@@ -24,7 +24,8 @@ explicit allowed responses from event handlers.
. ./test-lib.sh
. "$TEST_DIRECTORY"/test-lib-event.sh

projfs_start test_projfs_handlers source target --retval allow || exit 1
projfs_start test_projfs_handlers source target --retval-file retval || exit 1
echo allow > retval

projfs_event_printf notify create_dir d1
test_expect_success 'test event handler on directory creation' '
@@ -50,6 +51,7 @@ test_expect_success 'test permission request allowed on directory deletion' '
test_path_is_missing target/d1
'

rm retval
projfs_stop || exit 1

test_expect_success 'check all event notifications' '
4 changes: 3 additions & 1 deletion t/t700-vfs-event-ok.t
Original file line number Diff line number Diff line change
@@ -24,7 +24,8 @@ events are received and handled correctly through the VFS API.
. ./test-lib.sh
. "$TEST_DIRECTORY"/test-lib-event.sh

projfs_start test_vfsapi_handlers source target || exit 1
projfs_start test_vfsapi_handlers source target --retval-file retval || exit 1
touch retval

projfs_event_printf vfs create_dir d1
test_expect_success 'test event handler on parent directory creation' '
@@ -76,6 +77,7 @@ test_expect_success 'test permission granted on parent directory deletion' '
test_path_is_missing target/d1
'

rm retval
projfs_stop || exit 1

test_expect_success 'check all event notifications' '
4 changes: 3 additions & 1 deletion t/t701-vfs-event-err.t
Original file line number Diff line number Diff line change
@@ -24,7 +24,8 @@ events respond to handler errors through the VFS API.
. ./test-lib.sh
. "$TEST_DIRECTORY"/test-lib-event.sh

projfs_start test_vfsapi_handlers source target --retval EOutOfMemory || exit 1
projfs_start test_vfsapi_handlers source target --retval-file retval || exit 1
echo EOutOfMemory > retval

# TODO: we expect mkdir to create a dir despite the handler error and
# regardless of mkdir's failure exit code
@@ -54,6 +55,7 @@ test_expect_success 'test event handler error on directory deletion' '
test_path_is_dir target/d1
'

rm retval
projfs_stop || exit 1

test_expect_success 'check all event notifications' '
4 changes: 3 additions & 1 deletion t/t702-vfs-event-deny.t
Original file line number Diff line number Diff line change
@@ -24,7 +24,8 @@ denial responses from event handlers through the VFS API.
. ./test-lib.sh
. "$TEST_DIRECTORY"/test-lib-event.sh

projfs_start test_vfsapi_handlers source target --retval deny || exit 1
projfs_start test_vfsapi_handlers source target --retval-file retval || exit 1
echo deny > retval

# TODO: test_vfsapi_handlers always returns EPERM with --retval deny, unlike
# test_projfs_handlers, so mkdir gets a handler error; like t701.1,
@@ -58,6 +59,7 @@ test_expect_success 'test permission request denied on directory deletion' '
test_path_is_dir target/d1
'

rm retval
projfs_stop || exit 1

test_expect_success 'check all event notifications' '
4 changes: 3 additions & 1 deletion t/t703-vfs-event-null.t
Original file line number Diff line number Diff line change
@@ -25,7 +25,8 @@ the VFS API.
. ./test-lib.sh
. "$TEST_DIRECTORY"/test-lib-event.sh

projfs_start test_vfsapi_handlers source target --retval null || exit 1
projfs_start test_vfsapi_handlers source target --retval-file retval || exit 1
echo null > retval

# TODO: test_vfsapi_handlers always returns EINVAL with --retval null, unlike
# test_projfs_handlers, so mkdir sees a handler error; like t701.1,
@@ -59,6 +60,7 @@ test_expect_success 'test permission request denied on directory deletion' '
test_path_is_dir target/d1
'

rm retval
projfs_stop || exit 1

test_expect_success 'check all event notifications' '
4 changes: 3 additions & 1 deletion t/t704-vfs-event-allow.t
Original file line number Diff line number Diff line change
@@ -24,7 +24,8 @@ explicit allowed responses from event handlers through the VFS API.
. ./test-lib.sh
. "$TEST_DIRECTORY"/test-lib-event.sh

projfs_start test_vfsapi_handlers source target --retval allow || exit 1
projfs_start test_vfsapi_handlers source target --retval-file retval || exit 1
echo allow > retval

projfs_event_printf vfs create_dir d1
test_expect_success 'test event handler on directory creation' '
@@ -50,6 +51,7 @@ test_expect_success 'test permission request allowed on directory deletion' '
test_path_is_missing target/d1
'

rm retval
projfs_stop || exit 1

test_expect_success 'check all event notifications' '
84 changes: 73 additions & 11 deletions t/test_common.c
Original file line number Diff line number Diff line change
@@ -102,7 +102,7 @@ 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},
{ }, // TODO: retval-file
{ "retval-file", required_argument, NULL, TEST_OPT_NUM_RETFILE},
{ "timeout", required_argument, NULL, TEST_OPT_NUM_TIMEOUT}
};

@@ -114,12 +114,13 @@ struct opt_usage {
static const struct opt_usage all_opts_usage[] = {
{ NULL, 1 },
{ "allow|deny|null|<error>", 1 },
{ }, // TODO: <retval-file>
{ "<retval-file>", 1 },
{ "<max-seconds>", 1 }
};

/* option values */
static int optval_retval;
static const char *optval_retfile;
static long int optval_timeout;

static unsigned int opt_set_flags = TEST_OPT_NONE;
@@ -200,7 +201,7 @@ long int test_parse_long(const char *arg, int base)
return val;
}

int test_parse_retsym(int vfsapi, const char *retsym, int *retvalp)
int test_parse_retsym(int vfsapi, const char *retsym, int *retval)
{
const struct retval *retvals = get_retvals(vfsapi);
int ret = -1;
@@ -214,7 +215,7 @@ int test_parse_retsym(int vfsapi, const char *retsym, int *retvalp)
!strncmp(name, VFSAPI_PREFIX, VFSAPI_PREFIX_LEN) &&
!strcasecmp(name + VFSAPI_PREFIX_LEN, retsym))) {
ret = 0;
*retvalp = retvals[i].val;
*retval = retvals[i].val;
break;
}

@@ -224,6 +225,46 @@ int test_parse_retsym(int vfsapi, const char *retsym, int *retvalp)
return ret;
}

static void read_retfile(int vfsapi, int *retval, unsigned int *flags)
{
FILE *file;
char retsym[MAX_RETVAL_NAME_LEN + 1];

file = fopen(optval_retfile, "r");
if (file == NULL) {
if (errno != ENOENT)
warn("unable to open retval file: %s",
optval_retfile);
goto out;
}

errno = 0;
if (fgets(retsym, sizeof(retsym), file) != NULL) {
char *c;

c = strchr(retsym, '\n');
if (c != NULL)
*c = '\0';

if (test_parse_retsym(vfsapi, retsym, retval) < 0) {
warnx("invalid symbol in retval file: %s: %s",
optval_retfile, retsym);
}

*flags = TEST_VAL_SET | TEST_FILE_EXIST | TEST_FILE_VALID;
}
else if (errno > 0)
warn("unable to read retval file: %s", optval_retfile);
else
*flags = TEST_FILE_EXIST;

if (fclose(file) != 0)
warn("unable to close retval file: %s", optval_retfile);

out:
return;
}

static struct option *get_long_opts(unsigned int opt_flags)
{
struct option *long_opts;
@@ -290,10 +331,15 @@ void test_parse_opts(int argc, char *const argv[], unsigned int opt_flags,
case TEST_OPT_NUM_RETVAL:
if (test_parse_retsym(vfsapi, optarg,
&optval_retval) < 0)
test_exit_error(argv[0], "invalid retval: %s",
test_exit_error(argv[0],
"invalid retval symbol: %s",
optarg);
else
opt_set_flags |= TEST_OPT_RETVAL;
opt_set_flags |= TEST_OPT_RETVAL;
break;

case TEST_OPT_NUM_RETFILE:
optval_retfile = optarg;
opt_set_flags |= TEST_OPT_RETFILE;
break;

case TEST_OPT_NUM_TIMEOUT:
@@ -310,9 +356,8 @@ void test_parse_opts(int argc, char *const argv[], unsigned int opt_flags,
test_exit_error(argv[0],
"invalid option: -%c",
optopt);
else
test_exit_error(argv[0], "invalid option: %s",
argv[optind - 1]);
test_exit_error(argv[0], "invalid option: %s",
argv[optind - 1]);

default:
test_exit_error(argv[0], "unknown getopt code: %d",
@@ -346,6 +391,7 @@ void test_parse_mount_opts(int argc, char *const argv[],

unsigned int test_get_opts(unsigned int opt_flags, ...)
{
int vfsapi = (opt_flags & TEST_OPT_VFSAPI) ? 1 : 0;
unsigned int opt_flag = TEST_OPT_HELP;
unsigned int ret_flags = TEST_OPT_NONE;
va_list ap;
@@ -356,8 +402,10 @@ unsigned int test_get_opts(unsigned int opt_flags, ...)

while (opt_flags != TEST_OPT_NONE) {
unsigned int ret_flag;
unsigned int *f;
int *i;
long int *l;
const char **s;

opt_flag <<= 1;
if ((opt_flags & opt_flag) == TEST_OPT_NONE)
@@ -370,8 +418,22 @@ unsigned int test_get_opts(unsigned int opt_flags, ...)
switch (opt_flag) {
case TEST_OPT_RETVAL:
i = va_arg(ap, int*);
if (ret_flag != TEST_OPT_NONE)
f = va_arg(ap, unsigned int*);
*f = TEST_VAL_UNSET | TEST_FILE_NONE;
if (ret_flag != TEST_OPT_NONE) {
*i = optval_retval;
*f |= TEST_VAL_SET;
} else if ((opt_set_flags & TEST_OPT_RETFILE)
!= TEST_OPT_NONE) {
read_retfile(vfsapi, i, f);
ret_flags |= opt_flag;
}
break;

case TEST_OPT_RETFILE:
s = va_arg(ap, const char**);
if (ret_flag != TEST_OPT_NONE)
*s = optval_retfile;
break;

case TEST_OPT_TIMEOUT:
10 changes: 9 additions & 1 deletion t/test_common.h
Original file line number Diff line number Diff line change
@@ -28,16 +28,24 @@

#define TEST_OPT_NUM_HELP 0
#define TEST_OPT_NUM_RETVAL 1
// TODO: TEST_OPT_NUM_RETFILE
#define TEST_OPT_NUM_RETFILE 2
#define TEST_OPT_NUM_TIMEOUT 3

#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_NONE 0x0000
#define TEST_OPT_VFSAPI 0x8000 // not a command-line option

#define TEST_VAL_UNSET 0x0000
#define TEST_VAL_SET 0x0001

#define TEST_FILE_NONE 0x0000
#define TEST_FILE_EXIST 0x0002
#define TEST_FILE_VALID 0x0004

void test_exit_error(const char *argv0, const char *fmt, ...);

long int test_parse_long(const char *arg, int base);
47 changes: 34 additions & 13 deletions t/test_projfs_handlers.c
Original file line number Diff line number Diff line change
@@ -19,41 +19,60 @@
see <http://www.gnu.org/licenses/>.
*/

#include <errno.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>

#include "test_common.h"

static int test_handle_event(struct projfs_event *event, const char *desc,
int perm)
int proj, int perm)
{
unsigned int opt_flags;
unsigned int opt_flags, ret_flags;
const char *retfile;
int ret;

printf(" test %s for %s: "
"0x%04" PRIx64 "-%08" PRIx64 ", %d\n",
desc, event->path,
event->mask >> 32, event->mask & 0xFFFFFFFF, event->pid);

opt_flags = test_get_opts(TEST_OPT_RETVAL, &ret);

if (opt_flags == TEST_OPT_NONE)
opt_flags = test_get_opts((TEST_OPT_RETVAL | TEST_OPT_RETFILE),
&ret, &ret_flags, &retfile);

if ((opt_flags & TEST_OPT_RETFILE) == TEST_OPT_NONE ||
(ret_flags & TEST_FILE_EXIST) != TEST_FILE_NONE) {
printf(" test %s for %s: "
"0x%04" PRIx64 "-%08" PRIx64 ", %d\n",
desc, event->path,
event->mask >> 32, event->mask & 0xFFFFFFFF,
event->pid);
}

if (proj) {
if ((event->mask & ~PROJFS_ONDIR) != PROJFS_CREATE_SELF) {
fprintf(stderr, "unknown projection flags\n");
ret = -EINVAL;
}
}

if ((ret_flags & TEST_VAL_SET) == TEST_VAL_UNSET)
ret = perm ? PROJFS_ALLOW : 0;
else if(!perm && ret > 0)
ret = 0;

return ret;
}

static int test_proj_event(struct projfs_event *event)
{
return test_handle_event(event, "projection request", 1, 0);
}

static int test_notify_event(struct projfs_event *event)
{
return test_handle_event(event, "event notification", 0);
return test_handle_event(event, "event notification", 0, 0);
}

static int test_perm_event(struct projfs_event *event)
{
return test_handle_event(event, "permission request", 1);
return test_handle_event(event, "permission request", 0, 1);
}

int main(int argc, char *const argv[])
@@ -62,9 +81,11 @@ int main(int argc, char *const argv[])
struct projfs *fs;
struct projfs_handlers handlers = { 0 };

test_parse_mount_opts(argc, argv, TEST_OPT_RETVAL,
test_parse_mount_opts(argc, argv,
(TEST_OPT_RETVAL | TEST_OPT_RETFILE),
&lower_path, &mount_path);

handlers.handle_proj_event = &test_proj_event;
handlers.handle_notify_event = &test_notify_event;
handlers.handle_perm_event = &test_perm_event;

64 changes: 52 additions & 12 deletions t/test_vfsapi_handlers.c
Original file line number Diff line number Diff line change
@@ -27,6 +27,51 @@

#include "test_common.h"

static int test_handle_event(const char *desc, const char *path,
int pid, const char *procname,
int isdir, int type, int proj)
{
unsigned int opt_flags, ret_flags;
const char *retfile;
int ret;

opt_flags = test_get_opts((TEST_OPT_VFSAPI | TEST_OPT_RETVAL
| TEST_OPT_RETFILE),
&ret, &ret_flags, &retfile);

if ((opt_flags & TEST_OPT_RETFILE) == TEST_OPT_NONE ||
(ret_flags & TEST_FILE_EXIST) != TEST_FILE_NONE) {
printf(" Test%s for %s: %d, %s, %hhd, 0x%08X\n",
desc, path, pid, procname, isdir, type);
}

if (proj) {
if (!isdir) {
fprintf(stderr, "unsupported file projection\n");
ret = PrjFS_Result_Invalid;
}
}

if ((ret_flags & TEST_VAL_SET) == TEST_VAL_UNSET)
ret = PrjFS_Result_Success;

return ret;
}

static PrjFS_Result TestEnumerateDirectory(
_In_ unsigned long commandId,
_In_ const char* relativePath,
_In_ int triggeringProcessId,
_In_ const char* triggeringProcessName
)
{
(void)commandId; // prevent compiler warnings

return test_handle_event("EnumerateDirectory", relativePath,
triggeringProcessId, triggeringProcessName,
1, 0, 1);
}

static PrjFS_Result TestNotifyOperation(
_In_ unsigned long commandId,
_In_ const char* relativePath,
@@ -39,22 +84,14 @@ static PrjFS_Result TestNotifyOperation(
_In_ const char* destinationRelativePath
)
{
unsigned int opt_flags;
int retval;

printf(" TestNotifyOperation for %s: %d, %s, %hhd, 0x%08X\n",
relativePath, triggeringProcessId, triggeringProcessName,
isDirectory, notificationType);

(void)commandId; // prevent compiler warnings
(void)providerId;
(void)contentId;
(void)destinationRelativePath;

opt_flags = test_get_opts(TEST_OPT_RETVAL | TEST_OPT_VFSAPI, &retval);

return (opt_flags == TEST_OPT_NONE) ? PrjFS_Result_Success
: retval;
return test_handle_event("NotifyOperation", relativePath,
triggeringProcessId, triggeringProcessName,
isDirectory, notificationType, 0);
}

int main(int argc, char *const argv[])
@@ -63,10 +100,13 @@ int main(int argc, char *const argv[])
PrjFS_MountHandle *handle;
PrjFS_Callbacks callbacks = { 0 };

test_parse_mount_opts(argc, argv, (TEST_OPT_VFSAPI | TEST_OPT_RETVAL),
test_parse_mount_opts(argc, argv,
(TEST_OPT_VFSAPI | TEST_OPT_RETVAL
| TEST_OPT_RETFILE),
&lower_path, &mount_path);

memset(&callbacks, 0, sizeof(PrjFS_Callbacks));
callbacks.EnumerateDirectory = TestEnumerateDirectory;
callbacks.NotifyOperation = TestNotifyOperation;

test_start_vfsapi_mount(lower_path, mount_path, callbacks,

0 comments on commit 27772ea

Please sign in to comment.