2015-09-07 14:04:15 +00:00
|
|
|
#include "config.h"
|
|
|
|
|
|
|
|
#include <locale.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <errno.h>
|
|
|
|
|
|
|
|
#include "libglnx/libglnx.h"
|
|
|
|
|
|
|
|
#include <gio/gio.h>
|
|
|
|
#include <gio/gunixfdlist.h>
|
|
|
|
|
|
|
|
#include "document-portal/xdp-dbus.h"
|
|
|
|
|
2016-05-06 16:03:47 +00:00
|
|
|
#include "flatpak-dbus.h"
|
2015-09-07 14:04:15 +00:00
|
|
|
|
|
|
|
char outdir[] = "/tmp/xdp-test-XXXXXX";
|
|
|
|
|
|
|
|
GTestDBus *dbus;
|
|
|
|
GDBusConnection *session_bus;
|
|
|
|
XdpDbusDocuments *documents;
|
|
|
|
char *mountpoint;
|
2016-01-28 11:24:34 +00:00
|
|
|
static gboolean have_fuse;
|
2015-09-07 14:04:15 +00:00
|
|
|
|
|
|
|
static char *
|
|
|
|
make_doc_dir (const char *id, const char *app)
|
|
|
|
{
|
|
|
|
if (app)
|
|
|
|
return g_build_filename (mountpoint, "by-app", app, id, NULL);
|
|
|
|
else
|
|
|
|
return g_build_filename (mountpoint, id, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static char *
|
|
|
|
make_doc_path (const char *id, const char *basename, const char *app)
|
|
|
|
{
|
|
|
|
g_autofree char *dir = make_doc_dir (id, app);
|
2016-05-06 14:03:27 +00:00
|
|
|
|
2015-09-07 14:04:15 +00:00
|
|
|
return g_build_filename (dir, basename, NULL);
|
|
|
|
}
|
|
|
|
|
2016-03-14 10:33:13 +00:00
|
|
|
static void
|
|
|
|
assert_host_has_contents (const char *basename, const char *expected_contents)
|
|
|
|
{
|
|
|
|
g_autofree char *path = g_build_filename (outdir, basename, NULL);
|
|
|
|
g_autofree char *real_contents = NULL;
|
|
|
|
gsize real_contents_length;
|
|
|
|
GError *error = NULL;
|
|
|
|
|
|
|
|
g_file_get_contents (path, &real_contents, &real_contents_length, &error);
|
|
|
|
g_assert_no_error (error);
|
|
|
|
g_assert_cmpstr (real_contents, ==, expected_contents);
|
|
|
|
g_assert_cmpuint (real_contents_length, ==, strlen (expected_contents));
|
|
|
|
}
|
|
|
|
|
2015-09-07 14:04:15 +00:00
|
|
|
static void
|
|
|
|
assert_doc_has_contents (const char *id, const char *basename, const char *app, const char *expected_contents)
|
|
|
|
{
|
|
|
|
g_autofree char *path = make_doc_path (id, basename, app);
|
|
|
|
g_autofree char *real_contents = NULL;
|
|
|
|
gsize real_contents_length;
|
|
|
|
GError *error = NULL;
|
|
|
|
|
|
|
|
g_file_get_contents (path, &real_contents, &real_contents_length, &error);
|
|
|
|
g_assert_no_error (error);
|
|
|
|
g_assert_cmpstr (real_contents, ==, expected_contents);
|
|
|
|
g_assert_cmpuint (real_contents_length, ==, strlen (expected_contents));
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
assert_doc_not_exist (const char *id, const char *basename, const char *app)
|
|
|
|
{
|
|
|
|
g_autofree char *path = make_doc_path (id, basename, app);
|
|
|
|
struct stat buf;
|
|
|
|
int res, fd;
|
|
|
|
|
|
|
|
res = stat (path, &buf);
|
|
|
|
g_assert_cmpint (res, ==, -1);
|
|
|
|
g_assert_cmpint (errno, ==, ENOENT);
|
|
|
|
|
|
|
|
fd = open (path, O_RDONLY);
|
|
|
|
g_assert_cmpint (fd, ==, -1);
|
|
|
|
g_assert_cmpint (errno, ==, ENOENT);
|
|
|
|
}
|
|
|
|
|
|
|
|
static char *
|
|
|
|
export_file (const char *path, gboolean unique)
|
|
|
|
{
|
|
|
|
int fd, fd_id;
|
|
|
|
GUnixFDList *fd_list = NULL;
|
2016-05-06 14:03:27 +00:00
|
|
|
|
2015-09-07 14:04:15 +00:00
|
|
|
g_autoptr(GVariant) reply = NULL;
|
|
|
|
GError *error = NULL;
|
|
|
|
char *doc_id;
|
|
|
|
|
|
|
|
fd = open (path, O_PATH | O_CLOEXEC);
|
|
|
|
g_assert (fd >= 0);
|
|
|
|
|
|
|
|
fd_list = g_unix_fd_list_new ();
|
|
|
|
fd_id = g_unix_fd_list_append (fd_list, fd, &error);
|
|
|
|
g_assert_no_error (error);
|
|
|
|
close (fd);
|
|
|
|
|
|
|
|
reply = g_dbus_connection_call_with_unix_fd_list_sync (session_bus,
|
|
|
|
"org.freedesktop.portal.Documents",
|
|
|
|
"/org/freedesktop/portal/documents",
|
|
|
|
"org.freedesktop.portal.Documents",
|
|
|
|
"Add",
|
2015-09-17 18:08:10 +00:00
|
|
|
g_variant_new ("(hbb)", fd_id, !unique, FALSE),
|
2015-09-07 14:04:15 +00:00
|
|
|
G_VARIANT_TYPE ("(s)"),
|
|
|
|
G_DBUS_CALL_FLAGS_NONE,
|
|
|
|
30000,
|
|
|
|
fd_list, NULL,
|
|
|
|
NULL,
|
|
|
|
&error);
|
|
|
|
g_object_unref (fd_list);
|
|
|
|
g_assert_no_error (error);
|
2016-01-27 21:54:21 +00:00
|
|
|
g_assert (reply != NULL);
|
2015-09-07 14:04:15 +00:00
|
|
|
|
|
|
|
g_variant_get (reply, "(s)", &doc_id);
|
|
|
|
g_assert (doc_id != NULL);
|
|
|
|
return doc_id;
|
|
|
|
}
|
|
|
|
|
|
|
|
static char *
|
|
|
|
export_new_file (const char *basename, const char *contents, gboolean unique)
|
|
|
|
{
|
|
|
|
g_autofree char *path = NULL;
|
|
|
|
GError *error = NULL;
|
|
|
|
|
|
|
|
path = g_build_filename (outdir, basename, NULL);
|
|
|
|
|
|
|
|
g_file_set_contents (path, contents, -1, &error);
|
|
|
|
g_assert_no_error (error);
|
|
|
|
|
2015-09-08 13:31:01 +00:00
|
|
|
return export_file (path, unique);
|
2015-09-07 14:04:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
update_doc (const char *id, const char *basename, const char *app, const char *contents, GError **error)
|
|
|
|
{
|
|
|
|
g_autofree char *path = make_doc_path (id, basename, app);
|
|
|
|
|
|
|
|
return g_file_set_contents (path, contents, -1, error);
|
|
|
|
}
|
|
|
|
|
2016-03-14 10:33:13 +00:00
|
|
|
static gboolean
|
|
|
|
update_from_host (const char *basename, const char *contents, GError **error)
|
|
|
|
{
|
|
|
|
g_autofree char *path = g_build_filename (outdir, basename, NULL);
|
|
|
|
|
|
|
|
return g_file_set_contents (path, contents, -1, error);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-09-07 14:04:15 +00:00
|
|
|
static void
|
|
|
|
grant_permissions (const char *id, const char *app, gboolean write)
|
|
|
|
{
|
|
|
|
g_autoptr(GPtrArray) permissions = g_ptr_array_new ();
|
|
|
|
GError *error = NULL;
|
|
|
|
|
|
|
|
g_ptr_array_add (permissions, "read");
|
|
|
|
if (write)
|
|
|
|
g_ptr_array_add (permissions, "write");
|
|
|
|
g_ptr_array_add (permissions, NULL);
|
|
|
|
|
|
|
|
xdp_dbus_documents_call_grant_permissions_sync (documents,
|
|
|
|
id,
|
|
|
|
app,
|
2016-05-06 14:03:27 +00:00
|
|
|
(const char **) permissions->pdata,
|
2015-09-07 14:04:15 +00:00
|
|
|
NULL,
|
|
|
|
&error);
|
|
|
|
g_assert_no_error (error);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
test_create_doc (void)
|
|
|
|
{
|
2016-03-14 10:33:13 +00:00
|
|
|
g_autofree char *doc_path = NULL;
|
|
|
|
g_autofree char *doc_app_path = NULL;
|
|
|
|
g_autofree char *host_path = NULL;
|
2015-09-07 14:04:15 +00:00
|
|
|
g_autofree char *id = NULL;
|
2016-03-14 10:33:13 +00:00
|
|
|
g_autofree char *id2 = NULL;
|
|
|
|
g_autofree char *id3 = NULL;
|
|
|
|
g_autofree char *id4 = NULL;
|
|
|
|
g_autofree char *id5 = NULL;
|
2015-09-07 14:04:15 +00:00
|
|
|
const char *basename = "a-file";
|
|
|
|
GError *error = NULL;
|
|
|
|
|
2016-01-28 11:24:34 +00:00
|
|
|
if (!have_fuse)
|
|
|
|
{
|
|
|
|
g_test_skip ("this test requires FUSE");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-03-14 10:33:13 +00:00
|
|
|
/* Export a document */
|
2015-09-07 14:04:15 +00:00
|
|
|
id = export_new_file (basename, "content", FALSE);
|
|
|
|
|
2016-03-14 10:33:13 +00:00
|
|
|
/* Ensure its there and not viewable by apps */
|
2015-09-07 14:04:15 +00:00
|
|
|
assert_doc_has_contents (id, basename, NULL, "content");
|
2016-03-14 10:33:13 +00:00
|
|
|
assert_host_has_contents (basename, "content");
|
2015-09-07 14:04:15 +00:00
|
|
|
assert_doc_not_exist (id, basename, "com.test.App1");
|
|
|
|
assert_doc_not_exist (id, basename, "com.test.App2");
|
|
|
|
assert_doc_not_exist (id, "another-file", NULL);
|
|
|
|
assert_doc_not_exist ("anotherid", basename, NULL);
|
|
|
|
|
2016-03-14 10:33:13 +00:00
|
|
|
/* Create a tmp file in same dir, ensure it works and can't be seen by other apps */
|
|
|
|
assert_doc_not_exist (id, "tmp1", NULL);
|
|
|
|
update_doc (id, "tmp1", NULL, "tmpdata1", &error);
|
|
|
|
g_assert_no_error (error);
|
|
|
|
assert_doc_has_contents (id, "tmp1", NULL, "tmpdata1");
|
|
|
|
assert_doc_not_exist (id, "tmp1", "com.test.App1");
|
|
|
|
|
|
|
|
/* Let App 1 see the document (but not write) */
|
2015-09-07 14:04:15 +00:00
|
|
|
grant_permissions (id, "com.test.App1", FALSE);
|
|
|
|
|
2016-03-14 10:33:13 +00:00
|
|
|
/* Ensure App 1 and only it can see the document and tmpfile */
|
2015-09-07 14:04:15 +00:00
|
|
|
assert_doc_has_contents (id, basename, "com.test.App1", "content");
|
|
|
|
assert_doc_not_exist (id, basename, "com.test.App2");
|
2016-03-14 10:33:13 +00:00
|
|
|
assert_doc_not_exist (id, "tmp1", "com.test.App1");
|
|
|
|
|
|
|
|
/* Make sure App 1 can't create a tmpfile */
|
|
|
|
assert_doc_not_exist (id, "tmp2", "com.test.App1");
|
|
|
|
update_doc (id, "tmp2", "com.test.App1", "tmpdata2", &error);
|
|
|
|
g_assert_error (error, G_FILE_ERROR, G_FILE_ERROR_ACCES);
|
|
|
|
g_clear_error (&error);
|
|
|
|
assert_doc_not_exist (id, "tmp2", "com.test.App1");
|
2015-09-07 14:04:15 +00:00
|
|
|
|
2016-03-14 10:33:13 +00:00
|
|
|
/* Update the document contents, ensure this is propagater */
|
2015-09-07 14:04:15 +00:00
|
|
|
update_doc (id, basename, NULL, "content2", &error);
|
|
|
|
g_assert_no_error (error);
|
2016-03-14 10:33:13 +00:00
|
|
|
assert_host_has_contents (basename, "content2");
|
2015-09-07 14:04:15 +00:00
|
|
|
assert_doc_has_contents (id, basename, NULL, "content2");
|
|
|
|
assert_doc_has_contents (id, basename, "com.test.App1", "content2");
|
|
|
|
assert_doc_not_exist (id, basename, "com.test.App2");
|
2016-03-14 10:33:13 +00:00
|
|
|
assert_doc_not_exist (id, "tmp1", "com.test.App2");
|
|
|
|
|
|
|
|
/* Update the document contents outside fuse fd, ensure this is propagater */
|
|
|
|
update_from_host (basename, "content3", &error);
|
|
|
|
g_assert_no_error (error);
|
|
|
|
assert_host_has_contents (basename, "content3");
|
|
|
|
assert_doc_has_contents (id, basename, NULL, "content3");
|
|
|
|
assert_doc_has_contents (id, basename, "com.test.App1", "content3");
|
|
|
|
assert_doc_not_exist (id, basename, "com.test.App2");
|
|
|
|
assert_doc_not_exist (id, "tmp1", "com.test.App2");
|
2015-09-07 14:04:15 +00:00
|
|
|
|
2016-03-14 10:33:13 +00:00
|
|
|
/* Try to update the doc from an app that can't write to it */
|
|
|
|
update_doc (id, basename, "com.test.App1", "content4", &error);
|
2015-09-07 14:04:15 +00:00
|
|
|
g_assert_error (error, G_FILE_ERROR, G_FILE_ERROR_ACCES);
|
|
|
|
g_clear_error (&error);
|
2016-03-14 10:33:13 +00:00
|
|
|
|
|
|
|
/* Try to create a tmp file for an app that is not allowed */
|
|
|
|
assert_doc_not_exist (id, "tmp2", "com.test.App1");
|
|
|
|
update_doc (id, "tmp2", "com.test.App1", "tmpdata2", &error);
|
|
|
|
g_assert_error (error, G_FILE_ERROR, G_FILE_ERROR_ACCES);
|
|
|
|
g_clear_error (&error);
|
|
|
|
assert_doc_not_exist (id, "tmp2", "com.test.App1");
|
|
|
|
assert_doc_not_exist (id, "tmp2", NULL);
|
|
|
|
|
|
|
|
/* Grant write permissions to App1 */
|
|
|
|
grant_permissions (id, "com.test.App1", TRUE);
|
|
|
|
|
|
|
|
/* update the doc from an app with write access */
|
|
|
|
update_doc (id, basename, "com.test.App1", "content5", &error);
|
|
|
|
g_assert_no_error (error);
|
|
|
|
assert_host_has_contents (basename, "content5");
|
|
|
|
assert_doc_has_contents (id, basename, NULL, "content5");
|
|
|
|
assert_doc_has_contents (id, basename, "com.test.App1", "content5");
|
|
|
|
assert_doc_not_exist (id, basename, "com.test.App2");
|
|
|
|
|
|
|
|
/* Try to create a tmp file for an app */
|
|
|
|
assert_doc_not_exist (id, "tmp3", "com.test.App1");
|
|
|
|
update_doc (id, "tmp3", "com.test.App1", "tmpdata3", &error);
|
|
|
|
g_assert_no_error (error);
|
|
|
|
assert_doc_has_contents (id, "tmp3", "com.test.App1", "tmpdata3");
|
|
|
|
assert_doc_not_exist (id, "tmp3", NULL);
|
|
|
|
|
|
|
|
/* Re-Create a file from a fuse document file, in various ways */
|
|
|
|
doc_path = make_doc_path (id, basename, NULL);
|
|
|
|
doc_app_path = make_doc_path (id, basename, "com.test.App1");
|
|
|
|
host_path = g_build_filename (outdir, basename, NULL);
|
|
|
|
id2 = export_file (doc_path, FALSE);
|
|
|
|
g_assert_cmpstr (id, ==, id2);
|
|
|
|
id3 = export_file (doc_app_path, FALSE);
|
|
|
|
g_assert_cmpstr (id, ==, id3);
|
|
|
|
id4 = export_file (host_path, FALSE);
|
|
|
|
g_assert_cmpstr (id, ==, id4);
|
|
|
|
|
|
|
|
/* Ensure we can make a unique document */
|
|
|
|
id5 = export_file (host_path, TRUE);
|
|
|
|
g_assert_cmpstr (id, !=, id5);
|
2015-09-07 14:04:15 +00:00
|
|
|
}
|
|
|
|
|
2015-09-08 13:34:51 +00:00
|
|
|
static void
|
|
|
|
test_recursive_doc (void)
|
|
|
|
{
|
|
|
|
g_autofree char *id = NULL;
|
|
|
|
g_autofree char *id2 = NULL;
|
|
|
|
g_autofree char *id3 = NULL;
|
|
|
|
const char *basename = "recursive-file";
|
|
|
|
g_autofree char *path = NULL;
|
|
|
|
g_autofree char *app_path = NULL;
|
|
|
|
|
2016-01-28 11:24:34 +00:00
|
|
|
if (!have_fuse)
|
|
|
|
{
|
|
|
|
g_test_skip ("this test requires FUSE");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-09-08 13:34:51 +00:00
|
|
|
id = export_new_file (basename, "recursive-content", FALSE);
|
|
|
|
|
|
|
|
assert_doc_has_contents (id, basename, NULL, "recursive-content");
|
|
|
|
|
|
|
|
path = make_doc_path (id, basename, NULL);
|
|
|
|
g_print ("path: %s\n", path);
|
|
|
|
|
|
|
|
id2 = export_file (path, FALSE);
|
|
|
|
|
|
|
|
g_assert_cmpstr (id, ==, id2);
|
|
|
|
|
|
|
|
grant_permissions (id, "com.test.App1", FALSE);
|
|
|
|
|
|
|
|
app_path = make_doc_path (id, basename, "com.test.App1");
|
|
|
|
|
|
|
|
id3 = export_file (app_path, FALSE);
|
|
|
|
|
|
|
|
g_assert_cmpstr (id, ==, id3);
|
|
|
|
}
|
2015-09-07 14:04:15 +00:00
|
|
|
|
2016-01-28 23:02:00 +00:00
|
|
|
static void
|
|
|
|
global_setup (void)
|
2015-09-07 14:04:15 +00:00
|
|
|
{
|
|
|
|
gboolean inited;
|
2016-01-28 11:24:34 +00:00
|
|
|
g_autofree gchar *fusermount = NULL;
|
2015-09-07 14:04:15 +00:00
|
|
|
GError *error = NULL;
|
2016-05-17 08:27:57 +00:00
|
|
|
g_autofree gchar *services = NULL;
|
2015-09-07 14:04:15 +00:00
|
|
|
|
2016-01-28 11:24:34 +00:00
|
|
|
fusermount = g_find_program_in_path ("fusermount");
|
|
|
|
/* cache result so subsequent tests can be marked as skipped */
|
|
|
|
have_fuse = (access ("/dev/fuse", W_OK) == 0 &&
|
|
|
|
fusermount != NULL &&
|
|
|
|
g_file_test (fusermount, G_FILE_TEST_IS_EXECUTABLE));
|
|
|
|
|
|
|
|
if (!have_fuse)
|
|
|
|
return;
|
|
|
|
|
2015-09-07 14:04:15 +00:00
|
|
|
g_mkdtemp (outdir);
|
|
|
|
g_print ("outdir: %s\n", outdir);
|
|
|
|
|
|
|
|
g_setenv ("XDG_RUNTIME_DIR", outdir, TRUE);
|
|
|
|
g_setenv ("XDG_DATA_HOME", outdir, TRUE);
|
|
|
|
|
|
|
|
dbus = g_test_dbus_new (G_TEST_DBUS_NONE);
|
2016-05-17 08:27:57 +00:00
|
|
|
services = g_test_build_filename (G_TEST_BUILT, "services", NULL);
|
|
|
|
g_test_dbus_add_service_dir (dbus, services);
|
2015-09-07 14:04:15 +00:00
|
|
|
g_test_dbus_up (dbus);
|
|
|
|
|
2015-09-08 13:34:21 +00:00
|
|
|
/* g_test_dbus_up unsets this, so re-set */
|
|
|
|
g_setenv ("XDG_RUNTIME_DIR", outdir, TRUE);
|
|
|
|
|
2015-09-07 14:04:15 +00:00
|
|
|
session_bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error);
|
|
|
|
g_assert_no_error (error);
|
|
|
|
|
|
|
|
documents = xdp_dbus_documents_proxy_new_sync (session_bus, 0,
|
|
|
|
"org.freedesktop.portal.Documents",
|
|
|
|
"/org/freedesktop/portal/documents",
|
|
|
|
NULL, &error);
|
|
|
|
g_assert_no_error (error);
|
|
|
|
g_assert (documents != NULL);
|
|
|
|
|
|
|
|
inited = xdp_dbus_documents_call_get_mount_point_sync (documents, &mountpoint,
|
|
|
|
NULL, &error);
|
|
|
|
g_assert_no_error (error);
|
|
|
|
g_assert (inited);
|
|
|
|
g_assert (mountpoint != NULL);
|
2016-01-28 23:02:00 +00:00
|
|
|
}
|
2015-09-07 14:04:15 +00:00
|
|
|
|
2016-01-28 23:02:00 +00:00
|
|
|
static void
|
|
|
|
global_teardown (void)
|
|
|
|
{
|
|
|
|
GError *error = NULL;
|
2015-09-07 14:04:15 +00:00
|
|
|
|
2016-01-28 11:24:34 +00:00
|
|
|
if (!have_fuse)
|
|
|
|
return;
|
2015-09-07 14:04:15 +00:00
|
|
|
|
|
|
|
g_free (mountpoint);
|
|
|
|
|
|
|
|
g_object_unref (documents);
|
|
|
|
|
|
|
|
g_dbus_connection_close_sync (session_bus, NULL, &error);
|
|
|
|
g_assert_no_error (error);
|
|
|
|
|
|
|
|
g_object_unref (session_bus);
|
|
|
|
|
|
|
|
g_test_dbus_down (dbus);
|
|
|
|
|
|
|
|
g_object_unref (dbus);
|
|
|
|
|
|
|
|
/* We race on the unmount of the fuse fs, which causes the rm -rf to stop at the doc dir.
|
|
|
|
This makes the chance of completely removing the directory higher */
|
|
|
|
sleep (1);
|
|
|
|
|
|
|
|
glnx_shutil_rm_rf_at (-1, outdir, NULL, NULL);
|
2016-01-28 23:02:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
main (int argc, char **argv)
|
|
|
|
{
|
|
|
|
int res;
|
|
|
|
|
|
|
|
g_test_init (&argc, &argv, NULL);
|
|
|
|
|
|
|
|
g_test_add_func ("/db/create_doc", test_create_doc);
|
|
|
|
g_test_add_func ("/db/recursive_doc", test_recursive_doc);
|
|
|
|
|
|
|
|
global_setup ();
|
|
|
|
|
|
|
|
res = g_test_run ();
|
|
|
|
|
|
|
|
global_teardown ();
|
2015-09-07 14:04:15 +00:00
|
|
|
|
|
|
|
return res;
|
|
|
|
}
|