Skip to content
Snippets Groups Projects
Commit d4a5b773 authored by Simon McVittie's avatar Simon McVittie
Browse files

tests: Test pv_resolve_in_sysroot()

parent b6f8ae11
No related branches found
No related tags found
No related merge requests found
......@@ -43,6 +43,7 @@ tests = [
compiled_tests = [
'bwrap-lock',
'resolve-in-sysroot',
'utils',
]
......
/*
* Copyright © 2019-2020 Collabora Ltd.
*
* SPDX-License-Identifier: MIT
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <stdlib.h>
#include <unistd.h>
#include <glib.h>
#include <glib/gstdio.h>
#include "glib-backports.h"
#include "libglnx/libglnx.h"
#include "resolve-in-sysroot.h"
#include "test-utils.h"
#include "utils.h"
typedef struct
{
TestsOpenFdSet old_fds;
} Fixture;
static void
setup (Fixture *f,
gconstpointer context)
{
f->old_fds = tests_check_fd_leaks_enter ();
}
static void
teardown (Fixture *f,
gconstpointer context)
{
tests_check_fd_leaks_leave (f->old_fds);
}
static gboolean
fd_same_as_rel_path_nofollow (int fd,
int dfd,
const gchar *path)
{
GStatBuf fd_buffer, path_buffer;
return (fstat (fd, &fd_buffer) == 0
&& fstatat (dfd, path, &path_buffer, AT_SYMLINK_NOFOLLOW) == 0
&& fd_buffer.st_dev == path_buffer.st_dev
&& fd_buffer.st_ino == path_buffer.st_ino);
}
typedef struct
{
const char *name;
const char *target;
} Symlink;
typedef enum
{
RESOLVE_ERROR_DOMAIN_NONE,
RESOLVE_ERROR_DOMAIN_GIO,
} ResolveErrorDomain;
typedef enum
{
RESOLVE_CALL_FLAGS_IGNORE_PATH = (1 << 0),
RESOLVE_CALL_FLAGS_NONE = 0
} ResolveCallFlags;
typedef struct
{
struct
{
const char *path;
PvResolveFlags flags;
ResolveCallFlags test_flags;
} call;
struct
{
const char *path;
int code;
} expect;
} ResolveTest;
static void
test_resolve_in_sysroot (Fixture *f,
gconstpointer context)
{
static const char * const prepare_dirs[] =
{
"a/b/c/d/e",
"a/b2/c2/d2/e2",
};
static const Symlink prepare_symlinks[] =
{
{ "a/b/symlink_to_c", "c" },
{ "a/b/symlink_to_b2", "../b2" },
{ "a/b/symlink_to_c2", "../../a/b2/c2" },
{ "a/b/abs_symlink_to_run", "/run" },
{ "a/b/long_symlink_to_dev", "../../../../../../../../../../../dev" },
{ "x", "create_me" },
};
static const ResolveTest tests[] =
{
{ { "a/b/c/d" }, { "a/b/c/d" } },
{
{ "a/b/c/d", PV_RESOLVE_FLAGS_NONE, RESOLVE_CALL_FLAGS_IGNORE_PATH },
{ "a/b/c/d" },
},
{ { "a/b/c/d", PV_RESOLVE_FLAGS_MKDIR_P }, { "a/b/c/d" } },
{
{ "a/b/c/d", PV_RESOLVE_FLAGS_MKDIR_P, RESOLVE_CALL_FLAGS_IGNORE_PATH },
{ "a/b/c/d" },
},
{ { "create_me" }, { NULL, G_IO_ERROR_NOT_FOUND } },
{
{ "create_me", PV_RESOLVE_FLAGS_NONE, RESOLVE_CALL_FLAGS_IGNORE_PATH },
{ NULL, G_IO_ERROR_NOT_FOUND }
},
{ { "a/b/c/d", PV_RESOLVE_FLAGS_MKDIR_P }, { "a/b/c/d" } },
{ { "a/b///////.////./././///././c/d" }, { "a/b/c/d" } },
{ { "/a/b///////.////././../b2////././c2/d2" }, { "a/b2/c2/d2" } },
{ { "a/b/c/d/e/f" }, { NULL, G_IO_ERROR_NOT_FOUND } },
{ { "a/b/c/d/e/f", PV_RESOLVE_FLAGS_MKDIR_P }, { "a/b/c/d/e/f" } },
{ { "a/b/c/d/e/f" }, { "a/b/c/d/e/f" } },
{ { "a/b/c/d/e/f", PV_RESOLVE_FLAGS_MKDIR_P }, { "a/b/c/d/e/f" } },
{ { "a3/b3/c3" }, { NULL, G_IO_ERROR_NOT_FOUND } },
{ { "a3/b3/c3", PV_RESOLVE_FLAGS_MKDIR_P }, { "a3/b3/c3" } },
{ { "a/b/symlink_to_c" }, { "a/b/c" } },
{ { "a/b/symlink_to_c/d" }, { "a/b/c/d" } },
{
{ "a/b/symlink_to_c/d", PV_RESOLVE_FLAGS_KEEP_FINAL_SYMLINK },
{ "a/b/c/d" }
},
{
{ "a/b/symlink_to_c/d", PV_RESOLVE_FLAGS_REJECT_SYMLINKS },
{ NULL, G_IO_ERROR_TOO_MANY_LINKS }
},
{ { "a/b/symlink_to_b2" }, { "a/b2" } },
{ { "a/b/symlink_to_c2" }, { "a/b2/c2" } },
{ { "a/b/abs_symlink_to_run" }, { NULL, G_IO_ERROR_NOT_FOUND } },
{
{ "a/b/abs_symlink_to_run", PV_RESOLVE_FLAGS_KEEP_FINAL_SYMLINK },
{ "a/b/abs_symlink_to_run" }
},
{ { "run" }, { NULL, G_IO_ERROR_NOT_FOUND } }, /* Wasn't created yet */
{ { "a/b/abs_symlink_to_run", PV_RESOLVE_FLAGS_MKDIR_P }, { "run" } },
{ { "a/b/abs_symlink_to_run/host" }, { NULL, G_IO_ERROR_NOT_FOUND } },
{ { "a/b/abs_symlink_to_run/host", PV_RESOLVE_FLAGS_MKDIR_P }, { "run/host" } },
{ { "a/b/long_symlink_to_dev" }, { NULL, G_IO_ERROR_NOT_FOUND } },
{ { "a/b/long_symlink_to_dev/shm" }, { NULL, G_IO_ERROR_NOT_FOUND } },
{ { "a/b/long_symlink_to_dev/shm", PV_RESOLVE_FLAGS_MKDIR_P }, { "dev/shm" } },
{ { "a/b/../b2/c2/../c3", PV_RESOLVE_FLAGS_MKDIR_P }, { "a/b2/c3" } },
{ { "x" }, { NULL, G_IO_ERROR_NOT_FOUND } },
{ { "x", PV_RESOLVE_FLAGS_KEEP_FINAL_SYMLINK }, { "x" } },
/* This is a bit odd: unlike mkdir -p, we create targets for dangling
* symlinks. It's easier to do this than not, and for pressure-vessel's
* use-case it probably even makes more sense than not. */
{ { "x/y" }, { NULL, G_IO_ERROR_NOT_FOUND } },
{ { "x/y", PV_RESOLVE_FLAGS_MKDIR_P }, { "create_me/y" } },
};
g_autoptr(GError) error = NULL;
g_auto(GLnxTmpDir) tmpdir = { FALSE };
gsize i;
glnx_mkdtemp ("test-XXXXXX", 0700, &tmpdir, &error);
g_assert_no_error (error);
for (i = 0; i < G_N_ELEMENTS (prepare_dirs); i++)
{
const char *it = prepare_dirs[i];
glnx_shutil_mkdir_p_at (tmpdir.fd, it, 0700, NULL, &error);
g_assert_no_error (error);
}
for (i = 0; i < G_N_ELEMENTS (prepare_symlinks); i++)
{
const Symlink *it = &prepare_symlinks[i];
if (symlinkat (it->target, tmpdir.fd, it->name) != 0)
g_error ("symlinkat %s: %s", it->name, g_strerror (errno));
}
for (i = 0; i < G_N_ELEMENTS (tests); i++)
{
const ResolveTest *it = &tests[i];
glnx_autofd int fd = -1;
g_autofree gchar *path = NULL;
gchar **out_path;
g_autoptr(GString) description = g_string_new ("");
TestsOpenFdSet old_fds;
old_fds = tests_check_fd_leaks_enter ();
if (it->call.flags & PV_RESOLVE_FLAGS_MKDIR_P)
g_string_append (description, " (creating directories)");
if (it->call.flags & PV_RESOLVE_FLAGS_KEEP_FINAL_SYMLINK)
g_string_append (description, " (not following final symlink)");
if (it->call.flags & PV_RESOLVE_FLAGS_REJECT_SYMLINKS)
g_string_append (description, " (not following any symlink)");
g_test_message ("%" G_GSIZE_FORMAT ": Resolving %s%s",
i, it->call.path, description->str);
if (it->call.test_flags & RESOLVE_CALL_FLAGS_IGNORE_PATH)
out_path = NULL;
else
out_path = &path;
fd = pv_resolve_in_sysroot (tmpdir.fd, it->call.path,
it->call.flags, out_path, &error);
if (it->expect.path != NULL)
{
g_assert_no_error (error);
g_assert_cmpint (fd, >=, 0);
if (out_path != NULL)
g_assert_cmpstr (*out_path, ==, it->expect.path);
g_assert_true (fd_same_as_rel_path_nofollow (fd, tmpdir.fd,
it->expect.path));
}
else
{
g_assert_error (error, G_IO_ERROR, it->expect.code);
g_assert_cmpint (fd, ==, -1);
if (out_path != NULL)
g_assert_cmpstr (*out_path, ==, NULL);
g_clear_error (&error);
}
glnx_close_fd (&fd);
tests_check_fd_leaks_leave (old_fds);
}
}
int
main (int argc,
char **argv)
{
pv_avoid_gvfs ();
g_test_init (&argc, &argv, NULL);
g_test_add ("/resolve-in-sysroot", Fixture, NULL,
setup, test_resolve_in_sysroot, teardown);
return g_test_run ();
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment