diff options
-rw-r--r-- | app/xdg-app-builtins-document.c | 50 | ||||
-rw-r--r-- | data/org.freedesktop.portal.Documents.xml | 7 | ||||
-rw-r--r-- | document-portal/xdp-main.c | 96 |
3 files changed, 139 insertions, 14 deletions
diff --git a/app/xdg-app-builtins-document.c b/app/xdg-app-builtins-document.c index d2b2635..d066368 100644 --- a/app/xdg-app-builtins-document.c +++ b/app/xdg-app-builtins-document.c @@ -40,12 +40,14 @@ gboolean opt_unique = FALSE; gboolean opt_allow_write = FALSE; gboolean opt_allow_delete = FALSE; gboolean opt_transient = FALSE; +gboolean opt_noexist = FALSE; gboolean opt_allow_grant_permissions = FALSE; char **opt_apps = NULL; static GOptionEntry options[] = { { "unique", 'u', 0, G_OPTION_ARG_NONE, &opt_unique, "Create a unique document reference", NULL }, { "transient", 't', 0, G_OPTION_ARG_NONE, &opt_transient, "Make the document transient for the current session", NULL }, + { "noexist", 'n', 0, G_OPTION_ARG_NONE, &opt_noexist, "Don't require the file to exist already", NULL }, { "allow-write", 'w', 0, G_OPTION_ARG_NONE, &opt_allow_write, "Give the app write permissions", NULL }, { "allow-delete", 'd', 0, G_OPTION_ARG_NONE, &opt_allow_delete, "Give the app permissions to delete the document id", NULL }, { "allow-grant-permission", 'd', 0, G_OPTION_ARG_NONE, &opt_allow_grant_permissions, "Give the app permissions to grant furthern permissions", NULL }, @@ -65,6 +67,7 @@ xdg_app_builtin_export_file (int argc, char **argv, const char *file; g_autofree char *mountpoint = NULL; g_autofree char *basename = NULL; + g_autofree char *dirname = NULL; g_autofree char *doc_path = NULL; XdpDbusDocuments *documents; int fd, fd_id; @@ -83,6 +86,8 @@ xdg_app_builtin_export_file (int argc, char **argv, return usage_error (context, "FILE must be specified", error); file = argv[1]; + dirname = g_path_get_dirname (file); + basename = g_path_get_basename (file); session_bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, error); if (session_bus == NULL) @@ -99,7 +104,11 @@ xdg_app_builtin_export_file (int argc, char **argv, NULL, error)) return FALSE; - fd = open (file, O_PATH | O_CLOEXEC); + if (opt_noexist) + fd = open (dirname, O_PATH | O_CLOEXEC); + else + fd = open (file, O_PATH | O_CLOEXEC); + if (fd == -1) { glnx_set_error_from_errno (error); @@ -110,18 +119,32 @@ xdg_app_builtin_export_file (int argc, char **argv, fd_id = g_unix_fd_list_append (fd_list, fd, 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", - g_variant_new ("(hbb)", fd_id, !opt_unique, !opt_transient), - G_VARIANT_TYPE ("(s)"), - G_DBUS_CALL_FLAGS_NONE, - 30000, - fd_list, NULL, - NULL, - error); + if (opt_noexist) + reply = g_dbus_connection_call_with_unix_fd_list_sync (session_bus, + "org.freedesktop.portal.Documents", + "/org/freedesktop/portal/documents", + "org.freedesktop.portal.Documents", + "AddNamed", + g_variant_new ("(h^aybb)", fd_id, basename, !opt_unique, !opt_transient), + G_VARIANT_TYPE ("(s)"), + G_DBUS_CALL_FLAGS_NONE, + 30000, + fd_list, NULL, + NULL, + error); + else + 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", + g_variant_new ("(hbb)", fd_id, !opt_unique, !opt_transient), + G_VARIANT_TYPE ("(s)"), + G_DBUS_CALL_FLAGS_NONE, + 30000, + fd_list, NULL, + NULL, + error); g_object_unref (fd_list); if (reply == NULL) @@ -152,7 +175,6 @@ xdg_app_builtin_export_file (int argc, char **argv, } - basename = g_path_get_basename (file); doc_path = g_build_filename (mountpoint, doc_id, basename, NULL); g_print ("%s\n", doc_path); diff --git a/data/org.freedesktop.portal.Documents.xml b/data/org.freedesktop.portal.Documents.xml index d18a738..2b65b4e 100644 --- a/data/org.freedesktop.portal.Documents.xml +++ b/data/org.freedesktop.portal.Documents.xml @@ -34,6 +34,13 @@ <arg type='b' name='persistent' direction='in'/> <arg type='s' name='doc_id' direction='out'/> </method> + <method name="AddNamed"> + <arg type='h' name='o_path_parent_fd' direction='in'/> + <arg type='ay' name='filename' direction='in'/> + <arg type='b' name='reuse_existing' direction='in'/> + <arg type='b' name='persistent' direction='in'/> + <arg type='s' name='doc_id' direction='out'/> + </method> <method name="GrantPermissions"> <arg type='s' name='doc_id' direction='in'/> <arg type='s' name='app_id' direction='in'/> diff --git a/document-portal/xdp-main.c b/document-portal/xdp-main.c index 33b8757..4374891 100644 --- a/document-portal/xdp-main.c +++ b/document-portal/xdp-main.c @@ -457,6 +457,101 @@ portal_add (GDBusMethodInvocation *invocation, g_variant_new ("(s)", id)); } +static void +portal_add_named (GDBusMethodInvocation *invocation, + GVariant *parameters, + const char *app_id) +{ + GDBusMessage *message; + GUnixFDList *fd_list; + g_autofree char *id = NULL; + g_autofree char *proc_path = NULL; + int parent_fd_id, parent_fd, fds_len, fd_flags; + const int *fds; + char parent_path_buffer[PATH_MAX+1]; + g_autofree char *path = NULL; + ssize_t symlink_size; + struct stat parent_st_buf; + const char *filename; + gboolean reuse_existing, persistent; + g_autoptr(GVariant) filename_v = NULL; + + g_variant_get (parameters, "(h@aybb)", &parent_fd_id, &filename_v, &reuse_existing, &persistent); + filename = g_variant_get_bytestring (filename_v); + + /* This is only allowed from the host, or else we could leak existance of files */ + if (*app_id != 0) + { + g_dbus_method_invocation_return_error (invocation, XDG_APP_ERROR, XDG_APP_ERROR_NOT_ALLOWED, + "Not enough permissions"); + return; + } + + message = g_dbus_method_invocation_get_message (invocation); + fd_list = g_dbus_message_get_unix_fd_list (message); + + parent_fd = -1; + if (fd_list != NULL) + { + fds = g_unix_fd_list_peek_fds (fd_list, &fds_len); + if (parent_fd_id < fds_len) + parent_fd = fds[parent_fd_id]; + } + + if (strchr (filename, '/') != NULL) + { + g_dbus_method_invocation_return_error (invocation, + XDG_APP_ERROR, XDG_APP_ERROR_INVALID_ARGUMENT, + "Invalid filename passed"); + return; + } + + proc_path = g_strdup_printf ("/proc/self/fd/%d", parent_fd); + + if (parent_fd == -1 || + /* Must be able to get fd flags */ + (fd_flags = fcntl (parent_fd, F_GETFL)) == -1 || + /* Must be O_PATH */ + ((fd_flags & O_PATH) != O_PATH) || + /* Must not be O_NOFOLLOW (because we want the target file) */ + ((fd_flags & O_NOFOLLOW) == O_PATH) || + /* Must be able to fstat */ + fstat (parent_fd, &parent_st_buf) < 0 || + /* Must be a directory file */ + (parent_st_buf.st_mode & S_IFMT) != S_IFDIR || + /* Must be able to read path from /proc/self/fd */ + /* This is an absolute and (at least at open time) symlink-expanded path */ + (symlink_size = readlink (proc_path, parent_path_buffer, sizeof (parent_path_buffer) - 1)) < 0) + { + g_dbus_method_invocation_return_error (invocation, + XDG_APP_ERROR, XDG_APP_ERROR_INVALID_ARGUMENT, + "Invalid fd passed"); + return; + } + + if (parent_st_buf.st_dev == fuse_dev) + { + g_dbus_method_invocation_return_error (invocation, + XDG_APP_ERROR, XDG_APP_ERROR_INVALID_ARGUMENT, + "Invalid fd passed"); + return; + } + + parent_path_buffer[symlink_size] = 0; + + path = g_build_filename (parent_path_buffer, filename, NULL); + + g_debug ("portal_add_named %s\n", path); + + AUTOLOCK(db); + + id = do_create_doc (&parent_st_buf, path, reuse_existing, persistent); + + g_dbus_method_invocation_return_value (invocation, + g_variant_new ("(s)", id)); +} + + typedef void (*PortalMethod) (GDBusMethodInvocation *invocation, GVariant *parameters, const char *app_id); @@ -507,6 +602,7 @@ on_bus_acquired (GDBusConnection *connection, g_signal_connect_swapped (helper, "handle-get-mount-point", G_CALLBACK (handle_get_mount_point), NULL); g_signal_connect_swapped (helper, "handle-add", G_CALLBACK (handle_method), portal_add); + g_signal_connect_swapped (helper, "handle-add-named", G_CALLBACK (handle_method), portal_add_named); g_signal_connect_swapped (helper, "handle-grant-permissions", G_CALLBACK (handle_method), portal_grant_permissions); g_signal_connect_swapped (helper, "handle-revoke-permissions", G_CALLBACK (handle_method), portal_revoke_permissions); g_signal_connect_swapped (helper, "handle-delete", G_CALLBACK (handle_method), portal_delete); |