diff --git a/gio/Makefile.am b/gio/Makefile.am index 76e402dc4..8481a7aed 100644 --- a/gio/Makefile.am +++ b/gio/Makefile.am @@ -89,6 +89,8 @@ local_sources = \ glocalfilemonitor.h \ glocalfileoutputstream.c \ glocalfileoutputstream.h \ + glocalfileiostream.c \ + glocalfileiostream.h \ glocalvfs.c \ glocalvfs.h \ $(NULL) diff --git a/gio/glocalfile.c b/gio/glocalfile.c index efb841787..8ac60144d 100644 --- a/gio/glocalfile.c +++ b/gio/glocalfile.c @@ -78,6 +78,7 @@ #include "glocalfileenumerator.h" #include "glocalfileinputstream.h" #include "glocalfileoutputstream.h" +#include "glocalfileiostream.h" #include "glocaldirectorymonitor.h" #include "glocalfilemonitor.h" #include "gmountprivate.h" @@ -1322,6 +1323,7 @@ g_local_file_create (GFile *file, GError **error) { return _g_local_file_output_stream_create (G_LOCAL_FILE (file)->filename, + FALSE, flags, cancellable, error); } @@ -1334,10 +1336,72 @@ g_local_file_replace (GFile *file, GError **error) { return _g_local_file_output_stream_replace (G_LOCAL_FILE (file)->filename, + FALSE, etag, make_backup, flags, cancellable, error); } +static GFileIOStream * +g_local_file_open_readwrite (GFile *file, + GCancellable *cancellable, + GError **error) +{ + GFileOutputStream *output; + GFileIOStream *res; + + output = _g_local_file_output_stream_open (G_LOCAL_FILE (file)->filename, + TRUE, + cancellable, error); + if (output == NULL) + return NULL; + + res = _g_local_file_io_stream_new (G_LOCAL_FILE_OUTPUT_STREAM (output)); + g_object_unref (output); + return res; +} + +static GFileIOStream * +g_local_file_create_readwrite (GFile *file, + GFileCreateFlags flags, + GCancellable *cancellable, + GError **error) +{ + GFileOutputStream *output; + GFileIOStream *res; + + output = _g_local_file_output_stream_create (G_LOCAL_FILE (file)->filename, + TRUE, flags, + cancellable, error); + if (output == NULL) + return NULL; + + res = _g_local_file_io_stream_new (G_LOCAL_FILE_OUTPUT_STREAM (output)); + g_object_unref (output); + return res; +} + +static GFileIOStream * +g_local_file_replace_readwrite (GFile *file, + const char *etag, + gboolean make_backup, + GFileCreateFlags flags, + GCancellable *cancellable, + GError **error) +{ + GFileOutputStream *output; + GFileIOStream *res; + + output = _g_local_file_output_stream_replace (G_LOCAL_FILE (file)->filename, + TRUE, + etag, make_backup, flags, + cancellable, error); + if (output == NULL) + return NULL; + + res = _g_local_file_io_stream_new (G_LOCAL_FILE_OUTPUT_STREAM (output)); + g_object_unref (output); + return res; +} static gboolean g_local_file_delete (GFile *file, @@ -2288,6 +2352,9 @@ g_local_file_file_iface_init (GFileIface *iface) iface->append_to = g_local_file_append_to; iface->create = g_local_file_create; iface->replace = g_local_file_replace; + iface->open_readwrite = g_local_file_open_readwrite; + iface->create_readwrite = g_local_file_create_readwrite; + iface->replace_readwrite = g_local_file_replace_readwrite; iface->delete_file = g_local_file_delete; iface->trash = g_local_file_trash; iface->make_directory = g_local_file_make_directory; diff --git a/gio/glocalfileinputstream.c b/gio/glocalfileinputstream.c index a8d0104f5..544c568d8 100644 --- a/gio/glocalfileinputstream.c +++ b/gio/glocalfileinputstream.c @@ -49,6 +49,7 @@ G_DEFINE_TYPE (GLocalFileInputStream, g_local_file_input_stream, G_TYPE_FILE_INP struct _GLocalFileInputStreamPrivate { int fd; + guint do_close : 1; }; static gssize g_local_file_input_stream_read (GInputStream *stream, @@ -85,6 +86,13 @@ g_local_file_input_stream_finalize (GObject *object) G_OBJECT_CLASS (g_local_file_input_stream_parent_class)->finalize (object); } +void +_g_local_file_input_stream_set_do_close (GLocalFileInputStream *in, + gboolean do_close) +{ + in->priv->do_close = do_close; +} + static void g_local_file_input_stream_class_init (GLocalFileInputStreamClass *klass) { @@ -111,6 +119,7 @@ g_local_file_input_stream_init (GLocalFileInputStream *info) info->priv = G_TYPE_INSTANCE_GET_PRIVATE (info, G_TYPE_LOCAL_FILE_INPUT_STREAM, GLocalFileInputStreamPrivate); + info->priv->do_close = TRUE; } /** @@ -218,6 +227,9 @@ g_local_file_input_stream_close (GInputStream *stream, file = G_LOCAL_FILE_INPUT_STREAM (stream); + if (!file->priv->do_close) + return TRUE; + if (file->priv->fd == -1) return TRUE; diff --git a/gio/glocalfileinputstream.h b/gio/glocalfileinputstream.h index 29740d6c9..28f28bf7f 100644 --- a/gio/glocalfileinputstream.h +++ b/gio/glocalfileinputstream.h @@ -53,7 +53,10 @@ struct _GLocalFileInputStreamClass GType _g_local_file_input_stream_get_type (void) G_GNUC_CONST; -GFileInputStream * _g_local_file_input_stream_new (int fd); +GFileInputStream *_g_local_file_input_stream_new (int fd); +void _g_local_file_input_stream_set_do_close (GLocalFileInputStream *in, + gboolean do_close); + G_END_DECLS diff --git a/gio/glocalfileiostream.c b/gio/glocalfileiostream.c new file mode 100644 index 000000000..cb080616c --- /dev/null +++ b/gio/glocalfileiostream.c @@ -0,0 +1,114 @@ +/* GIO - GLib Input, IO 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., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + * + * Author: Alexander Larsson + */ + +#include "config.h" + +#include +#include +#include "glibintl.h" +#include "gioerror.h" +#include "gcancellable.h" +#include "glocalfileiostream.h" +#include "glocalfileinputstream.h" +#include "glocalfileinfo.h" + +#include "gioalias.h" + +#define g_local_file_io_stream_get_type _g_local_file_io_stream_get_type +G_DEFINE_TYPE (GLocalFileIOStream, g_local_file_io_stream, G_TYPE_FILE_IO_STREAM); + +static void +g_local_file_io_stream_finalize (GObject *object) +{ + GLocalFileIOStream *file; + + file = G_LOCAL_FILE_IO_STREAM (object); + + g_object_unref (file->input_stream); + g_object_unref (file->output_stream); + + G_OBJECT_CLASS (g_local_file_io_stream_parent_class)->finalize (object); +} + +GFileIOStream * +_g_local_file_io_stream_new (GLocalFileOutputStream *output_stream) +{ + GLocalFileIOStream *stream; + int fd; + + stream = g_object_new (G_TYPE_LOCAL_FILE_IO_STREAM, NULL); + stream->output_stream = g_object_ref (output_stream); + _g_local_file_output_stream_set_do_close (output_stream, FALSE); + fd = _g_local_file_output_stream_get_fd (output_stream); + stream->input_stream = (GInputStream *)_g_local_file_input_stream_new (fd); + _g_local_file_input_stream_set_do_close (G_LOCAL_FILE_INPUT_STREAM (stream->input_stream), + FALSE); + + return G_FILE_IO_STREAM (stream); +} + +static GInputStream * +g_local_file_io_stream_get_input_stream (GIOStream *stream) +{ + return G_LOCAL_FILE_IO_STREAM (stream)->input_stream; +} + +static GOutputStream * +g_local_file_io_stream_get_output_stream (GIOStream *stream) +{ + return G_LOCAL_FILE_IO_STREAM (stream)->output_stream; +} + + +static gboolean +g_local_file_io_stream_close (GIOStream *stream, + GCancellable *cancellable, + GError **error) +{ + GLocalFileIOStream *file = G_LOCAL_FILE_IO_STREAM (stream); + + /* There are shortcutted and can't fail */ + g_output_stream_close (file->output_stream, cancellable, NULL); + g_input_stream_close (file->input_stream, cancellable, NULL); + + return + _g_local_file_output_stream_really_close (G_LOCAL_FILE_OUTPUT_STREAM (file->output_stream), + cancellable, error); +} + +static void +g_local_file_io_stream_class_init (GLocalFileIOStreamClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + GIOStreamClass *stream_class = G_IO_STREAM_CLASS (klass); + + gobject_class->finalize = g_local_file_io_stream_finalize; + + stream_class->get_input_stream = g_local_file_io_stream_get_input_stream; + stream_class->get_output_stream = g_local_file_io_stream_get_output_stream; + stream_class->close_fn = g_local_file_io_stream_close; +} + +static void +g_local_file_io_stream_init (GLocalFileIOStream *stream) +{ +} diff --git a/gio/glocalfileiostream.h b/gio/glocalfileiostream.h new file mode 100644 index 000000000..be7c7863f --- /dev/null +++ b/gio/glocalfileiostream.h @@ -0,0 +1,60 @@ +/* GIO - GLib Input, Output and Streaming Library + * + * Copyright (C) 2006-2009 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., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + * + * Author: Alexander Larsson + */ + +#ifndef __G_LOCAL_FILE_IO_STREAM_H__ +#define __G_LOCAL_FILE_IO_STREAM_H__ + +#include +#include "glocalfileoutputstream.h" + +G_BEGIN_DECLS + +#define G_TYPE_LOCAL_FILE_IO_STREAM (_g_local_file_io_stream_get_type ()) +#define G_LOCAL_FILE_IO_STREAM(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_LOCAL_FILE_IO_STREAM, GLocalFileIOStream)) +#define G_LOCAL_FILE_IO_STREAM_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_LOCAL_FILE_IO_STREAM, GLocalFileIOStreamClass)) +#define G_IS_LOCAL_FILE_IO_STREAM(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_LOCAL_FILE_IO_STREAM)) +#define G_IS_LOCAL_FILE_IO_STREAM_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_LOCAL_FILE_IO_STREAM)) +#define G_LOCAL_FILE_IO_STREAM_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_LOCAL_FILE_IO_STREAM, GLocalFileIOStreamClass)) + +typedef struct _GLocalFileIOStream GLocalFileIOStream; +typedef struct _GLocalFileIOStreamClass GLocalFileIOStreamClass; +typedef struct _GLocalFileIOStreamPrivate GLocalFileIOStreamPrivate; + +struct _GLocalFileIOStream +{ + GFileIOStream parent_instance; + + GInputStream *input_stream; + GOutputStream *output_stream; +}; + +struct _GLocalFileIOStreamClass +{ + GFileIOStreamClass parent_class; +}; + +GType _g_local_file_io_stream_get_type (void) G_GNUC_CONST; +GFileIOStream * _g_local_file_io_stream_new (GLocalFileOutputStream *output_stream); + +G_END_DECLS + +#endif /* __G_LOCAL_FILE_IO_STREAM_H__ */ diff --git a/gio/glocalfileoutputstream.c b/gio/glocalfileoutputstream.c index 7c73da481..cb978314b 100644 --- a/gio/glocalfileoutputstream.c +++ b/gio/glocalfileoutputstream.c @@ -69,7 +69,8 @@ struct _GLocalFileOutputStreamPrivate { char *original_filename; char *backup_filename; char *etag; - gboolean sync_on_close; + guint sync_on_close : 1; + guint do_close : 1; int fd; }; @@ -142,6 +143,13 @@ g_local_file_output_stream_init (GLocalFileOutputStream *stream) stream->priv = G_TYPE_INSTANCE_GET_PRIVATE (stream, G_TYPE_LOCAL_FILE_OUTPUT_STREAM, GLocalFileOutputStreamPrivate); + stream->priv->do_close = TRUE; +} + +int +_g_local_file_output_stream_get_fd (GLocalFileOutputStream *out) +{ + return out->priv->fd; } static gssize @@ -180,17 +188,21 @@ g_local_file_output_stream_write (GOutputStream *stream, return res; } -static gboolean -g_local_file_output_stream_close (GOutputStream *stream, - GCancellable *cancellable, - GError **error) +void +_g_local_file_output_stream_set_do_close (GLocalFileOutputStream *out, + gboolean do_close) +{ + out->priv->do_close = do_close; +} + +gboolean +_g_local_file_output_stream_really_close (GLocalFileOutputStream *file, + GCancellable *cancellable, + GError **error) { - GLocalFileOutputStream *file; GLocalFileStat final_stat; int res; - file = G_LOCAL_FILE_OUTPUT_STREAM (stream); - #ifdef HAVE_FSYNC if (file->priv->sync_on_close && fsync (file->priv->fd) != 0) @@ -338,6 +350,23 @@ g_local_file_output_stream_close (GOutputStream *stream, return FALSE; } + +static gboolean +g_local_file_output_stream_close (GOutputStream *stream, + GCancellable *cancellable, + GError **error) +{ + GLocalFileOutputStream *file; + + file = G_LOCAL_FILE_OUTPUT_STREAM (stream); + + if (file->priv->do_close) + return _g_local_file_output_stream_really_close (file, + cancellable, + error); + return TRUE; +} + static char * g_local_file_output_stream_get_etag (GFileOutputStream *stream) { @@ -490,8 +519,56 @@ g_local_file_output_stream_query_info (GFileOutputStream *stream, error); } +GFileOutputStream * +_g_local_file_output_stream_open (const char *filename, + gboolean readable, + GCancellable *cancellable, + GError **error) +{ + GLocalFileOutputStream *stream; + int mode; + int fd; + int open_flags; + + if (g_cancellable_set_error_if_cancelled (cancellable, error)) + return NULL; + + open_flags = O_BINARY; + if (readable) + open_flags |= O_RDWR; + else + open_flags |= O_WRONLY; + + fd = g_open (filename, open_flags, 0666); + if (fd == -1) + { + int errsv = errno; + + if (errsv == EINVAL) + /* This must be an invalid filename, on e.g. FAT */ + g_set_error_literal (error, G_IO_ERROR, + G_IO_ERROR_INVALID_FILENAME, + _("Invalid filename")); + else + { + char *display_name = g_filename_display_name (filename); + g_set_error (error, G_IO_ERROR, + g_io_error_from_errno (errsv), + _("Error opening file '%s': %s"), + display_name, g_strerror (errsv)); + g_free (display_name); + } + return NULL; + } + + stream = g_object_new (G_TYPE_LOCAL_FILE_OUTPUT_STREAM, NULL); + stream->priv->fd = fd; + return G_FILE_OUTPUT_STREAM (stream); +} + GFileOutputStream * _g_local_file_output_stream_create (const char *filename, + gboolean readable, GFileCreateFlags flags, GCancellable *cancellable, GError **error) @@ -499,6 +576,7 @@ _g_local_file_output_stream_create (const char *filename, GLocalFileOutputStream *stream; int mode; int fd; + int open_flags; if (g_cancellable_set_error_if_cancelled (cancellable, error)) return NULL; @@ -507,8 +585,14 @@ _g_local_file_output_stream_create (const char *filename, mode = 0600; else mode = 0666; - - fd = g_open (filename, O_CREAT | O_EXCL | O_WRONLY | O_BINARY, mode); + + open_flags = O_CREAT | O_EXCL | O_BINARY; + if (readable) + open_flags |= O_RDWR; + else + open_flags |= O_WRONLY; + + fd = g_open (filename, open_flags, mode); if (fd == -1) { int errsv = errno; @@ -656,6 +740,7 @@ copy_file_data (gint sfd, static int handle_overwrite_open (const char *filename, + gboolean readable, const char *etag, gboolean create_backup, char **temp_filename, @@ -678,7 +763,7 @@ handle_overwrite_open (const char *filename, /* We only need read access to the original file if we are creating a backup. * We also add O_CREATE to avoid a race if the file was just removed */ - if (create_backup) + if (create_backup || readable) open_flags = O_RDWR | O_CREAT | O_BINARY; else open_flags = O_WRONLY | O_CREAT | O_BINARY; @@ -938,8 +1023,12 @@ handle_overwrite_open (const char *filename, g_strerror (errsv)); goto err_out2; } - - fd = g_open (filename, O_WRONLY | O_CREAT | O_BINARY, mode); + + if (readable) + open_flags = O_RDWR | O_CREAT | O_BINARY; + else + open_flags = O_WRONLY | O_CREAT | O_BINARY; + fd = g_open (filename, open_flags, mode); if (fd == -1) { int errsv = errno; @@ -981,6 +1070,7 @@ handle_overwrite_open (const char *filename, GFileOutputStream * _g_local_file_output_stream_replace (const char *filename, + gboolean readable, const char *etag, gboolean create_backup, GFileCreateFlags flags, @@ -992,6 +1082,7 @@ _g_local_file_output_stream_replace (const char *filename, int fd; char *temp_file; gboolean sync_on_close; + int open_flags; if (g_cancellable_set_error_if_cancelled (cancellable, error)) return NULL; @@ -1005,12 +1096,18 @@ _g_local_file_output_stream_replace (const char *filename, sync_on_close = FALSE; /* If the file doesn't exist, create it */ - fd = g_open (filename, O_CREAT | O_EXCL | O_WRONLY | O_BINARY, mode); + open_flags = O_CREAT | O_EXCL | O_BINARY; + if (readable) + open_flags |= O_RDWR; + else + open_flags |= O_WRONLY; + fd = g_open (filename, open_flags, mode); if (fd == -1 && errno == EEXIST) { /* The file already exists */ - fd = handle_overwrite_open (filename, etag, create_backup, &temp_file, + fd = handle_overwrite_open (filename, readable, etag, + create_backup, &temp_file, flags, cancellable, error); if (fd == -1) return NULL; diff --git a/gio/glocalfileoutputstream.h b/gio/glocalfileoutputstream.h index 3606f658f..5f08d1365 100644 --- a/gio/glocalfileoutputstream.h +++ b/gio/glocalfileoutputstream.h @@ -52,7 +52,20 @@ struct _GLocalFileOutputStreamClass }; GType _g_local_file_output_stream_get_type (void) G_GNUC_CONST; + +int _g_local_file_output_stream_get_fd (GLocalFileOutputStream *out); +void _g_local_file_output_stream_set_do_close (GLocalFileOutputStream *out, + gboolean do_close); +gboolean _g_local_file_output_stream_really_close (GLocalFileOutputStream *out, + GCancellable *cancellable, + GError **error); + +GFileOutputStream * _g_local_file_output_stream_open (const char *filename, + gboolean readable, + GCancellable *cancellable, + GError **error); GFileOutputStream * _g_local_file_output_stream_create (const char *filename, + gboolean readable, GFileCreateFlags flags, GCancellable *cancellable, GError **error); @@ -61,6 +74,7 @@ GFileOutputStream * _g_local_file_output_stream_append (const char *file GCancellable *cancellable, GError **error); GFileOutputStream * _g_local_file_output_stream_replace (const char *filename, + gboolean readable, const char *etag, gboolean create_backup, GFileCreateFlags flags,