| /* GIO - GLib Input, Output and Streaming Library | |
| * | |
| * Copyright (C) 2006-2007 Red Hat, Inc. | |
| * | |
| * SPDX-License-Identifier: LGPL-2.1-or-later | |
| * | |
| * 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.1 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 <glib.h> | |
| #include <glib/gstdio.h> | |
| #include "gcancellable.h" | |
| #include "gioerror.h" | |
| #include "glocalfileinputstream.h" | |
| #include "glocalfileinfo.h" | |
| #include "glibintl.h" | |
| #ifdef G_OS_UNIX | |
| #include <unistd.h> | |
| #include "glib-unix.h" | |
| #include "gfiledescriptorbased.h" | |
| #endif | |
| #ifdef G_OS_WIN32 | |
| #include <io.h> | |
| #endif | |
| struct _GLocalFileInputStreamPrivate { | |
| int fd; | |
| guint do_close : 1; | |
| }; | |
| #ifdef G_OS_UNIX | |
| static void g_file_descriptor_based_iface_init (GFileDescriptorBasedIface *iface); | |
| #endif | |
| #define g_local_file_input_stream_get_type _g_local_file_input_stream_get_type | |
| #ifdef G_OS_UNIX | |
| G_DEFINE_TYPE_WITH_CODE (GLocalFileInputStream, g_local_file_input_stream, G_TYPE_FILE_INPUT_STREAM, | |
| G_ADD_PRIVATE (GLocalFileInputStream) | |
| G_IMPLEMENT_INTERFACE (G_TYPE_FILE_DESCRIPTOR_BASED, | |
| g_file_descriptor_based_iface_init)) | |
| #else | |
| G_DEFINE_TYPE_WITH_CODE (GLocalFileInputStream, g_local_file_input_stream, G_TYPE_FILE_INPUT_STREAM, | |
| G_ADD_PRIVATE (GLocalFileInputStream)) | |
| #endif | |
| static gssize g_local_file_input_stream_read (GInputStream *stream, | |
| void *buffer, | |
| gsize count, | |
| GCancellable *cancellable, | |
| GError **error); | |
| static gboolean g_local_file_input_stream_close (GInputStream *stream, | |
| GCancellable *cancellable, | |
| GError **error); | |
| static goffset g_local_file_input_stream_tell (GFileInputStream *stream); | |
| static gboolean g_local_file_input_stream_can_seek (GFileInputStream *stream); | |
| static gboolean g_local_file_input_stream_seek (GFileInputStream *stream, | |
| goffset offset, | |
| GSeekType type, | |
| GCancellable *cancellable, | |
| GError **error); | |
| static GFileInfo *g_local_file_input_stream_query_info (GFileInputStream *stream, | |
| const char *attributes, | |
| GCancellable *cancellable, | |
| GError **error); | |
| #ifdef G_OS_UNIX | |
| static int g_local_file_input_stream_get_fd (GFileDescriptorBased *stream); | |
| #endif | |
| 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) | |
| { | |
| GInputStreamClass *stream_class = G_INPUT_STREAM_CLASS (klass); | |
| GFileInputStreamClass *file_stream_class = G_FILE_INPUT_STREAM_CLASS (klass); | |
| stream_class->read_fn = g_local_file_input_stream_read; | |
| stream_class->close_fn = g_local_file_input_stream_close; | |
| file_stream_class->tell = g_local_file_input_stream_tell; | |
| file_stream_class->can_seek = g_local_file_input_stream_can_seek; | |
| file_stream_class->seek = g_local_file_input_stream_seek; | |
| file_stream_class->query_info = g_local_file_input_stream_query_info; | |
| } | |
| #ifdef G_OS_UNIX | |
| static void | |
| g_file_descriptor_based_iface_init (GFileDescriptorBasedIface *iface) | |
| { | |
| iface->get_fd = g_local_file_input_stream_get_fd; | |
| } | |
| #endif | |
| static void | |
| g_local_file_input_stream_init (GLocalFileInputStream *info) | |
| { | |
| info->priv = g_local_file_input_stream_get_instance_private (info); | |
| info->priv->do_close = TRUE; | |
| } | |
| GFileInputStream * | |
| _g_local_file_input_stream_new (int fd) | |
| { | |
| GLocalFileInputStream *stream; | |
| stream = g_object_new (G_TYPE_LOCAL_FILE_INPUT_STREAM, NULL); | |
| stream->priv->fd = fd; | |
| return G_FILE_INPUT_STREAM (stream); | |
| } | |
| static gssize | |
| g_local_file_input_stream_read (GInputStream *stream, | |
| void *buffer, | |
| gsize count, | |
| GCancellable *cancellable, | |
| GError **error) | |
| { | |
| GLocalFileInputStream *file; | |
| gssize res; | |
| file = G_LOCAL_FILE_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 gboolean | |
| g_local_file_input_stream_close (GInputStream *stream, | |
| GCancellable *cancellable, | |
| GError **error) | |
| { | |
| GLocalFileInputStream *file; | |
| file = G_LOCAL_FILE_INPUT_STREAM (stream); | |
| if (!file->priv->do_close) | |
| return TRUE; | |
| if (file->priv->fd == -1) | |
| return TRUE; | |
| 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 | |
| g_local_file_input_stream_tell (GFileInputStream *stream) | |
| { | |
| GLocalFileInputStream *file; | |
| off_t pos; | |
| file = G_LOCAL_FILE_INPUT_STREAM (stream); | |
| pos = lseek (file->priv->fd, 0, SEEK_CUR); | |
| if (pos == (off_t)-1) | |
| return 0; | |
| return pos; | |
| } | |
| static gboolean | |
| g_local_file_input_stream_can_seek (GFileInputStream *stream) | |
| { | |
| GLocalFileInputStream *file; | |
| off_t pos; | |
| file = G_LOCAL_FILE_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 | |
| g_local_file_input_stream_seek (GFileInputStream *stream, | |
| goffset offset, | |
| GSeekType type, | |
| GCancellable *cancellable, | |
| GError **error) | |
| { | |
| GLocalFileInputStream *file; | |
| off_t pos; | |
| file = G_LOCAL_FILE_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 * | |
| g_local_file_input_stream_query_info (GFileInputStream *stream, | |
| const char *attributes, | |
| GCancellable *cancellable, | |
| GError **error) | |
| { | |
| GLocalFileInputStream *file; | |
| file = G_LOCAL_FILE_INPUT_STREAM (stream); | |
| if (g_cancellable_set_error_if_cancelled (cancellable, error)) | |
| return NULL; | |
| return _g_local_file_info_get_from_fd (file->priv->fd, | |
| attributes, | |
| error); | |
| } | |
| #ifdef G_OS_UNIX | |
| static int | |
| g_local_file_input_stream_get_fd (GFileDescriptorBased *fd_based) | |
| { | |
| GLocalFileInputStream *stream = G_LOCAL_FILE_INPUT_STREAM (fd_based); | |
| return stream->priv->fd; | |
| } | |
| #endif |