/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ /* camel-stream-fs.c : file system based stream * * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) * * 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. * * 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 . * * Authors: Bertrand Guiheneuf * Michael Zucchi */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include "camel-file-utils.h" #include "camel-operation.h" #include "camel-stream-fs.h" #include "camel-win32.h" #define CAMEL_STREAM_FS_GET_PRIVATE(obj) \ (G_TYPE_INSTANCE_GET_PRIVATE \ ((obj), CAMEL_TYPE_STREAM_FS, CamelStreamFsPrivate)) struct _CamelStreamFsPrivate { gint fd; /* file descriptor on the underlying file */ }; /* Forward Declarations */ static void camel_stream_fs_seekable_init (GSeekableIface *iface); G_DEFINE_TYPE_WITH_CODE ( CamelStreamFs, camel_stream_fs, CAMEL_TYPE_STREAM, G_IMPLEMENT_INTERFACE (G_TYPE_SEEKABLE, camel_stream_fs_seekable_init)) static void stream_fs_finalize (GObject *object) { CamelStreamFsPrivate *priv; priv = CAMEL_STREAM_FS_GET_PRIVATE (object); if (priv->fd != -1) close (priv->fd); /* Chain up to parent's finalize() method. */ G_OBJECT_CLASS (camel_stream_fs_parent_class)->finalize (object); } static gssize stream_fs_read (CamelStream *stream, gchar *buffer, gsize n, GCancellable *cancellable, GError **error) { CamelStreamFsPrivate *priv; gssize nread; priv = CAMEL_STREAM_FS_GET_PRIVATE (stream); nread = camel_read (priv->fd, buffer, n, cancellable, error); if (nread == 0) stream->eos = TRUE; return nread; } static gssize stream_fs_write (CamelStream *stream, const gchar *buffer, gsize n, GCancellable *cancellable, GError **error) { CamelStreamFsPrivate *priv; priv = CAMEL_STREAM_FS_GET_PRIVATE (stream); return camel_write (priv->fd, buffer, n, cancellable, error); } static gint stream_fs_flush (CamelStream *stream, GCancellable *cancellable, GError **error) { CamelStreamFsPrivate *priv; priv = CAMEL_STREAM_FS_GET_PRIVATE (stream); if (g_cancellable_set_error_if_cancelled (cancellable, error)) return -1; if (fsync (priv->fd) == -1) { g_set_error ( error, G_IO_ERROR, g_io_error_from_errno (errno), "%s", g_strerror (errno)); return -1; } return 0; } static gint stream_fs_close (CamelStream *stream, GCancellable *cancellable, GError **error) { CamelStreamFsPrivate *priv; priv = CAMEL_STREAM_FS_GET_PRIVATE (stream); if (g_cancellable_set_error_if_cancelled (cancellable, error)) return -1; if (close (priv->fd) == -1) { g_set_error ( error, G_IO_ERROR, g_io_error_from_errno (errno), "%s", g_strerror (errno)); return -1; } priv->fd = -1; return 0; } static goffset stream_fs_tell (GSeekable *seekable) { CamelStreamFsPrivate *priv; priv = CAMEL_STREAM_FS_GET_PRIVATE (seekable); return (goffset) lseek (priv->fd, 0, SEEK_CUR); } static gboolean stream_fs_can_seek (GSeekable *seekable) { return TRUE; } static gboolean stream_fs_seek (GSeekable *seekable, goffset offset, GSeekType type, GCancellable *cancellable, GError **error) { CamelStreamFsPrivate *priv; goffset real = 0; priv = CAMEL_STREAM_FS_GET_PRIVATE (seekable); switch (type) { case G_SEEK_SET: real = offset; break; case G_SEEK_CUR: real = g_seekable_tell (seekable) + offset; break; case G_SEEK_END: real = lseek (priv->fd, offset, SEEK_END); if (real == -1) { g_set_error ( error, G_IO_ERROR, g_io_error_from_errno (errno), "%s", g_strerror (errno)); return FALSE; } return TRUE; } real = lseek (priv->fd, real, SEEK_SET); if (real == -1) { g_set_error ( error, G_IO_ERROR, g_io_error_from_errno (errno), "%s", g_strerror (errno)); return FALSE; } CAMEL_STREAM (seekable)->eos = FALSE; return TRUE; } static gboolean stream_fs_can_truncate (GSeekable *seekable) { return FALSE; } static gboolean stream_fs_truncate_fn (GSeekable *seekable, goffset offset, GCancellable *cancellable, GError **error) { /* XXX Don't bother translating this. Camel never calls it. */ g_set_error ( error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, "Truncation is not supported"); return FALSE; } static void camel_stream_fs_class_init (CamelStreamFsClass *class) { GObjectClass *object_class; CamelStreamClass *stream_class; g_type_class_add_private (class, sizeof (CamelStreamFsPrivate)); object_class = G_OBJECT_CLASS (class); object_class->finalize = stream_fs_finalize; stream_class = CAMEL_STREAM_CLASS (class); stream_class->read = stream_fs_read; stream_class->write = stream_fs_write; stream_class->flush = stream_fs_flush; stream_class->close = stream_fs_close; } static void camel_stream_fs_seekable_init (GSeekableIface *iface) { iface->tell = stream_fs_tell; iface->can_seek = stream_fs_can_seek; iface->seek = stream_fs_seek; iface->can_truncate = stream_fs_can_truncate; iface->truncate_fn = stream_fs_truncate_fn; } static void camel_stream_fs_init (CamelStreamFs *stream) { stream->priv = CAMEL_STREAM_FS_GET_PRIVATE (stream); stream->priv->fd = -1; } /** * camel_stream_fs_new_with_fd: * @fd: a file descriptor * * Creates a new fs stream using the given file descriptor @fd as the * backing store. When the stream is destroyed, the file descriptor * will be closed. * * Returns: a new #CamelStreamFs **/ CamelStream * camel_stream_fs_new_with_fd (gint fd) { CamelStreamFsPrivate *priv; CamelStream *stream; if (fd == -1) return NULL; stream = g_object_new (CAMEL_TYPE_STREAM_FS, NULL); priv = CAMEL_STREAM_FS_GET_PRIVATE (stream); priv->fd = fd; return stream; } /** * camel_stream_fs_new_with_name: * @name: a local filename * @flags: flags as in open(2) * @mode: a file mode * @error: return location for a #GError, or %NULL * * Creates a new #CamelStreamFs corresponding to the named file, flags, * and mode. * * Returns: the new stream, or %NULL on error. **/ CamelStream * camel_stream_fs_new_with_name (const gchar *name, gint flags, mode_t mode, GError **error) { gint fd; fd = g_open (name, flags | O_BINARY, mode); if (fd == -1) { g_set_error ( error, G_IO_ERROR, g_io_error_from_errno (errno), "%s", g_strerror (errno)); return NULL; } return camel_stream_fs_new_with_fd (fd); } /** * camel_stream_fs_get_fd: * @stream: a #CamelStream * * Since: 2.32 **/ gint camel_stream_fs_get_fd (CamelStreamFs *stream) { g_return_val_if_fail (CAMEL_IS_STREAM_FS (stream), -1); return stream->priv->fd; }