diff options
author | Alexander Larsson <alexl@redhat.com> | 2015-04-16 21:00:21 +0200 |
---|---|---|
committer | Alexander Larsson <alexl@redhat.com> | 2015-04-16 21:00:21 +0200 |
commit | 9b2439d22870baf33b6d87191202339db41f60f8 (patch) | |
tree | 1d17cb5a94bfa54ff5459af31999e9ba734df21a | |
parent | 13aafd9f3518c9d39b4b7e25558fa19d899af221 (diff) | |
download | gvfs-wip/alexl/documents.tar.gz |
Initial version to do simple read and replace from xdg-document-portalwip/alexl/documents
For example:
$ echo foo | gvfs-save document:1
$ gvfs-cat document:1
foo
$ echo bar | gvfs-save document:1
$ gvfs-cat document:1
bar
-rw-r--r-- | client/Makefile.am | 3 | ||||
-rw-r--r-- | client/gdaemonvfs.c | 12 | ||||
-rw-r--r-- | client/gvfsdocumentfile.c | 691 | ||||
-rw-r--r-- | client/gvfsdocumentfile.h | 58 | ||||
-rw-r--r-- | client/gvfsdocumentinputstream.c | 311 | ||||
-rw-r--r-- | client/gvfsdocumentinputstream.h | 39 | ||||
-rw-r--r-- | client/gvfsdocumentoutputstream.c | 426 | ||||
-rw-r--r-- | client/gvfsdocumentoutputstream.h | 47 |
8 files changed, 1587 insertions, 0 deletions
diff --git a/client/Makefile.am b/client/Makefile.am index 8c7b76a1..ce9c5480 100644 --- a/client/Makefile.am +++ b/client/Makefile.am @@ -39,6 +39,9 @@ vfssources = \ gvfsiconloadable.c gvfsiconloadable.h \ gvfsuriutils.c gvfsuriutils.h \ gvfsurimapper.c gvfsurimapper.h \ + gvfsdocumentfile.c gvfsdocumentfile.h \ + gvfsdocumentinputstream.c gvfsdocumentinputstream.h \ + gvfsdocumentoutputstream.c gvfsdocumentoutputstream.h \ $(URI_PARSER_SOURCES) \ $(NULL) diff --git a/client/gdaemonvfs.c b/client/gdaemonvfs.c index 19e65b1a..dfca085d 100644 --- a/client/gdaemonvfs.c +++ b/client/gdaemonvfs.c @@ -29,6 +29,7 @@ #include "gdaemonvfs.h" #include "gvfsuriutils.h" #include "gdaemonfile.h" +#include "gvfsdocumentfile.h" #include <gio/gio.h> #include <gvfsdaemonprotocol.h> #include <gmodule.h> @@ -410,6 +411,17 @@ g_daemon_vfs_get_file_for_uri (GVfs *vfs, g_free (path); return file; } + + if (g_ascii_strncasecmp (uri, "document:", 9) == 0) + { + file = gvfs_document_file_new (uri); + + if (file == NULL) + /* Dummy file */ + return g_vfs_get_file_for_uri (G_DAEMON_VFS (vfs)->wrapped_vfs, uri); + + return file; + } if (get_mountspec_from_uri (daemon_vfs, uri, &spec, &path)) { diff --git a/client/gvfsdocumentfile.c b/client/gvfsdocumentfile.c new file mode 100644 index 00000000..a38d9971 --- /dev/null +++ b/client/gvfsdocumentfile.c @@ -0,0 +1,691 @@ +/* GIO - GLib Input, Output and Streaming Library + * + * Copyright (C) 2006-2007 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General + * Public License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + * Author: Alexander Larsson <alexl@redhat.com> + */ + +#include <config.h> + +#include <unistd.h> +#include <string.h> +#include <stdio.h> +#include <sys/types.h> +#include <sys/socket.h> + +#include "gvfsdocumentfile.h" +#include "gvfsdocumentinputstream.h" +#include "gvfsdocumentoutputstream.h" +#include <glib/gi18n-lib.h> +#include <gio/gio.h> +#include <gvfsdbus.h> +#include <gio/gunixfdlist.h> + +static void gvfs_document_file_file_iface_init (GFileIface *iface); + +static void gvfs_document_file_read_async (GFile *file, + int io_priority, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data); + +G_DEFINE_TYPE_WITH_CODE (GVfsDocumentFile, gvfs_document_file, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (G_TYPE_FILE, + gvfs_document_file_file_iface_init)) + +static void +gvfs_document_file_finalize (GObject *object) +{ + GVfsDocumentFile *doc; + + doc = GVFS_DOCUMENT_FILE (object); + + g_free (doc->path); + + if (G_OBJECT_CLASS (gvfs_document_file_parent_class)->finalize) + (*G_OBJECT_CLASS (gvfs_document_file_parent_class)->finalize) (object); +} + +static void +gvfs_document_file_class_init (GVfsDocumentFileClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + gobject_class->finalize = gvfs_document_file_finalize; +} + +static void +gvfs_document_file_init (GVfsDocumentFile *doc) +{ +} + +static char * +canonicalize_path (char *path) +{ + char *p, *q; + + /* Canonicalize multiple consecutive slashes */ + p = path; + while (*p != 0) + { + q = p; + while (*q && *q == '/') + q++; + + if (q > p + 1) + memmove (p+1, q, strlen (q)+1); + + /* Skip over the one separator */ + p++; + + /* Drop trailing (not first) slash */ + if (*p == 0 && p > path + 1) + { + p--; + *p = 0; + } + + /* Skip until next separator */ + while (*p != 0 && *p != '/') + p++; + } + + return path; +} + +static char * +path_from_uri (const char *uri) +{ + char *to_free = NULL; + char *path, *res, *p, *q; + const char *path_part, *hash; + int len = -1; + + path_part = uri + strlen ("document:"); + + if (g_str_has_prefix (path_part, "///")) + path_part += 2; + else if (g_str_has_prefix (path_part, "//")) + return NULL; /* Has hostname, not valid */ + + hash = strchr (path_part, '#'); + if (hash != NULL) + { + len = hash - path_part; + path_part = to_free = g_strndup (path_part, len); + } + + res = g_uri_unescape_string (path_part, "/"); + + g_clear_pointer (&to_free, g_free); + + if (res == NULL) + return NULL; + + if (*res != '/') + { + to_free = res; + res = g_strconcat ("/", res, NULL); + g_free (to_free); + } + + return canonicalize_path (res); +} + +/* Takes ownership of path */ +static GFile * +gvfs_document_file_new_steals_path (char *path) +{ + GVfsDocumentFile *doc; + + doc = g_object_new (GVFS_TYPE_DOCUMENT_FILE, NULL); + doc->path = path; + + return G_FILE (doc); +} + +GFile * +gvfs_document_file_new (const char *uri) +{ + GVfsDocumentFile *doc; + char *path; + + path = path_from_uri (uri); + + if (path == NULL) + return NULL; /* Creates a dummy GFile */ + + return gvfs_document_file_new_steals_path (path); +} + +static gboolean +gvfs_document_file_is_native (GFile *file) +{ + return FALSE; +} + +static gboolean +gvfs_document_file_has_uri_scheme (GFile *file, + const char *uri_scheme) +{ + GVfsDocumentFile *doc = GVFS_DOCUMENT_FILE (file); + + return g_ascii_strcasecmp ("document", uri_scheme) == 0; +} + +static char * +gvfs_document_file_get_uri_scheme (GFile *file) +{ + GVfsDocumentFile *doc = GVFS_DOCUMENT_FILE (file); + const char *scheme; + + return g_strdup ("document"); +} + +static char * +gvfs_document_file_get_basename (GFile *file) +{ + GVfsDocumentFile *doc = GVFS_DOCUMENT_FILE (file); + char *last_slash; + + return g_path_get_basename (doc->path); +} + +static char * +gvfs_document_file_get_path (GFile *file) +{ + return NULL; +} + +static char * +gvfs_document_file_get_uri (GFile *file) +{ + GVfsDocumentFile *doc = GVFS_DOCUMENT_FILE (file); + char *res, *escaped_path; + + escaped_path = g_uri_escape_string (doc->path, + G_URI_RESERVED_CHARS_ALLOWED_IN_PATH, FALSE); + res = g_strconcat ("document://", escaped_path, NULL); + g_free (escaped_path); + + return res; +} + +static char * +gvfs_document_file_get_parse_name (GFile *file) +{ + GVfsDocumentFile *doc = GVFS_DOCUMENT_FILE (file); + + return gvfs_document_file_get_uri (file); +} + +static GFile * +gvfs_document_file_get_parent (GFile *file) +{ + GVfsDocumentFile *doc = GVFS_DOCUMENT_FILE (file); + GVfsDocumentFile *parent; + char *dirname; + + if (strcmp (doc->path, "/")) + return NULL; + + dirname = g_path_get_dirname (doc->path); + + return gvfs_document_file_new_steals_path (dirname); +} + +static GFile * +gvfs_document_file_dup (GFile *file) +{ + GVfsDocumentFile *doc = GVFS_DOCUMENT_FILE (file); + + return gvfs_document_file_new_steals_path (g_strdup (doc->path)); +} + +static guint +gvfs_document_file_hash (GFile *file) +{ + GVfsDocumentFile *doc = GVFS_DOCUMENT_FILE (file); + + return g_str_hash (doc->path); +} + +static gboolean +gvfs_document_file_equal (GFile *file1, + GFile *file2) +{ + GVfsDocumentFile *doc1 = GVFS_DOCUMENT_FILE (file1); + GVfsDocumentFile *doc2 = GVFS_DOCUMENT_FILE (file2); + + return g_str_equal (doc1->path, doc2->path); +} + +static const char * +match_prefix (const char *path, + const char *prefix) +{ + int prefix_len; + + prefix_len = strlen (prefix); + if (strncmp (path, prefix, prefix_len) != 0) + return NULL; + + /* Handle the case where prefix is the root, so that + * the IS_DIR_SEPRARATOR check below works */ + if (prefix_len > 0 && + prefix[prefix_len-1] == '/') + prefix_len--; + + return path + prefix_len; +} + +static gboolean +gvfs_document_file_prefix_matches (GFile *parent, + GFile *descendant) +{ + GVfsDocumentFile *parent_doc = GVFS_DOCUMENT_FILE (parent); + GVfsDocumentFile *descendant_doc = GVFS_DOCUMENT_FILE (descendant); + const char *remainder; + + remainder = match_prefix (descendant_doc->path, parent_doc->path); + if (remainder != NULL && *remainder == '/') + return TRUE; + return FALSE; +} + +static char * +gvfs_document_file_get_relative_path (GFile *parent, + GFile *descendant) +{ + GVfsDocumentFile *parent_doc = GVFS_DOCUMENT_FILE (parent); + GVfsDocumentFile *descendant_doc = GVFS_DOCUMENT_FILE (descendant); + const char *remainder; + + remainder = match_prefix (descendant_doc->path, parent_doc->path); + if (remainder != NULL && *remainder == '/') + return g_strdup (remainder + 1); + return NULL; +} + +static GFile * +gvfs_document_file_resolve_relative_path (GFile *file, + const char *relative_path) +{ + GVfsDocumentFile *doc = GVFS_DOCUMENT_FILE (file); + char *filename; + GFile *child; + + if (g_path_is_absolute (relative_path)) + return gvfs_document_file_new_steals_path (canonicalize_path (g_strdup (doc->path))); + + filename = g_build_filename (doc->path, relative_path, NULL); + child = gvfs_document_file_new_steals_path (canonicalize_path (filename)); + g_free (filename); + + return child; +} + +static GFileEnumerator * +gvfs_document_file_enumerate_children (GFile *file, + const char *attributes, + GFileQueryInfoFlags flags, + GCancellable *cancellable, + GError **error) +{ + return NULL; +} + +static GFileInfo * +gvfs_document_file_query_info (GFile *file, + const char *attributes, + GFileQueryInfoFlags flags, + GCancellable *cancellable, + GError **error) +{ + return NULL; +} + +static void +gvfs_document_file_query_info_async (GFile *file, + const char *attributes, + GFileQueryInfoFlags flags, + int io_priority, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ +} + +static GFileInfo * +gvfs_document_file_query_info_finish (GFile *file, + GAsyncResult *res, + GError **error) +{ + return NULL; +} + +static void +gvfs_document_file_read_async (GFile *file, + int io_priority, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data) +{ +} + +static GFileInputStream * +gvfs_document_file_read_finish (GFile *file, + GAsyncResult *res, + GError **error) +{ + GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res); + gpointer op; + + op = g_simple_async_result_get_op_res_gpointer (simple); + if (op) + return g_object_ref (op); + + return NULL; +} + +static gboolean +verify_file_path (GVfsDocumentFile *doc, + GError **error) +{ + if (strcmp (doc->path, "/") == 0) + { + g_set_error_literal (error, G_IO_ERROR, + G_IO_ERROR_IS_DIRECTORY, + _("Can't open directory")); + return FALSE; + } + + if (strchr (doc->path + 1, '/') != NULL) + { + g_set_error_literal (error, G_IO_ERROR, + G_IO_ERROR_NOT_FOUND, + _("No such file")); + return FALSE; + } + + return TRUE; + +} + +static GVariant * +sync_document_call (GVfsDocumentFile *doc, + const char *method, + GVariant *parameters, + const GVariantType *reply_type, + GUnixFDList **out_fd_list, + GCancellable *cancellable, + GError **error) +{ + GVariant *res; + GDBusConnection *bus; + GUnixFDList *ret_fd_list; + GVariant *reply, *fd_v; + char *path; + int handle, fd; + + if (!verify_file_path (doc, error)) + return NULL; + + bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, error); + if (bus == NULL) + return NULL; + + path = g_build_filename ("/org/freedesktop/portal/document", doc->path, NULL); + + res = g_dbus_connection_call_with_unix_fd_list_sync (bus, + "org.freedesktop.portal.DocumentPortal", + path, + "org.freedesktop.portal.Document", + method, + parameters, reply_type, + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + out_fd_list, + cancellable, + error); + + g_object_unref (bus); + return res; +} + +static GFileInputStream * +gvfs_document_file_read (GFile *file, + GCancellable *cancellable, + GError **error) +{ + GVfsDocumentFile *doc = GVFS_DOCUMENT_FILE (file); + GDBusConnection *bus; + GUnixFDList *ret_fd_list; + GVariant *reply, *fd_v; + char *path; + int handle, fd; + + reply = sync_document_call (doc, "Read", + g_variant_new ("()"), + G_VARIANT_TYPE("(h)"), + &ret_fd_list, + cancellable, error); + if (reply == NULL) + return NULL; + + if (ret_fd_list == NULL) + { + g_variant_unref (reply); + g_set_error_literal (error, G_IO_ERROR, + G_IO_ERROR_FAILED, + _("No file descriptor returned")); + return NULL; + } + + g_variant_get (reply, "(@h)", &fd_v); + handle = g_variant_get_handle (fd_v); + g_variant_unref (reply); + + fd = g_unix_fd_list_get (ret_fd_list, handle, error); + g_object_unref (ret_fd_list); + if (fd == -1) + return NULL; + + return _gvfs_document_input_stream_new (fd); +} + +static GFileOutputStream * +gvfs_document_file_create (GFile *file, + GFileCreateFlags flags, + GCancellable *cancellable, + GError **error) +{ + return NULL; +} + +static GFileOutputStream * +gvfs_document_file_replace (GFile *file, + const char *etag, + gboolean make_backup, + GFileCreateFlags flags, + GCancellable *cancellable, + GError **error) +{ + GVfsDocumentFile *doc = GVFS_DOCUMENT_FILE (file); + GDBusConnection *bus; + GUnixFDList *ret_fd_list; + GVariant *reply, *fd_v; + char *path; + int handle, fd; + guint32 id; + char *flags_array[] = { + NULL + }; + + reply = sync_document_call (doc, "PrepareUpdate", + g_variant_new ("(s^as)", + etag ? etag : "", + flags_array), + G_VARIANT_TYPE("(uh)"), + &ret_fd_list, + cancellable, error); + if (reply == NULL) + return NULL; + + if (ret_fd_list == NULL) + { + g_variant_unref (reply); + g_set_error_literal (error, G_IO_ERROR, + G_IO_ERROR_FAILED, + _("No file descriptor returned")); + return NULL; + } + + g_variant_get (reply, "(u@h)", &id, &fd_v); + handle = g_variant_get_handle (fd_v); + g_variant_unref (reply); + + fd = g_unix_fd_list_get (ret_fd_list, handle, error); + g_object_unref (ret_fd_list); + if (fd == -1) + return NULL; + + return gvfs_document_output_stream_new (doc->path, id, fd); +} + +static void +gvfs_document_file_create_async (GFile *file, + GFileCreateFlags flags, + int io_priority, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + file_open_write_async (file, + 0, "", FALSE, flags, io_priority, + cancellable, + callback, user_data); +} + +static GFileOutputStream * +gvfs_document_file_create_finish (GFile *file, + GAsyncResult *res, + GError **error) +{ + GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res); + GFileOutputStream *output_stream; + + output_stream = g_simple_async_result_get_op_res_gpointer (simple); + if (output_stream) + return g_object_ref (output_stream); + + return NULL; +} + +static void +gvfs_document_file_enumerate_children_async (GFile *file, + const char *attributes, + GFileQueryInfoFlags flags, + int io_priority, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ +} + +static GFileEnumerator * +gvfs_document_file_enumerate_children_finish (GFile *file, + GAsyncResult *res, + GError **error) +{ + GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res); + GFileEnumerator *enumerator; + + enumerator = g_simple_async_result_get_op_res_gpointer (simple); + if (enumerator) + return g_object_ref (enumerator); + + return NULL; +} + +static void +gvfs_document_file_replace_async (GFile *file, + const char *etag, + gboolean make_backup, + GFileCreateFlags flags, + int io_priority, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ +} + +static GFileOutputStream * +gvfs_document_file_replace_finish (GFile *file, + GAsyncResult *res, + GError **error) +{ + GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res); + GFileOutputStream *output_stream; + + output_stream = g_simple_async_result_get_op_res_gpointer (simple); + if (output_stream) + return g_object_ref (output_stream); + + return NULL; +} + +static void +gvfs_document_file_file_iface_init (GFileIface *iface) +{ + iface->dup = gvfs_document_file_dup; + iface->hash = gvfs_document_file_hash; + iface->equal = gvfs_document_file_equal; + iface->is_native = gvfs_document_file_is_native; + iface->has_uri_scheme = gvfs_document_file_has_uri_scheme; + iface->get_uri_scheme = gvfs_document_file_get_uri_scheme; + iface->get_basename = gvfs_document_file_get_basename; + iface->get_path = gvfs_document_file_get_path; + iface->get_uri = gvfs_document_file_get_uri; + iface->get_parse_name = gvfs_document_file_get_parse_name; + iface->get_parent = gvfs_document_file_get_parent; + iface->prefix_matches = gvfs_document_file_prefix_matches; + iface->get_relative_path = gvfs_document_file_get_relative_path; + iface->resolve_relative_path = gvfs_document_file_resolve_relative_path; + iface->enumerate_children = gvfs_document_file_enumerate_children; + iface->query_info = gvfs_document_file_query_info; + iface->query_info_async = gvfs_document_file_query_info_async; + iface->query_info_finish = gvfs_document_file_query_info_finish; + iface->read_fn = gvfs_document_file_read; + iface->create = gvfs_document_file_create; + iface->replace = gvfs_document_file_replace; + + /* Async operations */ + + /* + iface->read_async = gvfs_document_file_read_async; + iface->read_finish = gvfs_document_file_read_finish; + iface->create_async = gvfs_document_file_create_async; + iface->create_finish = gvfs_document_file_create_finish; + iface->enumerate_children_async = gvfs_document_file_enumerate_children_async; + iface->enumerate_children_finish = gvfs_document_file_enumerate_children_finish; + iface->replace_async = gvfs_document_file_replace_async; + iface->replace_finish = gvfs_document_file_replace_finish; + */ +} diff --git a/client/gvfsdocumentfile.h b/client/gvfsdocumentfile.h new file mode 100644 index 00000000..3e2a1ee5 --- /dev/null +++ b/client/gvfsdocumentfile.h @@ -0,0 +1,58 @@ +/* GIO - GLib Input, Output and Streaming Library + * + * Copyright (C) 2006-2007 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General + * Public License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + * Author: Alexander Larsson <alexl@redhat.com> + */ + +#ifndef __G_DOCUMENT_FILE_H__ +#define __G_DOCUMENT_FILE_H__ + +#include <gio/gio.h> + +G_BEGIN_DECLS + +#define GVFS_TYPE_DOCUMENT_FILE (gvfs_document_file_get_type ()) +#define GVFS_DOCUMENT_FILE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GVFS_TYPE_DOCUMENT_FILE, GVfsDocumentFile)) +#define GVFS_DOCUMENT_FILE_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GVFS_TYPE_DOCUMENT_FILE, GVfsDocumentFileClass)) +#define GVFS_IS_DOCUMENT_FILE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GVFS_TYPE_DOCUMENT_FILE)) +#define GVFS_IS_DOCUMENT_FILE_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GVFS_TYPE_DOCUMENT_FILE)) +#define GVFS_DOCUMENT_FILE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_VFS_TYPE_DOCUMENT_FILE, GVfsDocumentFileClass)) + +typedef struct _GVfsDocumentFile GVfsDocumentFile; +typedef struct _GVfsDocumentFileClass GVfsDocumentFileClass; + +struct _GVfsDocumentFileClass +{ + GObjectClass parent_class; +}; + +struct _GVfsDocumentFile +{ + GObject parent_instance; + + char *path; +}; + +GType gvfs_document_file_get_type (void) G_GNUC_CONST; + +GFile * gvfs_document_file_new (const char *uri); + +G_END_DECLS + +#endif /* __G_DOCUMENT_FILE_H__ */ diff --git a/client/gvfsdocumentinputstream.c b/client/gvfsdocumentinputstream.c new file mode 100644 index 00000000..f1ea855b --- /dev/null +++ b/client/gvfsdocumentinputstream.c @@ -0,0 +1,311 @@ +#include "config.h" + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> + +#include <glib.h> +#include <glib/gstdio.h> +#include "gvfsdocumentinputstream.h" + +#include <unistd.h> +#include "glib-unix.h" +#include <gio/gfiledescriptorbased.h> +#include <glib/gi18n-lib.h> + + +struct _GVfsDocumentInputStreamPrivate { + int fd; +}; + +static void g_file_descriptor_based_iface_init (GFileDescriptorBasedIface *iface); + +#define gvfs_document_input_stream_get_type _gvfs_document_input_stream_get_type +G_DEFINE_TYPE_WITH_CODE (GVfsDocumentInputStream, gvfs_document_input_stream, G_TYPE_FILE_INPUT_STREAM, + G_ADD_PRIVATE (GVfsDocumentInputStream) + G_IMPLEMENT_INTERFACE (G_TYPE_FILE_DESCRIPTOR_BASED, + g_file_descriptor_based_iface_init)) + +static gssize gvfs_document_input_stream_read (GInputStream *stream, + void *buffer, + gsize count, + GCancellable *cancellable, + GError **error); +static gssize gvfs_document_input_stream_skip (GInputStream *stream, + gsize count, + GCancellable *cancellable, + GError **error); +static gboolean gvfs_document_input_stream_close (GInputStream *stream, + GCancellable *cancellable, + GError **error); +static goffset gvfs_document_input_stream_tell (GFileInputStream *stream); +static gboolean gvfs_document_input_stream_can_seek (GFileInputStream *stream); +static gboolean gvfs_document_input_stream_seek (GFileInputStream *stream, + goffset offset, + GSeekType type, + GCancellable *cancellable, + GError **error); +static GFileInfo *gvfs_document_input_stream_query_info (GFileInputStream *stream, + const char *attributes, + GCancellable *cancellable, + GError **error); +static int gvfs_document_input_stream_get_fd (GFileDescriptorBased *stream); + +static void +gvfs_document_input_stream_class_init (GVfsDocumentInputStreamClass *klass) +{ + GInputStreamClass *stream_class = G_INPUT_STREAM_CLASS (klass); + GFileInputStreamClass *file_stream_class = G_FILE_INPUT_STREAM_CLASS (klass); + + stream_class->read_fn = gvfs_document_input_stream_read; + stream_class->skip = gvfs_document_input_stream_skip; + stream_class->close_fn = gvfs_document_input_stream_close; + file_stream_class->tell = gvfs_document_input_stream_tell; + file_stream_class->can_seek = gvfs_document_input_stream_can_seek; + file_stream_class->seek = gvfs_document_input_stream_seek; + file_stream_class->query_info = gvfs_document_input_stream_query_info; +} + +static void +g_file_descriptor_based_iface_init (GFileDescriptorBasedIface *iface) +{ + iface->get_fd = gvfs_document_input_stream_get_fd; +} + +static void +gvfs_document_input_stream_init (GVfsDocumentInputStream *info) +{ + info->priv = gvfs_document_input_stream_get_instance_private (info); +} + +GFileInputStream * +_gvfs_document_input_stream_new (int fd) +{ + GVfsDocumentInputStream *stream; + + stream = g_object_new (GVFS_TYPE_DOCUMENT_INPUT_STREAM, NULL); + stream->priv->fd = fd; + + return G_FILE_INPUT_STREAM (stream); +} + +static gssize +gvfs_document_input_stream_read (GInputStream *stream, + void *buffer, + gsize count, + GCancellable *cancellable, + GError **error) +{ + GVfsDocumentInputStream *file; + gssize res; + + file = GVFS_DOCUMENT_INPUT_STREAM (stream); + + res = -1; + while (1) + { + if (g_cancellable_set_error_if_cancelled (cancellable, error)) + break; + res = read (file->priv->fd, buffer, count); + if (res == -1) + { + int errsv = errno; + + if (errsv == EINTR) + continue; + + g_set_error (error, G_IO_ERROR, + g_io_error_from_errno (errsv), + _("Error reading from file: %s"), + g_strerror (errsv)); + } + + break; + } + + return res; +} + +static gssize +gvfs_document_input_stream_skip (GInputStream *stream, + gsize count, + GCancellable *cancellable, + GError **error) +{ + off_t start, end; + GVfsDocumentInputStream *file; + + file = GVFS_DOCUMENT_INPUT_STREAM (stream); + + if (g_cancellable_set_error_if_cancelled (cancellable, error)) + return -1; + + start = lseek (file->priv->fd, 0, SEEK_CUR); + if (start == -1) + { + int errsv = errno; + + g_set_error (error, G_IO_ERROR, + g_io_error_from_errno (errsv), + _("Error seeking in file: %s"), + g_strerror (errsv)); + return -1; + } + + end = lseek (file->priv->fd, 0, SEEK_END); + if (end == -1) + { + int errsv = errno; + + g_set_error (error, G_IO_ERROR, + g_io_error_from_errno (errsv), + _("Error seeking in file: %s"), + g_strerror (errsv)); + return -1; + } + + if (end - start > count) + { + end = lseek (file->priv->fd, count - (end - start), SEEK_CUR); + if (end == -1) + { + int errsv = errno; + + g_set_error (error, G_IO_ERROR, + g_io_error_from_errno (errsv), + _("Error seeking in file: %s"), + g_strerror (errsv)); + return -1; + } + } + + return end - start; +} + +static gboolean +gvfs_document_input_stream_close (GInputStream *stream, + GCancellable *cancellable, + GError **error) +{ + GVfsDocumentInputStream *file; + + file = GVFS_DOCUMENT_INPUT_STREAM (stream); + + if (!g_close (file->priv->fd, NULL)) + { + int errsv = errno; + + g_set_error (error, G_IO_ERROR, + g_io_error_from_errno (errsv), + _("Error closing file: %s"), + g_strerror (errsv)); + return FALSE; + } + + return TRUE; +} + + +static goffset +gvfs_document_input_stream_tell (GFileInputStream *stream) +{ + GVfsDocumentInputStream *file; + off_t pos; + + file = GVFS_DOCUMENT_INPUT_STREAM (stream); + + pos = lseek (file->priv->fd, 0, SEEK_CUR); + + if (pos == (off_t)-1) + return 0; + + return pos; +} + +static gboolean +gvfs_document_input_stream_can_seek (GFileInputStream *stream) +{ + GVfsDocumentInputStream *file; + off_t pos; + + file = GVFS_DOCUMENT_INPUT_STREAM (stream); + + pos = lseek (file->priv->fd, 0, SEEK_CUR); + + if (pos == (off_t)-1 && errno == ESPIPE) + return FALSE; + + return TRUE; +} + +static int +seek_type_to_lseek (GSeekType type) +{ + switch (type) + { + default: + case G_SEEK_CUR: + return SEEK_CUR; + + case G_SEEK_SET: + return SEEK_SET; + + case G_SEEK_END: + return SEEK_END; + } +} + +static gboolean +gvfs_document_input_stream_seek (GFileInputStream *stream, + goffset offset, + GSeekType type, + GCancellable *cancellable, + GError **error) +{ + GVfsDocumentInputStream *file; + off_t pos; + + file = GVFS_DOCUMENT_INPUT_STREAM (stream); + + pos = lseek (file->priv->fd, offset, seek_type_to_lseek (type)); + + if (pos == (off_t)-1) + { + int errsv = errno; + + g_set_error (error, G_IO_ERROR, + g_io_error_from_errno (errsv), + _("Error seeking in file: %s"), + g_strerror (errsv)); + return FALSE; + } + + return TRUE; +} + +static GFileInfo * +gvfs_document_input_stream_query_info (GFileInputStream *stream, + const char *attributes, + GCancellable *cancellable, + GError **error) +{ + GVfsDocumentInputStream *file; + GFileInfo *info; + + file = GVFS_DOCUMENT_INPUT_STREAM (stream); + + if (g_cancellable_set_error_if_cancelled (cancellable, error)) + return NULL; + + info = g_file_info_new (); + + return info; +} + +static int +gvfs_document_input_stream_get_fd (GFileDescriptorBased *fd_based) +{ + GVfsDocumentInputStream *stream = GVFS_DOCUMENT_INPUT_STREAM (fd_based); + return stream->priv->fd; +} diff --git a/client/gvfsdocumentinputstream.h b/client/gvfsdocumentinputstream.h new file mode 100644 index 00000000..f8488c4a --- /dev/null +++ b/client/gvfsdocumentinputstream.h @@ -0,0 +1,39 @@ + +#ifndef __GVFS_DOCUMENT_INPUT_STREAM_H__ +#define __GVFS_DOCUMENT_INPUT_STREAM_H__ + +#include <gio/gio.h> + +G_BEGIN_DECLS + +#define GVFS_TYPE_DOCUMENT_INPUT_STREAM (_gvfs_document_input_stream_get_type ()) +#define GVFS_DOCUMENT_INPUT_STREAM(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GVFS_TYPE_DOCUMENT_INPUT_STREAM, GVfsDocumentInputStream)) +#define GVFS_DOCUMENT_INPUT_STREAM_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GVFS_TYPE_DOCUMENT_INPUT_STREAM, GVfsDocumentInputStreamClass)) +#define GVFS_IS_DOCUMENT_INPUT_STREAM(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GVFS_TYPE_DOCUMENT_INPUT_STREAM)) +#define GVFS_IS_DOCUMENT_INPUT_STREAM_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GVFS_TYPE_DOCUMENT_INPUT_STREAM)) +#define GVFS_DOCUMENT_INPUT_STREAM_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GVFS_TYPE_DOCUMENT_INPUT_STREAM, GVfsDocumentInputStreamClass)) + +typedef struct _GVfsDocumentInputStream GVfsDocumentInputStream; +typedef struct _GVfsDocumentInputStreamClass GVfsDocumentInputStreamClass; +typedef struct _GVfsDocumentInputStreamPrivate GVfsDocumentInputStreamPrivate; + +struct _GVfsDocumentInputStream +{ + GFileInputStream parent_instance; + + /*< private >*/ + GVfsDocumentInputStreamPrivate *priv; +}; + +struct _GVfsDocumentInputStreamClass +{ + GFileInputStreamClass parent_class; +}; + +GType _gvfs_document_input_stream_get_type (void) G_GNUC_CONST; + +GFileInputStream *_gvfs_document_input_stream_new (int fd); + +G_END_DECLS + +#endif /* __GVFS_DOCUMENT_INPUT_STREAM_H__ */ diff --git a/client/gvfsdocumentoutputstream.c b/client/gvfsdocumentoutputstream.c new file mode 100644 index 00000000..ab30e15a --- /dev/null +++ b/client/gvfsdocumentoutputstream.c @@ -0,0 +1,426 @@ +/* GIO - GLib Input, Output and Streaming Library + * + * Copyright (C) 2006-2007 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General + * Public License along with this library; if not, see <http://www.gnu.org/licenses/>. + * + * Author: Alexander Larsson <alexl@redhat.com> + */ + +#include "config.h" + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include <string.h> + +#include <glib.h> +#include <glib/gstdio.h> +#include <glib/gi18n-lib.h> +#include <gio/gio.h> + +#include <unistd.h> +#include <gio/gfiledescriptorbased.h> + +#include "gvfsdocumentoutputstream.h" + +#ifndef O_BINARY +#define O_BINARY 0 +#endif + +struct _GVfsDocumentOutputStreamPrivate { + char *etag; + int fd; + guint32 id; + char *doc_handle; +}; + +static void g_file_descriptor_based_iface_init (GFileDescriptorBasedIface *iface); + +#define gvfs_document_output_stream_get_type _gvfs_document_output_stream_get_type +G_DEFINE_TYPE_WITH_CODE (GVfsDocumentOutputStream, gvfs_document_output_stream, G_TYPE_FILE_OUTPUT_STREAM, + G_ADD_PRIVATE (GVfsDocumentOutputStream) + G_IMPLEMENT_INTERFACE (G_TYPE_FILE_DESCRIPTOR_BASED, + g_file_descriptor_based_iface_init)) + + + +static gssize gvfs_document_output_stream_write (GOutputStream *stream, + const void *buffer, + gsize count, + GCancellable *cancellable, + GError **error); +static gboolean gvfs_document_output_stream_close (GOutputStream *stream, + GCancellable *cancellable, + GError **error); +static GFileInfo *gvfs_document_output_stream_query_info (GFileOutputStream *stream, + const char *attributes, + GCancellable *cancellable, + GError **error); +static char * gvfs_document_output_stream_get_etag (GFileOutputStream *stream); +static goffset gvfs_document_output_stream_tell (GFileOutputStream *stream); +static gboolean gvfs_document_output_stream_can_seek (GFileOutputStream *stream); +static gboolean gvfs_document_output_stream_seek (GFileOutputStream *stream, + goffset offset, + GSeekType type, + GCancellable *cancellable, + GError **error); +static gboolean gvfs_document_output_stream_can_truncate (GFileOutputStream *stream); +static gboolean gvfs_document_output_stream_truncate (GFileOutputStream *stream, + goffset size, + GCancellable *cancellable, + GError **error); +static int gvfs_document_output_stream_get_fd (GFileDescriptorBased *stream); + + +static void +gvfs_document_output_stream_finalize (GObject *object) +{ + GVfsDocumentOutputStream *file; + + file = GVFS_DOCUMENT_OUTPUT_STREAM (object); + + g_free (file->priv->doc_handle); + g_free (file->priv->etag); + + G_OBJECT_CLASS (gvfs_document_output_stream_parent_class)->finalize (object); +} + +static void +gvfs_document_output_stream_class_init (GVfsDocumentOutputStreamClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + GOutputStreamClass *stream_class = G_OUTPUT_STREAM_CLASS (klass); + GFileOutputStreamClass *file_stream_class = G_FILE_OUTPUT_STREAM_CLASS (klass); + + gobject_class->finalize = gvfs_document_output_stream_finalize; + + stream_class->write_fn = gvfs_document_output_stream_write; + stream_class->close_fn = gvfs_document_output_stream_close; + file_stream_class->query_info = gvfs_document_output_stream_query_info; + file_stream_class->get_etag = gvfs_document_output_stream_get_etag; + file_stream_class->tell = gvfs_document_output_stream_tell; + file_stream_class->can_seek = gvfs_document_output_stream_can_seek; + file_stream_class->seek = gvfs_document_output_stream_seek; + file_stream_class->can_truncate = gvfs_document_output_stream_can_truncate; + file_stream_class->truncate_fn = gvfs_document_output_stream_truncate; +} + +static void +g_file_descriptor_based_iface_init (GFileDescriptorBasedIface *iface) +{ + iface->get_fd = gvfs_document_output_stream_get_fd; +} + +static void +gvfs_document_output_stream_init (GVfsDocumentOutputStream *stream) +{ + stream->priv = gvfs_document_output_stream_get_instance_private (stream); +} + +static gssize +gvfs_document_output_stream_write (GOutputStream *stream, + const void *buffer, + gsize count, + GCancellable *cancellable, + GError **error) +{ + GVfsDocumentOutputStream *file; + gssize res; + + file = GVFS_DOCUMENT_OUTPUT_STREAM (stream); + + while (1) + { + if (g_cancellable_set_error_if_cancelled (cancellable, error)) + return -1; + res = write (file->priv->fd, buffer, count); + if (res == -1) + { + int errsv = errno; + + if (errsv == EINTR) + continue; + + g_set_error (error, G_IO_ERROR, + g_io_error_from_errno (errsv), + _("Error writing to file: %s"), + g_strerror (errsv)); + } + + break; + } + + return res; +} + +static GVariant * +sync_document_call (GVfsDocumentOutputStream *stream, + const char *method, + GVariant *parameters, + const GVariantType *reply_type, + GUnixFDList **out_fd_list, + GCancellable *cancellable, + GError **error) +{ + GVariant *res; + GDBusConnection *bus; + GUnixFDList *ret_fd_list; + GVariant *reply, *fd_v; + char *path; + int handle, fd; + + bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, error); + if (bus == NULL) + return NULL; + + path = g_build_filename ("/org/freedesktop/portal/document", stream->priv->doc_handle, NULL); + + res = g_dbus_connection_call_with_unix_fd_list_sync (bus, + "org.freedesktop.portal.DocumentPortal", + path, + "org.freedesktop.portal.Document", + method, + parameters, reply_type, + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + out_fd_list, + cancellable, + error); + + g_object_unref (bus); + return res; +} + +static gboolean +gvfs_document_output_stream_close (GOutputStream *stream, + GCancellable *cancellable, + GError **error) +{ + GVfsDocumentOutputStream *file; + gboolean failed; + GVariant *reply; + + file = GVFS_DOCUMENT_OUTPUT_STREAM (stream); + + failed = g_cancellable_set_error_if_cancelled (cancellable, error); + + /* Always close, even if cancelled */ + if (!g_close (file->priv->fd, NULL) && !failed) + { + int errsv = errno; + g_set_error (error, G_IO_ERROR, + g_io_error_from_errno (errsv), + _("Error closing file: %s"), + g_strerror (errsv)); + } + + if (failed) + { + reply = sync_document_call (file, "AbortUpdate", + g_variant_new ("(u)", file->priv->id), + G_VARIANT_TYPE("()"), + NULL, + NULL, NULL); + g_variant_unref (reply); + return FALSE; + } + + /* TODO: What if this is cancelled? Do we leak the update? */ + reply = sync_document_call (file, "FinishUpdate", + g_variant_new ("(u)", file->priv->id), + G_VARIANT_TYPE("()"), + NULL, + cancellable, error); + if (reply == NULL) + return FALSE; + + g_variant_unref (reply); + + return TRUE; +} + +static char * +gvfs_document_output_stream_get_etag (GFileOutputStream *stream) +{ + GVfsDocumentOutputStream *file; + + file = GVFS_DOCUMENT_OUTPUT_STREAM (stream); + + return g_strdup (file->priv->etag); +} + +static goffset +gvfs_document_output_stream_tell (GFileOutputStream *stream) +{ + GVfsDocumentOutputStream *file; + off_t pos; + + file = GVFS_DOCUMENT_OUTPUT_STREAM (stream); + + pos = lseek (file->priv->fd, 0, SEEK_CUR); + + if (pos == (off_t)-1) + return 0; + + return pos; +} + +static gboolean +gvfs_document_output_stream_can_seek (GFileOutputStream *stream) +{ + GVfsDocumentOutputStream *file; + off_t pos; + + file = GVFS_DOCUMENT_OUTPUT_STREAM (stream); + + pos = lseek (file->priv->fd, 0, SEEK_CUR); + + if (pos == (off_t)-1 && errno == ESPIPE) + return FALSE; + + return TRUE; +} + +static int +seek_type_to_lseek (GSeekType type) +{ + switch (type) + { + default: + case G_SEEK_CUR: + return SEEK_CUR; + + case G_SEEK_SET: + return SEEK_SET; + + case G_SEEK_END: + return SEEK_END; + } +} + +static gboolean +gvfs_document_output_stream_seek (GFileOutputStream *stream, + goffset offset, + GSeekType type, + GCancellable *cancellable, + GError **error) +{ + GVfsDocumentOutputStream *file; + off_t pos; + + file = GVFS_DOCUMENT_OUTPUT_STREAM (stream); + + pos = lseek (file->priv->fd, offset, seek_type_to_lseek (type)); + + if (pos == (off_t)-1) + { + int errsv = errno; + + g_set_error (error, G_IO_ERROR, + g_io_error_from_errno (errsv), + _("Error seeking in file: %s"), + g_strerror (errsv)); + return FALSE; + } + + return TRUE; +} + +static gboolean +gvfs_document_output_stream_can_truncate (GFileOutputStream *stream) +{ + /* We can't truncate pipes and stuff where we can't seek */ + return gvfs_document_output_stream_can_seek (stream); +} + +static gboolean +gvfs_document_output_stream_truncate (GFileOutputStream *stream, + goffset size, + GCancellable *cancellable, + GError **error) +{ + GVfsDocumentOutputStream *file; + int res; + + file = GVFS_DOCUMENT_OUTPUT_STREAM (stream); + + restart: + res = ftruncate (file->priv->fd, size); + + if (res == -1) + { + int errsv = errno; + + if (errsv == EINTR) + { + if (g_cancellable_set_error_if_cancelled (cancellable, error)) + return FALSE; + goto restart; + } + + g_set_error (error, G_IO_ERROR, + g_io_error_from_errno (errsv), + _("Error truncating file: %s"), + g_strerror (errsv)); + return FALSE; + } + + return TRUE; +} + + +static GFileInfo * +gvfs_document_output_stream_query_info (GFileOutputStream *stream, + const char *attributes, + GCancellable *cancellable, + GError **error) +{ + GVfsDocumentOutputStream *file; + GFileInfo *info; + + file = GVFS_DOCUMENT_OUTPUT_STREAM (stream); + + if (g_cancellable_set_error_if_cancelled (cancellable, error)) + return NULL; + + info = g_file_info_new (); + + return info; +} + +GFileOutputStream * +gvfs_document_output_stream_new (const char *handle, + guint32 id, + int fd) +{ + GVfsDocumentOutputStream *stream; + + stream = g_object_new (GVFS_TYPE_DOCUMENT_OUTPUT_STREAM, NULL); + stream->priv->doc_handle = g_strdup (handle); + stream->priv->id = id; + stream->priv->fd = fd; + + return G_FILE_OUTPUT_STREAM (stream); +} + +static int +gvfs_document_output_stream_get_fd (GFileDescriptorBased *fd_based) +{ + GVfsDocumentOutputStream *stream = GVFS_DOCUMENT_OUTPUT_STREAM (fd_based); + + return stream->priv->fd; +} diff --git a/client/gvfsdocumentoutputstream.h b/client/gvfsdocumentoutputstream.h new file mode 100644 index 00000000..f8b9248b --- /dev/null +++ b/client/gvfsdocumentoutputstream.h @@ -0,0 +1,47 @@ + +#ifndef __GVFS_DOCUMENT_OUTPUT_STREAM_H__ +#define __GVFS_DOCUMENT_OUTPUT_STREAM_H__ + +#include <gio/gfileoutputstream.h> + +G_BEGIN_DECLS + +#define GVFS_TYPE_DOCUMENT_OUTPUT_STREAM (_gvfs_document_output_stream_get_type ()) +#define GVFS_DOCUMENT_OUTPUT_STREAM(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GVFS_TYPE_DOCUMENT_OUTPUT_STREAM, GVfsDocumentOutputStream)) +#define GVFS_DOCUMENT_OUTPUT_STREAM_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GVFS_TYPE_DOCUMENT_OUTPUT_STREAM, GVfsDocumentOutputStreamClass)) +#define GVFS_IS_DOCUMENT_OUTPUT_STREAM(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GVFS_TYPE_DOCUMENT_OUTPUT_STREAM)) +#define GVFS_IS_DOCUMENT_OUTPUT_STREAM_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GVFS_TYPE_DOCUMENT_OUTPUT_STREAM)) +#define GVFS_DOCUMENT_OUTPUT_STREAM_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GVFS_TYPE_DOCUMENT_OUTPUT_STREAM, GVfsDocumentOutputStreamClass)) + +typedef struct _GVfsDocumentOutputStream GVfsDocumentOutputStream; +typedef struct _GVfsDocumentOutputStreamClass GVfsDocumentOutputStreamClass; +typedef struct _GVfsDocumentOutputStreamPrivate GVfsDocumentOutputStreamPrivate; + +struct _GVfsDocumentOutputStream +{ + GFileOutputStream parent_instance; + + /*< private >*/ + GVfsDocumentOutputStreamPrivate *priv; +}; + +struct _GVfsDocumentOutputStreamClass +{ + GFileOutputStreamClass parent_class; +}; + +GType gvfs_document_output_stream_get_type (void) G_GNUC_CONST; + +void gvfs_document_output_stream_set_do_close (GVfsDocumentOutputStream *out, + gboolean do_close); +gboolean gvfs_document_output_stream_really_close (GVfsDocumentOutputStream *out, + GCancellable *cancellable, + GError **error); + +GFileOutputStream * gvfs_document_output_stream_new (const char *handle, + guint32 id, + int fd); + +G_END_DECLS + +#endif /* __GVFS_DOCUMENT_OUTPUT_STREAM_H__ */ |