2007-11-26 17:13:05 +01:00
|
|
|
/* GIO - GLib Input, Output and Streaming Library
|
|
|
|
*
|
|
|
|
* Copyright (C) 2006-2007 Red Hat, Inc.
|
|
|
|
*
|
2022-05-18 10:12:45 +02:00
|
|
|
* SPDX-License-Identifier: LGPL-2.1-or-later
|
|
|
|
*
|
2007-11-26 17:13:05 +01:00
|
|
|
* 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
|
2017-05-27 18:21:30 +02:00
|
|
|
* version 2.1 of the License, or (at your option) any later version.
|
2007-11-26 17:13:05 +01:00
|
|
|
*
|
|
|
|
* 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
|
2014-01-23 12:58:29 +01:00
|
|
|
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
2007-11-26 17:13:05 +01:00
|
|
|
*
|
|
|
|
* Author: Alexander Larsson <alexl@redhat.com>
|
|
|
|
*/
|
|
|
|
|
2020-08-14 17:04:37 +02:00
|
|
|
/* Needed for the statx() calls in inline functions in glocalfileinfo.h */
|
|
|
|
#ifndef _GNU_SOURCE
|
|
|
|
#define _GNU_SOURCE
|
|
|
|
#endif
|
|
|
|
|
2008-06-22 17:10:51 +02:00
|
|
|
#include "config.h"
|
2007-11-26 17:13:05 +01:00
|
|
|
|
|
|
|
#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 "glibintl.h"
|
|
|
|
#include "gioerror.h"
|
2008-07-01 08:32:35 +02:00
|
|
|
#include "gcancellable.h"
|
2007-11-26 17:13:05 +01:00
|
|
|
#include "glocalfileoutputstream.h"
|
Ensure g_file_copy() does not temporarily expose private files
Previously, g_file_copy() would (on Unix) create files with the
default mode of 644. For applications which might at user request
copy arbitrary private files such as ~/.ssh or /etc/shadow, a
world-readable copy would be temporarily exposed.
This patch is suboptimal in that it *only* fixes g_file_copy()
for the case where both source and destination are instances of
GLocalFile on Unix.
The reason for this is that the public GFile APIs for creating files
allow very limited control over the access permissions for the created
file; one can either say a file is "private" or not. Fixing
this by adding e.g. g_file_create_with_attributes() would make sense,
except this would entail 8 new API calls for all the variants of
_create(), _create_async(), _replace(), _replace_async(),
_create_readwrite(), _create_readwrite_async(), _replace_readwrite(),
_replace_readwrite_async(). That can be done as a separate patch
later.
https://bugzilla.gnome.org/show_bug.cgi?id=699959
2013-05-12 08:28:01 +02:00
|
|
|
#include "gfileinfo.h"
|
2007-11-26 17:13:05 +01:00
|
|
|
#include "glocalfileinfo.h"
|
|
|
|
|
2010-05-20 16:51:00 +02:00
|
|
|
#ifdef G_OS_UNIX
|
Replace #ifdef HAVE_UNISTD_H checks with #ifdef G_OS_UNIX
In Windows development environments that have it, <unistd.h> is mostly
just a wrapper around several other native headers (in particular,
<io.h>, which contains read(), close(), etc, and <process.h>, which
contains getpid()). But given that some Windows dev environments don't
have <unistd.h>, everything that uses those functions on Windows
already needed to include the correct Windows header as well, and so
there is never any point to including <unistd.h> on Windows.
Also, remove some <unistd.h> includes (and a few others) that were
unnecessary even on unix.
https://bugzilla.gnome.org/show_bug.cgi?id=710519
2013-10-19 19:04:00 +02:00
|
|
|
#include <unistd.h>
|
2010-05-20 16:51:00 +02:00
|
|
|
#include "gfiledescriptorbased.h"
|
2018-09-13 20:07:39 +02:00
|
|
|
#include <sys/uio.h>
|
2010-05-20 16:51:00 +02:00
|
|
|
#endif
|
|
|
|
|
W32: Add a stat() implementation for private use
This commit adds new W32-only functions to gstdio.c,
and a new header file, gstdioprivate.h.
These functions are:
g_win32_stat_utf8()
g_win32_lstat_utf8()
g_win32_fstat()
and they fill a private structure, GWin32PrivateStat,
which has all the fields that normal stat has, as well as some
extras.
These functions are then used throughout glib and gio to get better
data about the system. Specifically:
* Full, 64-bit size, guaranteed (g_stat() is forced to use 32-bit st_size)
* Full, 64-bit file identifier (st_ino is 0 when normal stat() is used, and still is)
* W32 File attributes (which stat() doesn't report); in particular, this allows
symlinks to be correctly identified
* Full, 64-bit time, guaranteed (g_stat() uses 32-bit st_*time on 32-bit Windows)
* Allocated file size (as a W32 replacement for the missing st_blocks)
st_mode remains unchanged (thus, no S_ISLNK), so when these are given back to
glib users (via g_stat(), for example, which is now implemented by calling g_win32_stat_utf8),
this field does not contain anything unexpected.
g_lstat() now calls g_win32_lstat_utf8(), which works on symlinks the way it's supposed to.
Also adds the g_win32_readlink_utf8() function, which behaves like readlink()
(including its inability to return 0-terminated strings and inability to say how large
the output buffer should be; these limitations are purely for compatibility with
existing glib code).
Thus, symlink support should now be much better, although far from being complete.
A new W32-only test in gio/tests/file.c highlights the following features:
* allocated size
* 64-bit time
* unique file IDs
https://bugzilla.gnome.org/show_bug.cgi?id=788180
2017-09-29 12:14:41 +02:00
|
|
|
#include "glib-private.h"
|
2019-05-29 15:04:55 +02:00
|
|
|
#include "gioprivate.h"
|
W32: Add a stat() implementation for private use
This commit adds new W32-only functions to gstdio.c,
and a new header file, gstdioprivate.h.
These functions are:
g_win32_stat_utf8()
g_win32_lstat_utf8()
g_win32_fstat()
and they fill a private structure, GWin32PrivateStat,
which has all the fields that normal stat has, as well as some
extras.
These functions are then used throughout glib and gio to get better
data about the system. Specifically:
* Full, 64-bit size, guaranteed (g_stat() is forced to use 32-bit st_size)
* Full, 64-bit file identifier (st_ino is 0 when normal stat() is used, and still is)
* W32 File attributes (which stat() doesn't report); in particular, this allows
symlinks to be correctly identified
* Full, 64-bit time, guaranteed (g_stat() uses 32-bit st_*time on 32-bit Windows)
* Allocated file size (as a W32 replacement for the missing st_blocks)
st_mode remains unchanged (thus, no S_ISLNK), so when these are given back to
glib users (via g_stat(), for example, which is now implemented by calling g_win32_stat_utf8),
this field does not contain anything unexpected.
g_lstat() now calls g_win32_lstat_utf8(), which works on symlinks the way it's supposed to.
Also adds the g_win32_readlink_utf8() function, which behaves like readlink()
(including its inability to return 0-terminated strings and inability to say how large
the output buffer should be; these limitations are purely for compatibility with
existing glib code).
Thus, symlink support should now be much better, although far from being complete.
A new W32-only test in gio/tests/file.c highlights the following features:
* allocated size
* 64-bit time
* unique file IDs
https://bugzilla.gnome.org/show_bug.cgi?id=788180
2017-09-29 12:14:41 +02:00
|
|
|
|
2007-12-08 13:01:06 +01:00
|
|
|
#ifdef G_OS_WIN32
|
|
|
|
#include <io.h>
|
|
|
|
#ifndef S_ISDIR
|
|
|
|
#define S_ISDIR(m) (((m) & _S_IFMT) == _S_IFDIR)
|
|
|
|
#endif
|
|
|
|
#ifndef S_ISREG
|
|
|
|
#define S_ISREG(m) (((m) & _S_IFMT) == _S_IFREG)
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
|
2008-02-18 11:10:58 +01:00
|
|
|
#ifndef O_BINARY
|
|
|
|
#define O_BINARY 0
|
|
|
|
#endif
|
|
|
|
|
2021-02-24 18:42:24 +01:00
|
|
|
#ifndef O_CLOEXEC
|
|
|
|
#define O_CLOEXEC 0
|
|
|
|
#else
|
|
|
|
#define HAVE_O_CLOEXEC 1
|
|
|
|
#endif
|
|
|
|
|
2013-06-11 01:29:58 +02:00
|
|
|
struct _GLocalFileOutputStreamPrivate {
|
|
|
|
char *tmp_filename;
|
|
|
|
char *original_filename;
|
|
|
|
char *backup_filename;
|
|
|
|
char *etag;
|
|
|
|
guint sync_on_close : 1;
|
|
|
|
guint do_close : 1;
|
|
|
|
int fd;
|
|
|
|
};
|
2007-11-28 13:39:07 +01:00
|
|
|
|
2010-05-20 16:51:00 +02:00
|
|
|
#ifdef G_OS_UNIX
|
2010-02-07 17:18:06 +01:00
|
|
|
static void g_file_descriptor_based_iface_init (GFileDescriptorBasedIface *iface);
|
2010-05-20 16:51:00 +02:00
|
|
|
#endif
|
|
|
|
|
2007-11-28 17:01:59 +01:00
|
|
|
#define g_local_file_output_stream_get_type _g_local_file_output_stream_get_type
|
2010-05-20 16:51:00 +02:00
|
|
|
#ifdef G_OS_UNIX
|
2010-06-22 11:13:21 +02:00
|
|
|
G_DEFINE_TYPE_WITH_CODE (GLocalFileOutputStream, g_local_file_output_stream, G_TYPE_FILE_OUTPUT_STREAM,
|
2013-06-11 01:29:58 +02:00
|
|
|
G_ADD_PRIVATE (GLocalFileOutputStream)
|
2010-02-07 17:18:06 +01:00
|
|
|
G_IMPLEMENT_INTERFACE (G_TYPE_FILE_DESCRIPTOR_BASED,
|
2013-06-11 01:29:58 +02:00
|
|
|
g_file_descriptor_based_iface_init))
|
2010-06-22 11:13:21 +02:00
|
|
|
#else
|
2013-06-11 01:29:58 +02:00
|
|
|
G_DEFINE_TYPE_WITH_CODE (GLocalFileOutputStream, g_local_file_output_stream, G_TYPE_FILE_OUTPUT_STREAM,
|
|
|
|
G_ADD_PRIVATE (GLocalFileOutputStream))
|
2010-06-22 11:13:21 +02:00
|
|
|
#endif
|
2010-02-07 17:18:06 +01:00
|
|
|
|
2007-11-26 17:13:05 +01:00
|
|
|
|
|
|
|
/* Some of the file replacement code was based on the code from gedit,
|
|
|
|
* relicenced to LGPL with permissions from the authors.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#define BACKUP_EXTENSION "~"
|
|
|
|
|
|
|
|
static gssize g_local_file_output_stream_write (GOutputStream *stream,
|
|
|
|
const void *buffer,
|
|
|
|
gsize count,
|
|
|
|
GCancellable *cancellable,
|
|
|
|
GError **error);
|
2018-09-13 20:07:39 +02:00
|
|
|
#ifdef G_OS_UNIX
|
|
|
|
static gboolean g_local_file_output_stream_writev (GOutputStream *stream,
|
|
|
|
const GOutputVector *vectors,
|
|
|
|
gsize n_vectors,
|
|
|
|
gsize *bytes_written,
|
|
|
|
GCancellable *cancellable,
|
|
|
|
GError **error);
|
|
|
|
#endif
|
2007-11-26 17:13:05 +01:00
|
|
|
static gboolean g_local_file_output_stream_close (GOutputStream *stream,
|
|
|
|
GCancellable *cancellable,
|
|
|
|
GError **error);
|
|
|
|
static GFileInfo *g_local_file_output_stream_query_info (GFileOutputStream *stream,
|
2009-03-17 12:21:37 +01:00
|
|
|
const char *attributes,
|
2007-11-26 17:13:05 +01:00
|
|
|
GCancellable *cancellable,
|
|
|
|
GError **error);
|
|
|
|
static char * g_local_file_output_stream_get_etag (GFileOutputStream *stream);
|
|
|
|
static goffset g_local_file_output_stream_tell (GFileOutputStream *stream);
|
|
|
|
static gboolean g_local_file_output_stream_can_seek (GFileOutputStream *stream);
|
|
|
|
static gboolean g_local_file_output_stream_seek (GFileOutputStream *stream,
|
|
|
|
goffset offset,
|
|
|
|
GSeekType type,
|
|
|
|
GCancellable *cancellable,
|
|
|
|
GError **error);
|
|
|
|
static gboolean g_local_file_output_stream_can_truncate (GFileOutputStream *stream);
|
|
|
|
static gboolean g_local_file_output_stream_truncate (GFileOutputStream *stream,
|
|
|
|
goffset size,
|
|
|
|
GCancellable *cancellable,
|
|
|
|
GError **error);
|
2010-05-20 16:51:00 +02:00
|
|
|
#ifdef G_OS_UNIX
|
2010-02-07 17:18:06 +01:00
|
|
|
static int g_local_file_output_stream_get_fd (GFileDescriptorBased *stream);
|
2010-05-20 16:51:00 +02:00
|
|
|
#endif
|
2007-11-26 17:13:05 +01:00
|
|
|
|
|
|
|
static void
|
|
|
|
g_local_file_output_stream_finalize (GObject *object)
|
|
|
|
{
|
|
|
|
GLocalFileOutputStream *file;
|
|
|
|
|
|
|
|
file = G_LOCAL_FILE_OUTPUT_STREAM (object);
|
|
|
|
|
|
|
|
g_free (file->priv->tmp_filename);
|
|
|
|
g_free (file->priv->original_filename);
|
|
|
|
g_free (file->priv->backup_filename);
|
|
|
|
g_free (file->priv->etag);
|
2008-06-16 11:54:04 +02:00
|
|
|
|
|
|
|
G_OBJECT_CLASS (g_local_file_output_stream_parent_class)->finalize (object);
|
2007-11-26 17:13:05 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
g_local_file_output_stream_class_init (GLocalFileOutputStreamClass *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);
|
2013-06-11 01:29:58 +02:00
|
|
|
|
2007-11-26 17:13:05 +01:00
|
|
|
gobject_class->finalize = g_local_file_output_stream_finalize;
|
|
|
|
|
2007-12-05 11:38:03 +01:00
|
|
|
stream_class->write_fn = g_local_file_output_stream_write;
|
2018-09-13 20:07:39 +02:00
|
|
|
#ifdef G_OS_UNIX
|
|
|
|
stream_class->writev_fn = g_local_file_output_stream_writev;
|
|
|
|
#endif
|
2007-12-05 11:38:03 +01:00
|
|
|
stream_class->close_fn = g_local_file_output_stream_close;
|
2007-11-26 17:13:05 +01:00
|
|
|
file_stream_class->query_info = g_local_file_output_stream_query_info;
|
|
|
|
file_stream_class->get_etag = g_local_file_output_stream_get_etag;
|
|
|
|
file_stream_class->tell = g_local_file_output_stream_tell;
|
|
|
|
file_stream_class->can_seek = g_local_file_output_stream_can_seek;
|
|
|
|
file_stream_class->seek = g_local_file_output_stream_seek;
|
|
|
|
file_stream_class->can_truncate = g_local_file_output_stream_can_truncate;
|
2007-12-05 11:38:03 +01:00
|
|
|
file_stream_class->truncate_fn = g_local_file_output_stream_truncate;
|
2007-11-26 17:13:05 +01:00
|
|
|
}
|
|
|
|
|
2010-05-20 16:51:00 +02:00
|
|
|
#ifdef G_OS_UNIX
|
2010-02-07 17:18:06 +01:00
|
|
|
static void
|
|
|
|
g_file_descriptor_based_iface_init (GFileDescriptorBasedIface *iface)
|
|
|
|
{
|
|
|
|
iface->get_fd = g_local_file_output_stream_get_fd;
|
|
|
|
}
|
2010-05-20 16:51:00 +02:00
|
|
|
#endif
|
2010-02-07 17:18:06 +01:00
|
|
|
|
2007-11-26 17:13:05 +01:00
|
|
|
static void
|
|
|
|
g_local_file_output_stream_init (GLocalFileOutputStream *stream)
|
|
|
|
{
|
2013-06-24 16:43:04 +02:00
|
|
|
stream->priv = g_local_file_output_stream_get_instance_private (stream);
|
2009-05-13 13:03:47 +02:00
|
|
|
stream->priv->do_close = TRUE;
|
|
|
|
}
|
|
|
|
|
2007-11-26 17:13:05 +01:00
|
|
|
static gssize
|
2007-11-30 06:11:25 +01:00
|
|
|
g_local_file_output_stream_write (GOutputStream *stream,
|
|
|
|
const void *buffer,
|
|
|
|
gsize count,
|
|
|
|
GCancellable *cancellable,
|
|
|
|
GError **error)
|
2007-11-26 17:13:05 +01:00
|
|
|
{
|
|
|
|
GLocalFileOutputStream *file;
|
|
|
|
gssize res;
|
|
|
|
|
|
|
|
file = G_LOCAL_FILE_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)
|
|
|
|
{
|
Save errno before calling other funcs that potentially alter it. Bug
* gio/gdesktopappinfo.c: (ensure_dir):
* gio/glocalfile.c: (g_local_file_query_filesystem_info),
(g_local_file_read), (g_local_file_delete), (g_local_file_trash),
(g_local_file_move):
* gio/glocalfileinfo.c: (set_xattr), (_g_local_file_info_get),
(_g_local_file_info_get_from_fd), (set_unix_mode),
(set_unix_uid_gid), (set_symlink), (set_mtime_atime):
* gio/glocalfileinputstream.c: (g_local_file_input_stream_read),
(g_local_file_input_stream_skip),
(g_local_file_input_stream_close),
(g_local_file_input_stream_seek):
* gio/glocalfileoutputstream.c:
(g_local_file_output_stream_write),
(g_local_file_output_stream_close),
(g_local_file_output_stream_seek),
(g_local_file_output_stream_truncate), (copy_file_data),
(handle_overwrite_open):
* gio/gunixinputstream.c: (g_unix_input_stream_read),
(g_unix_input_stream_close), (read_async_cb), (close_async_cb):
* gio/gunixoutputstream.c: (g_unix_output_stream_write),
(g_unix_output_stream_close), (write_async_cb), (close_async_cb):
Save
errno before calling other funcs that potentially alter it. Bug
#514766.
svn path=/trunk/; revision=6466
2008-02-06 16:10:08 +01:00
|
|
|
int errsv = errno;
|
|
|
|
|
|
|
|
if (errsv == EINTR)
|
2007-11-26 17:13:05 +01:00
|
|
|
continue;
|
|
|
|
|
|
|
|
g_set_error (error, G_IO_ERROR,
|
Save errno before calling other funcs that potentially alter it. Bug
* gio/gdesktopappinfo.c: (ensure_dir):
* gio/glocalfile.c: (g_local_file_query_filesystem_info),
(g_local_file_read), (g_local_file_delete), (g_local_file_trash),
(g_local_file_move):
* gio/glocalfileinfo.c: (set_xattr), (_g_local_file_info_get),
(_g_local_file_info_get_from_fd), (set_unix_mode),
(set_unix_uid_gid), (set_symlink), (set_mtime_atime):
* gio/glocalfileinputstream.c: (g_local_file_input_stream_read),
(g_local_file_input_stream_skip),
(g_local_file_input_stream_close),
(g_local_file_input_stream_seek):
* gio/glocalfileoutputstream.c:
(g_local_file_output_stream_write),
(g_local_file_output_stream_close),
(g_local_file_output_stream_seek),
(g_local_file_output_stream_truncate), (copy_file_data),
(handle_overwrite_open):
* gio/gunixinputstream.c: (g_unix_input_stream_read),
(g_unix_input_stream_close), (read_async_cb), (close_async_cb):
* gio/gunixoutputstream.c: (g_unix_output_stream_write),
(g_unix_output_stream_close), (write_async_cb), (close_async_cb):
Save
errno before calling other funcs that potentially alter it. Bug
#514766.
svn path=/trunk/; revision=6466
2008-02-06 16:10:08 +01:00
|
|
|
g_io_error_from_errno (errsv),
|
2007-11-26 17:13:05 +01:00
|
|
|
_("Error writing to file: %s"),
|
Save errno before calling other funcs that potentially alter it. Bug
* gio/gdesktopappinfo.c: (ensure_dir):
* gio/glocalfile.c: (g_local_file_query_filesystem_info),
(g_local_file_read), (g_local_file_delete), (g_local_file_trash),
(g_local_file_move):
* gio/glocalfileinfo.c: (set_xattr), (_g_local_file_info_get),
(_g_local_file_info_get_from_fd), (set_unix_mode),
(set_unix_uid_gid), (set_symlink), (set_mtime_atime):
* gio/glocalfileinputstream.c: (g_local_file_input_stream_read),
(g_local_file_input_stream_skip),
(g_local_file_input_stream_close),
(g_local_file_input_stream_seek):
* gio/glocalfileoutputstream.c:
(g_local_file_output_stream_write),
(g_local_file_output_stream_close),
(g_local_file_output_stream_seek),
(g_local_file_output_stream_truncate), (copy_file_data),
(handle_overwrite_open):
* gio/gunixinputstream.c: (g_unix_input_stream_read),
(g_unix_input_stream_close), (read_async_cb), (close_async_cb):
* gio/gunixoutputstream.c: (g_unix_output_stream_write),
(g_unix_output_stream_close), (write_async_cb), (close_async_cb):
Save
errno before calling other funcs that potentially alter it. Bug
#514766.
svn path=/trunk/; revision=6466
2008-02-06 16:10:08 +01:00
|
|
|
g_strerror (errsv));
|
2007-11-26 17:13:05 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2018-09-13 20:07:39 +02:00
|
|
|
/* On Windows there is no equivalent API for files. The closest API to that is
|
|
|
|
* WriteFileGather() but it is useless in general: it requires, among other
|
|
|
|
* things, that each chunk is the size of a whole page and in memory aligned
|
|
|
|
* to a page. We can't possibly guarantee that in GLib.
|
|
|
|
*/
|
|
|
|
#ifdef G_OS_UNIX
|
|
|
|
/* Macro to check if struct iovec and GOutputVector have the same ABI */
|
|
|
|
#define G_OUTPUT_VECTOR_IS_IOVEC (sizeof (struct iovec) == sizeof (GOutputVector) && \
|
2020-01-20 13:41:34 +01:00
|
|
|
G_SIZEOF_MEMBER (struct iovec, iov_base) == G_SIZEOF_MEMBER (GOutputVector, buffer) && \
|
2018-09-13 20:07:39 +02:00
|
|
|
G_STRUCT_OFFSET (struct iovec, iov_base) == G_STRUCT_OFFSET (GOutputVector, buffer) && \
|
2020-01-20 13:41:34 +01:00
|
|
|
G_SIZEOF_MEMBER (struct iovec, iov_len) == G_SIZEOF_MEMBER (GOutputVector, size) && \
|
2018-09-13 20:07:39 +02:00
|
|
|
G_STRUCT_OFFSET (struct iovec, iov_len) == G_STRUCT_OFFSET (GOutputVector, size))
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
g_local_file_output_stream_writev (GOutputStream *stream,
|
|
|
|
const GOutputVector *vectors,
|
|
|
|
gsize n_vectors,
|
|
|
|
gsize *bytes_written,
|
|
|
|
GCancellable *cancellable,
|
|
|
|
GError **error)
|
|
|
|
{
|
|
|
|
GLocalFileOutputStream *file;
|
|
|
|
gssize res;
|
|
|
|
struct iovec *iov;
|
|
|
|
|
|
|
|
if (bytes_written)
|
|
|
|
*bytes_written = 0;
|
|
|
|
|
2019-05-29 15:04:55 +02:00
|
|
|
/* Clamp the number of vectors if more given than we can write in one go.
|
|
|
|
* The caller has to handle short writes anyway.
|
2018-09-13 20:07:39 +02:00
|
|
|
*/
|
2019-05-29 15:04:55 +02:00
|
|
|
if (n_vectors > G_IOV_MAX)
|
|
|
|
n_vectors = G_IOV_MAX;
|
2018-09-13 20:07:39 +02:00
|
|
|
|
|
|
|
file = G_LOCAL_FILE_OUTPUT_STREAM (stream);
|
|
|
|
|
|
|
|
if (G_OUTPUT_VECTOR_IS_IOVEC)
|
|
|
|
{
|
|
|
|
/* ABI is compatible */
|
|
|
|
iov = (struct iovec *) vectors;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
gsize i;
|
|
|
|
|
|
|
|
/* ABI is incompatible */
|
|
|
|
iov = g_newa (struct iovec, n_vectors);
|
|
|
|
for (i = 0; i < n_vectors; i++)
|
|
|
|
{
|
|
|
|
iov[i].iov_base = (void *)vectors[i].buffer;
|
|
|
|
iov[i].iov_len = vectors[i].size;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
while (1)
|
|
|
|
{
|
|
|
|
if (g_cancellable_set_error_if_cancelled (cancellable, error))
|
|
|
|
return FALSE;
|
|
|
|
res = writev (file->priv->fd, iov, n_vectors);
|
|
|
|
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));
|
|
|
|
}
|
|
|
|
else if (bytes_written)
|
|
|
|
{
|
|
|
|
*bytes_written = res;
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return res != -1;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2009-05-13 13:03:47 +02:00
|
|
|
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)
|
2007-11-26 17:13:05 +01:00
|
|
|
{
|
2008-08-23 03:09:08 +02:00
|
|
|
GLocalFileStat final_stat;
|
2007-11-26 17:13:05 +01:00
|
|
|
|
2009-03-16 17:03:13 +01:00
|
|
|
if (file->priv->sync_on_close &&
|
2020-05-27 19:50:35 +02:00
|
|
|
g_fsync (file->priv->fd) != 0)
|
2009-03-16 17:03:13 +01:00
|
|
|
{
|
|
|
|
int errsv = errno;
|
|
|
|
|
|
|
|
g_set_error (error, G_IO_ERROR,
|
2009-08-28 05:02:08 +02:00
|
|
|
g_io_error_from_errno (errsv),
|
2009-03-16 17:03:13 +01:00
|
|
|
_("Error writing to file: %s"),
|
|
|
|
g_strerror (errsv));
|
|
|
|
goto err_out;
|
|
|
|
}
|
2020-05-27 19:50:35 +02:00
|
|
|
|
2008-08-23 03:09:08 +02:00
|
|
|
#ifdef G_OS_WIN32
|
|
|
|
|
|
|
|
/* Must close before renaming on Windows, so just do the close first
|
|
|
|
* in all cases for now.
|
|
|
|
*/
|
W32: Add a stat() implementation for private use
This commit adds new W32-only functions to gstdio.c,
and a new header file, gstdioprivate.h.
These functions are:
g_win32_stat_utf8()
g_win32_lstat_utf8()
g_win32_fstat()
and they fill a private structure, GWin32PrivateStat,
which has all the fields that normal stat has, as well as some
extras.
These functions are then used throughout glib and gio to get better
data about the system. Specifically:
* Full, 64-bit size, guaranteed (g_stat() is forced to use 32-bit st_size)
* Full, 64-bit file identifier (st_ino is 0 when normal stat() is used, and still is)
* W32 File attributes (which stat() doesn't report); in particular, this allows
symlinks to be correctly identified
* Full, 64-bit time, guaranteed (g_stat() uses 32-bit st_*time on 32-bit Windows)
* Allocated file size (as a W32 replacement for the missing st_blocks)
st_mode remains unchanged (thus, no S_ISLNK), so when these are given back to
glib users (via g_stat(), for example, which is now implemented by calling g_win32_stat_utf8),
this field does not contain anything unexpected.
g_lstat() now calls g_win32_lstat_utf8(), which works on symlinks the way it's supposed to.
Also adds the g_win32_readlink_utf8() function, which behaves like readlink()
(including its inability to return 0-terminated strings and inability to say how large
the output buffer should be; these limitations are purely for compatibility with
existing glib code).
Thus, symlink support should now be much better, although far from being complete.
A new W32-only test in gio/tests/file.c highlights the following features:
* allocated size
* 64-bit time
* unique file IDs
https://bugzilla.gnome.org/show_bug.cgi?id=788180
2017-09-29 12:14:41 +02:00
|
|
|
if (GLIB_PRIVATE_CALL (g_win32_fstat) (file->priv->fd, &final_stat) == 0)
|
2008-08-23 03:09:08 +02:00
|
|
|
file->priv->etag = _g_local_file_info_create_etag (&final_stat);
|
|
|
|
|
2013-01-25 18:05:26 +01:00
|
|
|
if (!g_close (file->priv->fd, NULL))
|
2008-08-23 03:09:08 +02:00
|
|
|
{
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
2007-11-26 17:13:05 +01:00
|
|
|
if (file->priv->tmp_filename)
|
|
|
|
{
|
|
|
|
/* We need to move the temp file to its final place,
|
|
|
|
* and possibly create the backup file
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (file->priv->backup_filename)
|
|
|
|
{
|
|
|
|
if (g_cancellable_set_error_if_cancelled (cancellable, error))
|
|
|
|
goto err_out;
|
|
|
|
|
Require POSIX.1 (1990) compliance on unix
Assume unix platforms support the original POSIX.1 standard.
Specifically, assume that if G_OS_UNIX, then we have chown(),
getcwd(), getgrgid(), getpwuid(), link(), <grp.h>, <pwd.h>,
<sys/types.h>, <sys/uio.h>, <sys/wait.h>, and <unistd.h>.
Additionally, since all versions of Windows that we care about also
have <sys/types.h>, we can remove HAVE_SYS_TYPES_H checks everywhere.
Also remove one include of <sys/times.h>, and the corresponding
configure check, since the include is not currently needed (and may
always have just been a typo for <sys/time.h>).
https://bugzilla.gnome.org/show_bug.cgi?id=710519
2013-10-19 19:03:59 +02:00
|
|
|
#ifdef G_OS_UNIX
|
2007-11-26 17:13:05 +01:00
|
|
|
/* create original -> backup link, the original is then renamed over */
|
2008-02-18 16:35:16 +01:00
|
|
|
if (g_unlink (file->priv->backup_filename) != 0 &&
|
2007-11-26 17:13:05 +01:00
|
|
|
errno != ENOENT)
|
|
|
|
{
|
Save errno before calling other funcs that potentially alter it. Bug
* gio/gdesktopappinfo.c: (ensure_dir):
* gio/glocalfile.c: (g_local_file_query_filesystem_info),
(g_local_file_read), (g_local_file_delete), (g_local_file_trash),
(g_local_file_move):
* gio/glocalfileinfo.c: (set_xattr), (_g_local_file_info_get),
(_g_local_file_info_get_from_fd), (set_unix_mode),
(set_unix_uid_gid), (set_symlink), (set_mtime_atime):
* gio/glocalfileinputstream.c: (g_local_file_input_stream_read),
(g_local_file_input_stream_skip),
(g_local_file_input_stream_close),
(g_local_file_input_stream_seek):
* gio/glocalfileoutputstream.c:
(g_local_file_output_stream_write),
(g_local_file_output_stream_close),
(g_local_file_output_stream_seek),
(g_local_file_output_stream_truncate), (copy_file_data),
(handle_overwrite_open):
* gio/gunixinputstream.c: (g_unix_input_stream_read),
(g_unix_input_stream_close), (read_async_cb), (close_async_cb):
* gio/gunixoutputstream.c: (g_unix_output_stream_write),
(g_unix_output_stream_close), (write_async_cb), (close_async_cb):
Save
errno before calling other funcs that potentially alter it. Bug
#514766.
svn path=/trunk/; revision=6466
2008-02-06 16:10:08 +01:00
|
|
|
int errsv = errno;
|
|
|
|
|
2007-11-26 17:13:05 +01:00
|
|
|
g_set_error (error, G_IO_ERROR,
|
|
|
|
G_IO_ERROR_CANT_CREATE_BACKUP,
|
|
|
|
_("Error removing old backup link: %s"),
|
Save errno before calling other funcs that potentially alter it. Bug
* gio/gdesktopappinfo.c: (ensure_dir):
* gio/glocalfile.c: (g_local_file_query_filesystem_info),
(g_local_file_read), (g_local_file_delete), (g_local_file_trash),
(g_local_file_move):
* gio/glocalfileinfo.c: (set_xattr), (_g_local_file_info_get),
(_g_local_file_info_get_from_fd), (set_unix_mode),
(set_unix_uid_gid), (set_symlink), (set_mtime_atime):
* gio/glocalfileinputstream.c: (g_local_file_input_stream_read),
(g_local_file_input_stream_skip),
(g_local_file_input_stream_close),
(g_local_file_input_stream_seek):
* gio/glocalfileoutputstream.c:
(g_local_file_output_stream_write),
(g_local_file_output_stream_close),
(g_local_file_output_stream_seek),
(g_local_file_output_stream_truncate), (copy_file_data),
(handle_overwrite_open):
* gio/gunixinputstream.c: (g_unix_input_stream_read),
(g_unix_input_stream_close), (read_async_cb), (close_async_cb):
* gio/gunixoutputstream.c: (g_unix_output_stream_write),
(g_unix_output_stream_close), (write_async_cb), (close_async_cb):
Save
errno before calling other funcs that potentially alter it. Bug
#514766.
svn path=/trunk/; revision=6466
2008-02-06 16:10:08 +01:00
|
|
|
g_strerror (errsv));
|
2007-11-26 17:13:05 +01:00
|
|
|
goto err_out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (link (file->priv->original_filename, file->priv->backup_filename) != 0)
|
|
|
|
{
|
2008-02-06 14:45:26 +01:00
|
|
|
/* link failed or is not supported, try rename */
|
2008-02-18 16:35:16 +01:00
|
|
|
if (g_rename (file->priv->original_filename, file->priv->backup_filename) != 0)
|
2008-02-06 14:45:26 +01:00
|
|
|
{
|
Save errno before calling other funcs that potentially alter it. Bug
* gio/gdesktopappinfo.c: (ensure_dir):
* gio/glocalfile.c: (g_local_file_query_filesystem_info),
(g_local_file_read), (g_local_file_delete), (g_local_file_trash),
(g_local_file_move):
* gio/glocalfileinfo.c: (set_xattr), (_g_local_file_info_get),
(_g_local_file_info_get_from_fd), (set_unix_mode),
(set_unix_uid_gid), (set_symlink), (set_mtime_atime):
* gio/glocalfileinputstream.c: (g_local_file_input_stream_read),
(g_local_file_input_stream_skip),
(g_local_file_input_stream_close),
(g_local_file_input_stream_seek):
* gio/glocalfileoutputstream.c:
(g_local_file_output_stream_write),
(g_local_file_output_stream_close),
(g_local_file_output_stream_seek),
(g_local_file_output_stream_truncate), (copy_file_data),
(handle_overwrite_open):
* gio/gunixinputstream.c: (g_unix_input_stream_read),
(g_unix_input_stream_close), (read_async_cb), (close_async_cb):
* gio/gunixoutputstream.c: (g_unix_output_stream_write),
(g_unix_output_stream_close), (write_async_cb), (close_async_cb):
Save
errno before calling other funcs that potentially alter it. Bug
#514766.
svn path=/trunk/; revision=6466
2008-02-06 16:10:08 +01:00
|
|
|
int errsv = errno;
|
|
|
|
|
2008-02-06 14:45:26 +01:00
|
|
|
g_set_error (error, G_IO_ERROR,
|
|
|
|
G_IO_ERROR_CANT_CREATE_BACKUP,
|
|
|
|
_("Error creating backup copy: %s"),
|
Save errno before calling other funcs that potentially alter it. Bug
* gio/gdesktopappinfo.c: (ensure_dir):
* gio/glocalfile.c: (g_local_file_query_filesystem_info),
(g_local_file_read), (g_local_file_delete), (g_local_file_trash),
(g_local_file_move):
* gio/glocalfileinfo.c: (set_xattr), (_g_local_file_info_get),
(_g_local_file_info_get_from_fd), (set_unix_mode),
(set_unix_uid_gid), (set_symlink), (set_mtime_atime):
* gio/glocalfileinputstream.c: (g_local_file_input_stream_read),
(g_local_file_input_stream_skip),
(g_local_file_input_stream_close),
(g_local_file_input_stream_seek):
* gio/glocalfileoutputstream.c:
(g_local_file_output_stream_write),
(g_local_file_output_stream_close),
(g_local_file_output_stream_seek),
(g_local_file_output_stream_truncate), (copy_file_data),
(handle_overwrite_open):
* gio/gunixinputstream.c: (g_unix_input_stream_read),
(g_unix_input_stream_close), (read_async_cb), (close_async_cb):
* gio/gunixoutputstream.c: (g_unix_output_stream_write),
(g_unix_output_stream_close), (write_async_cb), (close_async_cb):
Save
errno before calling other funcs that potentially alter it. Bug
#514766.
svn path=/trunk/; revision=6466
2008-02-06 16:10:08 +01:00
|
|
|
g_strerror (errsv));
|
2008-02-06 14:45:26 +01:00
|
|
|
goto err_out;
|
|
|
|
}
|
2007-11-26 17:13:05 +01:00
|
|
|
}
|
|
|
|
#else
|
|
|
|
/* If link not supported, just rename... */
|
2008-02-18 16:35:16 +01:00
|
|
|
if (g_rename (file->priv->original_filename, file->priv->backup_filename) != 0)
|
2007-11-26 17:13:05 +01:00
|
|
|
{
|
Save errno before calling other funcs that potentially alter it. Bug
* gio/gdesktopappinfo.c: (ensure_dir):
* gio/glocalfile.c: (g_local_file_query_filesystem_info),
(g_local_file_read), (g_local_file_delete), (g_local_file_trash),
(g_local_file_move):
* gio/glocalfileinfo.c: (set_xattr), (_g_local_file_info_get),
(_g_local_file_info_get_from_fd), (set_unix_mode),
(set_unix_uid_gid), (set_symlink), (set_mtime_atime):
* gio/glocalfileinputstream.c: (g_local_file_input_stream_read),
(g_local_file_input_stream_skip),
(g_local_file_input_stream_close),
(g_local_file_input_stream_seek):
* gio/glocalfileoutputstream.c:
(g_local_file_output_stream_write),
(g_local_file_output_stream_close),
(g_local_file_output_stream_seek),
(g_local_file_output_stream_truncate), (copy_file_data),
(handle_overwrite_open):
* gio/gunixinputstream.c: (g_unix_input_stream_read),
(g_unix_input_stream_close), (read_async_cb), (close_async_cb):
* gio/gunixoutputstream.c: (g_unix_output_stream_write),
(g_unix_output_stream_close), (write_async_cb), (close_async_cb):
Save
errno before calling other funcs that potentially alter it. Bug
#514766.
svn path=/trunk/; revision=6466
2008-02-06 16:10:08 +01:00
|
|
|
int errsv = errno;
|
|
|
|
|
2007-11-26 17:13:05 +01:00
|
|
|
g_set_error (error, G_IO_ERROR,
|
|
|
|
G_IO_ERROR_CANT_CREATE_BACKUP,
|
|
|
|
_("Error creating backup copy: %s"),
|
Save errno before calling other funcs that potentially alter it. Bug
* gio/gdesktopappinfo.c: (ensure_dir):
* gio/glocalfile.c: (g_local_file_query_filesystem_info),
(g_local_file_read), (g_local_file_delete), (g_local_file_trash),
(g_local_file_move):
* gio/glocalfileinfo.c: (set_xattr), (_g_local_file_info_get),
(_g_local_file_info_get_from_fd), (set_unix_mode),
(set_unix_uid_gid), (set_symlink), (set_mtime_atime):
* gio/glocalfileinputstream.c: (g_local_file_input_stream_read),
(g_local_file_input_stream_skip),
(g_local_file_input_stream_close),
(g_local_file_input_stream_seek):
* gio/glocalfileoutputstream.c:
(g_local_file_output_stream_write),
(g_local_file_output_stream_close),
(g_local_file_output_stream_seek),
(g_local_file_output_stream_truncate), (copy_file_data),
(handle_overwrite_open):
* gio/gunixinputstream.c: (g_unix_input_stream_read),
(g_unix_input_stream_close), (read_async_cb), (close_async_cb):
* gio/gunixoutputstream.c: (g_unix_output_stream_write),
(g_unix_output_stream_close), (write_async_cb), (close_async_cb):
Save
errno before calling other funcs that potentially alter it. Bug
#514766.
svn path=/trunk/; revision=6466
2008-02-06 16:10:08 +01:00
|
|
|
g_strerror (errsv));
|
2007-11-26 17:13:05 +01:00
|
|
|
goto err_out;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (g_cancellable_set_error_if_cancelled (cancellable, error))
|
|
|
|
goto err_out;
|
|
|
|
|
|
|
|
/* tmp -> original */
|
2008-02-18 16:35:16 +01:00
|
|
|
if (g_rename (file->priv->tmp_filename, file->priv->original_filename) != 0)
|
2007-11-26 17:13:05 +01:00
|
|
|
{
|
Save errno before calling other funcs that potentially alter it. Bug
* gio/gdesktopappinfo.c: (ensure_dir):
* gio/glocalfile.c: (g_local_file_query_filesystem_info),
(g_local_file_read), (g_local_file_delete), (g_local_file_trash),
(g_local_file_move):
* gio/glocalfileinfo.c: (set_xattr), (_g_local_file_info_get),
(_g_local_file_info_get_from_fd), (set_unix_mode),
(set_unix_uid_gid), (set_symlink), (set_mtime_atime):
* gio/glocalfileinputstream.c: (g_local_file_input_stream_read),
(g_local_file_input_stream_skip),
(g_local_file_input_stream_close),
(g_local_file_input_stream_seek):
* gio/glocalfileoutputstream.c:
(g_local_file_output_stream_write),
(g_local_file_output_stream_close),
(g_local_file_output_stream_seek),
(g_local_file_output_stream_truncate), (copy_file_data),
(handle_overwrite_open):
* gio/gunixinputstream.c: (g_unix_input_stream_read),
(g_unix_input_stream_close), (read_async_cb), (close_async_cb):
* gio/gunixoutputstream.c: (g_unix_output_stream_write),
(g_unix_output_stream_close), (write_async_cb), (close_async_cb):
Save
errno before calling other funcs that potentially alter it. Bug
#514766.
svn path=/trunk/; revision=6466
2008-02-06 16:10:08 +01:00
|
|
|
int errsv = errno;
|
|
|
|
|
2007-11-26 17:13:05 +01:00
|
|
|
g_set_error (error, G_IO_ERROR,
|
2008-05-27 23:44:35 +02:00
|
|
|
g_io_error_from_errno (errsv),
|
2007-11-27 17:29:54 +01:00
|
|
|
_("Error renaming temporary file: %s"),
|
Save errno before calling other funcs that potentially alter it. Bug
* gio/gdesktopappinfo.c: (ensure_dir):
* gio/glocalfile.c: (g_local_file_query_filesystem_info),
(g_local_file_read), (g_local_file_delete), (g_local_file_trash),
(g_local_file_move):
* gio/glocalfileinfo.c: (set_xattr), (_g_local_file_info_get),
(_g_local_file_info_get_from_fd), (set_unix_mode),
(set_unix_uid_gid), (set_symlink), (set_mtime_atime):
* gio/glocalfileinputstream.c: (g_local_file_input_stream_read),
(g_local_file_input_stream_skip),
(g_local_file_input_stream_close),
(g_local_file_input_stream_seek):
* gio/glocalfileoutputstream.c:
(g_local_file_output_stream_write),
(g_local_file_output_stream_close),
(g_local_file_output_stream_seek),
(g_local_file_output_stream_truncate), (copy_file_data),
(handle_overwrite_open):
* gio/gunixinputstream.c: (g_unix_input_stream_read),
(g_unix_input_stream_close), (read_async_cb), (close_async_cb):
* gio/gunixoutputstream.c: (g_unix_output_stream_write),
(g_unix_output_stream_close), (write_async_cb), (close_async_cb):
Save
errno before calling other funcs that potentially alter it. Bug
#514766.
svn path=/trunk/; revision=6466
2008-02-06 16:10:08 +01:00
|
|
|
g_strerror (errsv));
|
2007-11-26 17:13:05 +01:00
|
|
|
goto err_out;
|
|
|
|
}
|
2012-11-07 16:36:05 +01:00
|
|
|
|
|
|
|
g_clear_pointer (&file->priv->tmp_filename, g_free);
|
2007-11-26 17:13:05 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (g_cancellable_set_error_if_cancelled (cancellable, error))
|
|
|
|
goto err_out;
|
|
|
|
|
2008-08-23 03:09:08 +02:00
|
|
|
#ifndef G_OS_WIN32 /* Already did the fstat() and close() above on Win32 */
|
|
|
|
|
2020-08-14 16:52:03 +02:00
|
|
|
if (g_local_file_fstat (file->priv->fd, G_LOCAL_FILE_STAT_FIELD_MTIME, G_LOCAL_FILE_STAT_FIELD_ALL, &final_stat) == 0)
|
2007-11-26 17:13:05 +01:00
|
|
|
file->priv->etag = _g_local_file_info_create_etag (&final_stat);
|
|
|
|
|
2013-01-25 18:05:26 +01:00
|
|
|
if (!g_close (file->priv->fd, NULL))
|
2007-11-26 17:13:05 +01:00
|
|
|
{
|
2013-01-25 18:05:26 +01:00
|
|
|
int errsv = errno;
|
|
|
|
|
|
|
|
g_set_error (error, G_IO_ERROR,
|
|
|
|
g_io_error_from_errno (errsv),
|
|
|
|
_("Error closing file: %s"),
|
|
|
|
g_strerror (errsv));
|
|
|
|
goto err_out;
|
2007-11-26 17:13:05 +01:00
|
|
|
}
|
2008-08-23 03:09:08 +02:00
|
|
|
|
|
|
|
#endif
|
2013-01-25 18:05:26 +01:00
|
|
|
|
|
|
|
return TRUE;
|
2007-11-26 17:13:05 +01:00
|
|
|
err_out:
|
2008-08-23 03:09:08 +02:00
|
|
|
|
|
|
|
#ifndef G_OS_WIN32
|
2007-11-26 17:13:05 +01:00
|
|
|
/* A simple try to close the fd in case we fail before the actual close */
|
2016-06-16 20:45:21 +02:00
|
|
|
g_close (file->priv->fd, NULL);
|
2008-08-23 03:09:08 +02:00
|
|
|
#endif
|
2012-11-07 16:36:05 +01:00
|
|
|
if (file->priv->tmp_filename)
|
|
|
|
g_unlink (file->priv->tmp_filename);
|
|
|
|
|
2007-11-26 17:13:05 +01:00
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2009-05-13 13:03:47 +02:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2007-11-26 17:13:05 +01:00
|
|
|
static char *
|
2007-11-30 06:11:25 +01:00
|
|
|
g_local_file_output_stream_get_etag (GFileOutputStream *stream)
|
2007-11-26 17:13:05 +01:00
|
|
|
{
|
|
|
|
GLocalFileOutputStream *file;
|
|
|
|
|
|
|
|
file = G_LOCAL_FILE_OUTPUT_STREAM (stream);
|
|
|
|
|
|
|
|
return g_strdup (file->priv->etag);
|
|
|
|
}
|
|
|
|
|
|
|
|
static goffset
|
|
|
|
g_local_file_output_stream_tell (GFileOutputStream *stream)
|
|
|
|
{
|
|
|
|
GLocalFileOutputStream *file;
|
|
|
|
off_t pos;
|
|
|
|
|
|
|
|
file = G_LOCAL_FILE_OUTPUT_STREAM (stream);
|
|
|
|
|
|
|
|
pos = lseek (file->priv->fd, 0, SEEK_CUR);
|
|
|
|
|
|
|
|
if (pos == (off_t)-1)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
return pos;
|
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
g_local_file_output_stream_can_seek (GFileOutputStream *stream)
|
|
|
|
{
|
|
|
|
GLocalFileOutputStream *file;
|
|
|
|
off_t pos;
|
|
|
|
|
|
|
|
file = G_LOCAL_FILE_OUTPUT_STREAM (stream);
|
|
|
|
|
|
|
|
pos = lseek (file->priv->fd, 0, SEEK_CUR);
|
|
|
|
|
2008-01-21 15:02:19 +01:00
|
|
|
if (pos == (off_t)-1 && errno == ESPIPE)
|
2007-11-26 17:13:05 +01:00
|
|
|
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
|
2007-11-30 06:11:25 +01:00
|
|
|
g_local_file_output_stream_seek (GFileOutputStream *stream,
|
|
|
|
goffset offset,
|
|
|
|
GSeekType type,
|
|
|
|
GCancellable *cancellable,
|
|
|
|
GError **error)
|
2007-11-26 17:13:05 +01:00
|
|
|
{
|
|
|
|
GLocalFileOutputStream *file;
|
|
|
|
off_t pos;
|
|
|
|
|
|
|
|
file = G_LOCAL_FILE_OUTPUT_STREAM (stream);
|
|
|
|
|
|
|
|
pos = lseek (file->priv->fd, offset, seek_type_to_lseek (type));
|
|
|
|
|
|
|
|
if (pos == (off_t)-1)
|
|
|
|
{
|
Save errno before calling other funcs that potentially alter it. Bug
* gio/gdesktopappinfo.c: (ensure_dir):
* gio/glocalfile.c: (g_local_file_query_filesystem_info),
(g_local_file_read), (g_local_file_delete), (g_local_file_trash),
(g_local_file_move):
* gio/glocalfileinfo.c: (set_xattr), (_g_local_file_info_get),
(_g_local_file_info_get_from_fd), (set_unix_mode),
(set_unix_uid_gid), (set_symlink), (set_mtime_atime):
* gio/glocalfileinputstream.c: (g_local_file_input_stream_read),
(g_local_file_input_stream_skip),
(g_local_file_input_stream_close),
(g_local_file_input_stream_seek):
* gio/glocalfileoutputstream.c:
(g_local_file_output_stream_write),
(g_local_file_output_stream_close),
(g_local_file_output_stream_seek),
(g_local_file_output_stream_truncate), (copy_file_data),
(handle_overwrite_open):
* gio/gunixinputstream.c: (g_unix_input_stream_read),
(g_unix_input_stream_close), (read_async_cb), (close_async_cb):
* gio/gunixoutputstream.c: (g_unix_output_stream_write),
(g_unix_output_stream_close), (write_async_cb), (close_async_cb):
Save
errno before calling other funcs that potentially alter it. Bug
#514766.
svn path=/trunk/; revision=6466
2008-02-06 16:10:08 +01:00
|
|
|
int errsv = errno;
|
|
|
|
|
2007-11-26 17:13:05 +01:00
|
|
|
g_set_error (error, G_IO_ERROR,
|
Save errno before calling other funcs that potentially alter it. Bug
* gio/gdesktopappinfo.c: (ensure_dir):
* gio/glocalfile.c: (g_local_file_query_filesystem_info),
(g_local_file_read), (g_local_file_delete), (g_local_file_trash),
(g_local_file_move):
* gio/glocalfileinfo.c: (set_xattr), (_g_local_file_info_get),
(_g_local_file_info_get_from_fd), (set_unix_mode),
(set_unix_uid_gid), (set_symlink), (set_mtime_atime):
* gio/glocalfileinputstream.c: (g_local_file_input_stream_read),
(g_local_file_input_stream_skip),
(g_local_file_input_stream_close),
(g_local_file_input_stream_seek):
* gio/glocalfileoutputstream.c:
(g_local_file_output_stream_write),
(g_local_file_output_stream_close),
(g_local_file_output_stream_seek),
(g_local_file_output_stream_truncate), (copy_file_data),
(handle_overwrite_open):
* gio/gunixinputstream.c: (g_unix_input_stream_read),
(g_unix_input_stream_close), (read_async_cb), (close_async_cb):
* gio/gunixoutputstream.c: (g_unix_output_stream_write),
(g_unix_output_stream_close), (write_async_cb), (close_async_cb):
Save
errno before calling other funcs that potentially alter it. Bug
#514766.
svn path=/trunk/; revision=6466
2008-02-06 16:10:08 +01:00
|
|
|
g_io_error_from_errno (errsv),
|
2007-11-26 17:13:05 +01:00
|
|
|
_("Error seeking in file: %s"),
|
Save errno before calling other funcs that potentially alter it. Bug
* gio/gdesktopappinfo.c: (ensure_dir):
* gio/glocalfile.c: (g_local_file_query_filesystem_info),
(g_local_file_read), (g_local_file_delete), (g_local_file_trash),
(g_local_file_move):
* gio/glocalfileinfo.c: (set_xattr), (_g_local_file_info_get),
(_g_local_file_info_get_from_fd), (set_unix_mode),
(set_unix_uid_gid), (set_symlink), (set_mtime_atime):
* gio/glocalfileinputstream.c: (g_local_file_input_stream_read),
(g_local_file_input_stream_skip),
(g_local_file_input_stream_close),
(g_local_file_input_stream_seek):
* gio/glocalfileoutputstream.c:
(g_local_file_output_stream_write),
(g_local_file_output_stream_close),
(g_local_file_output_stream_seek),
(g_local_file_output_stream_truncate), (copy_file_data),
(handle_overwrite_open):
* gio/gunixinputstream.c: (g_unix_input_stream_read),
(g_unix_input_stream_close), (read_async_cb), (close_async_cb):
* gio/gunixoutputstream.c: (g_unix_output_stream_write),
(g_unix_output_stream_close), (write_async_cb), (close_async_cb):
Save
errno before calling other funcs that potentially alter it. Bug
#514766.
svn path=/trunk/; revision=6466
2008-02-06 16:10:08 +01:00
|
|
|
g_strerror (errsv));
|
2007-11-26 17:13:05 +01:00
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
2007-11-30 06:11:25 +01:00
|
|
|
g_local_file_output_stream_can_truncate (GFileOutputStream *stream)
|
2007-11-26 17:13:05 +01:00
|
|
|
{
|
|
|
|
/* We can't truncate pipes and stuff where we can't seek */
|
|
|
|
return g_local_file_output_stream_can_seek (stream);
|
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
2007-11-30 06:11:25 +01:00
|
|
|
g_local_file_output_stream_truncate (GFileOutputStream *stream,
|
|
|
|
goffset size,
|
|
|
|
GCancellable *cancellable,
|
|
|
|
GError **error)
|
2007-11-26 17:13:05 +01:00
|
|
|
{
|
|
|
|
GLocalFileOutputStream *file;
|
|
|
|
int res;
|
|
|
|
|
|
|
|
file = G_LOCAL_FILE_OUTPUT_STREAM (stream);
|
|
|
|
|
|
|
|
restart:
|
2007-12-08 13:01:06 +01:00
|
|
|
#ifdef G_OS_WIN32
|
|
|
|
res = g_win32_ftruncate (file->priv->fd, size);
|
|
|
|
#else
|
2007-11-30 06:11:25 +01:00
|
|
|
res = ftruncate (file->priv->fd, size);
|
2007-12-08 13:01:06 +01:00
|
|
|
#endif
|
2007-11-26 17:13:05 +01:00
|
|
|
|
|
|
|
if (res == -1)
|
|
|
|
{
|
Save errno before calling other funcs that potentially alter it. Bug
* gio/gdesktopappinfo.c: (ensure_dir):
* gio/glocalfile.c: (g_local_file_query_filesystem_info),
(g_local_file_read), (g_local_file_delete), (g_local_file_trash),
(g_local_file_move):
* gio/glocalfileinfo.c: (set_xattr), (_g_local_file_info_get),
(_g_local_file_info_get_from_fd), (set_unix_mode),
(set_unix_uid_gid), (set_symlink), (set_mtime_atime):
* gio/glocalfileinputstream.c: (g_local_file_input_stream_read),
(g_local_file_input_stream_skip),
(g_local_file_input_stream_close),
(g_local_file_input_stream_seek):
* gio/glocalfileoutputstream.c:
(g_local_file_output_stream_write),
(g_local_file_output_stream_close),
(g_local_file_output_stream_seek),
(g_local_file_output_stream_truncate), (copy_file_data),
(handle_overwrite_open):
* gio/gunixinputstream.c: (g_unix_input_stream_read),
(g_unix_input_stream_close), (read_async_cb), (close_async_cb):
* gio/gunixoutputstream.c: (g_unix_output_stream_write),
(g_unix_output_stream_close), (write_async_cb), (close_async_cb):
Save
errno before calling other funcs that potentially alter it. Bug
#514766.
svn path=/trunk/; revision=6466
2008-02-06 16:10:08 +01:00
|
|
|
int errsv = errno;
|
|
|
|
|
|
|
|
if (errsv == EINTR)
|
2007-11-26 17:13:05 +01:00
|
|
|
{
|
|
|
|
if (g_cancellable_set_error_if_cancelled (cancellable, error))
|
|
|
|
return FALSE;
|
|
|
|
goto restart;
|
|
|
|
}
|
|
|
|
|
|
|
|
g_set_error (error, G_IO_ERROR,
|
Save errno before calling other funcs that potentially alter it. Bug
* gio/gdesktopappinfo.c: (ensure_dir):
* gio/glocalfile.c: (g_local_file_query_filesystem_info),
(g_local_file_read), (g_local_file_delete), (g_local_file_trash),
(g_local_file_move):
* gio/glocalfileinfo.c: (set_xattr), (_g_local_file_info_get),
(_g_local_file_info_get_from_fd), (set_unix_mode),
(set_unix_uid_gid), (set_symlink), (set_mtime_atime):
* gio/glocalfileinputstream.c: (g_local_file_input_stream_read),
(g_local_file_input_stream_skip),
(g_local_file_input_stream_close),
(g_local_file_input_stream_seek):
* gio/glocalfileoutputstream.c:
(g_local_file_output_stream_write),
(g_local_file_output_stream_close),
(g_local_file_output_stream_seek),
(g_local_file_output_stream_truncate), (copy_file_data),
(handle_overwrite_open):
* gio/gunixinputstream.c: (g_unix_input_stream_read),
(g_unix_input_stream_close), (read_async_cb), (close_async_cb):
* gio/gunixoutputstream.c: (g_unix_output_stream_write),
(g_unix_output_stream_close), (write_async_cb), (close_async_cb):
Save
errno before calling other funcs that potentially alter it. Bug
#514766.
svn path=/trunk/; revision=6466
2008-02-06 16:10:08 +01:00
|
|
|
g_io_error_from_errno (errsv),
|
2007-11-26 17:13:05 +01:00
|
|
|
_("Error truncating file: %s"),
|
Save errno before calling other funcs that potentially alter it. Bug
* gio/gdesktopappinfo.c: (ensure_dir):
* gio/glocalfile.c: (g_local_file_query_filesystem_info),
(g_local_file_read), (g_local_file_delete), (g_local_file_trash),
(g_local_file_move):
* gio/glocalfileinfo.c: (set_xattr), (_g_local_file_info_get),
(_g_local_file_info_get_from_fd), (set_unix_mode),
(set_unix_uid_gid), (set_symlink), (set_mtime_atime):
* gio/glocalfileinputstream.c: (g_local_file_input_stream_read),
(g_local_file_input_stream_skip),
(g_local_file_input_stream_close),
(g_local_file_input_stream_seek):
* gio/glocalfileoutputstream.c:
(g_local_file_output_stream_write),
(g_local_file_output_stream_close),
(g_local_file_output_stream_seek),
(g_local_file_output_stream_truncate), (copy_file_data),
(handle_overwrite_open):
* gio/gunixinputstream.c: (g_unix_input_stream_read),
(g_unix_input_stream_close), (read_async_cb), (close_async_cb):
* gio/gunixoutputstream.c: (g_unix_output_stream_write),
(g_unix_output_stream_close), (write_async_cb), (close_async_cb):
Save
errno before calling other funcs that potentially alter it. Bug
#514766.
svn path=/trunk/; revision=6466
2008-02-06 16:10:08 +01:00
|
|
|
g_strerror (errsv));
|
2007-11-26 17:13:05 +01:00
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static GFileInfo *
|
2007-11-30 06:11:25 +01:00
|
|
|
g_local_file_output_stream_query_info (GFileOutputStream *stream,
|
2009-03-17 12:21:37 +01:00
|
|
|
const char *attributes,
|
2007-11-30 06:11:25 +01:00
|
|
|
GCancellable *cancellable,
|
|
|
|
GError **error)
|
2007-11-26 17:13:05 +01:00
|
|
|
{
|
|
|
|
GLocalFileOutputStream *file;
|
|
|
|
|
|
|
|
file = G_LOCAL_FILE_OUTPUT_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);
|
|
|
|
}
|
|
|
|
|
2011-12-08 23:17:07 +01:00
|
|
|
GFileOutputStream *
|
|
|
|
_g_local_file_output_stream_new (int fd)
|
|
|
|
{
|
|
|
|
GLocalFileOutputStream *stream;
|
|
|
|
|
|
|
|
stream = g_object_new (G_TYPE_LOCAL_FILE_OUTPUT_STREAM, NULL);
|
|
|
|
stream->priv->fd = fd;
|
|
|
|
return G_FILE_OUTPUT_STREAM (stream);
|
|
|
|
}
|
|
|
|
|
2013-05-09 19:47:50 +02:00
|
|
|
static void
|
|
|
|
set_error_from_open_errno (const char *filename,
|
|
|
|
GError **error)
|
|
|
|
{
|
|
|
|
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),
|
2016-09-30 05:47:15 +02:00
|
|
|
_("Error opening file “%s”: %s"),
|
2013-05-09 19:47:50 +02:00
|
|
|
display_name, g_strerror (errsv));
|
|
|
|
g_free (display_name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-05-09 19:39:50 +02:00
|
|
|
static GFileOutputStream *
|
|
|
|
output_stream_open (const char *filename,
|
|
|
|
gint open_flags,
|
|
|
|
guint mode,
|
|
|
|
GCancellable *cancellable,
|
|
|
|
GError **error)
|
2009-05-13 13:03:47 +02:00
|
|
|
{
|
|
|
|
GLocalFileOutputStream *stream;
|
2013-05-09 19:39:50 +02:00
|
|
|
gint fd;
|
2009-05-13 13:03:47 +02:00
|
|
|
|
2013-05-09 19:39:50 +02:00
|
|
|
fd = g_open (filename, open_flags, mode);
|
2009-05-13 13:03:47 +02:00
|
|
|
if (fd == -1)
|
|
|
|
{
|
2013-05-09 19:47:50 +02:00
|
|
|
set_error_from_open_errno (filename, error);
|
2009-05-13 13:03:47 +02:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
stream = g_object_new (G_TYPE_LOCAL_FILE_OUTPUT_STREAM, NULL);
|
|
|
|
stream->priv->fd = fd;
|
|
|
|
return G_FILE_OUTPUT_STREAM (stream);
|
|
|
|
}
|
|
|
|
|
2013-05-09 19:39:50 +02:00
|
|
|
GFileOutputStream *
|
|
|
|
_g_local_file_output_stream_open (const char *filename,
|
|
|
|
gboolean readable,
|
|
|
|
GCancellable *cancellable,
|
|
|
|
GError **error)
|
|
|
|
{
|
|
|
|
int open_flags;
|
|
|
|
|
|
|
|
if (g_cancellable_set_error_if_cancelled (cancellable, error))
|
|
|
|
return NULL;
|
|
|
|
|
2023-02-19 16:22:22 +01:00
|
|
|
open_flags = O_BINARY | O_CLOEXEC;
|
2013-05-09 19:39:50 +02:00
|
|
|
if (readable)
|
|
|
|
open_flags |= O_RDWR;
|
|
|
|
else
|
|
|
|
open_flags |= O_WRONLY;
|
|
|
|
|
|
|
|
return output_stream_open (filename, open_flags, 0666, cancellable, error);
|
|
|
|
}
|
|
|
|
|
Ensure g_file_copy() does not temporarily expose private files
Previously, g_file_copy() would (on Unix) create files with the
default mode of 644. For applications which might at user request
copy arbitrary private files such as ~/.ssh or /etc/shadow, a
world-readable copy would be temporarily exposed.
This patch is suboptimal in that it *only* fixes g_file_copy()
for the case where both source and destination are instances of
GLocalFile on Unix.
The reason for this is that the public GFile APIs for creating files
allow very limited control over the access permissions for the created
file; one can either say a file is "private" or not. Fixing
this by adding e.g. g_file_create_with_attributes() would make sense,
except this would entail 8 new API calls for all the variants of
_create(), _create_async(), _replace(), _replace_async(),
_create_readwrite(), _create_readwrite_async(), _replace_readwrite(),
_replace_readwrite_async(). That can be done as a separate patch
later.
https://bugzilla.gnome.org/show_bug.cgi?id=699959
2013-05-12 08:28:01 +02:00
|
|
|
static gint
|
|
|
|
mode_from_flags_or_info (GFileCreateFlags flags,
|
|
|
|
GFileInfo *reference_info)
|
|
|
|
{
|
|
|
|
if (flags & G_FILE_CREATE_PRIVATE)
|
|
|
|
return 0600;
|
|
|
|
else if (reference_info && g_file_info_has_attribute (reference_info, "unix::mode"))
|
|
|
|
return g_file_info_get_attribute_uint32 (reference_info, "unix::mode") & (~S_IFMT);
|
|
|
|
else
|
|
|
|
return 0666;
|
|
|
|
}
|
|
|
|
|
2007-11-26 17:13:05 +01:00
|
|
|
GFileOutputStream *
|
2007-11-30 06:11:25 +01:00
|
|
|
_g_local_file_output_stream_create (const char *filename,
|
2009-05-13 13:03:47 +02:00
|
|
|
gboolean readable,
|
2007-11-30 06:11:25 +01:00
|
|
|
GFileCreateFlags flags,
|
Ensure g_file_copy() does not temporarily expose private files
Previously, g_file_copy() would (on Unix) create files with the
default mode of 644. For applications which might at user request
copy arbitrary private files such as ~/.ssh or /etc/shadow, a
world-readable copy would be temporarily exposed.
This patch is suboptimal in that it *only* fixes g_file_copy()
for the case where both source and destination are instances of
GLocalFile on Unix.
The reason for this is that the public GFile APIs for creating files
allow very limited control over the access permissions for the created
file; one can either say a file is "private" or not. Fixing
this by adding e.g. g_file_create_with_attributes() would make sense,
except this would entail 8 new API calls for all the variants of
_create(), _create_async(), _replace(), _replace_async(),
_create_readwrite(), _create_readwrite_async(), _replace_readwrite(),
_replace_readwrite_async(). That can be done as a separate patch
later.
https://bugzilla.gnome.org/show_bug.cgi?id=699959
2013-05-12 08:28:01 +02:00
|
|
|
GFileInfo *reference_info,
|
2007-11-30 06:11:25 +01:00
|
|
|
GCancellable *cancellable,
|
|
|
|
GError **error)
|
2007-11-26 17:13:05 +01:00
|
|
|
{
|
|
|
|
int mode;
|
2009-05-13 13:03:47 +02:00
|
|
|
int open_flags;
|
2007-11-26 17:13:05 +01:00
|
|
|
|
|
|
|
if (g_cancellable_set_error_if_cancelled (cancellable, error))
|
|
|
|
return NULL;
|
|
|
|
|
Ensure g_file_copy() does not temporarily expose private files
Previously, g_file_copy() would (on Unix) create files with the
default mode of 644. For applications which might at user request
copy arbitrary private files such as ~/.ssh or /etc/shadow, a
world-readable copy would be temporarily exposed.
This patch is suboptimal in that it *only* fixes g_file_copy()
for the case where both source and destination are instances of
GLocalFile on Unix.
The reason for this is that the public GFile APIs for creating files
allow very limited control over the access permissions for the created
file; one can either say a file is "private" or not. Fixing
this by adding e.g. g_file_create_with_attributes() would make sense,
except this would entail 8 new API calls for all the variants of
_create(), _create_async(), _replace(), _replace_async(),
_create_readwrite(), _create_readwrite_async(), _replace_readwrite(),
_replace_readwrite_async(). That can be done as a separate patch
later.
https://bugzilla.gnome.org/show_bug.cgi?id=699959
2013-05-12 08:28:01 +02:00
|
|
|
mode = mode_from_flags_or_info (flags, reference_info);
|
2009-05-13 13:03:47 +02:00
|
|
|
|
2023-02-19 16:22:22 +01:00
|
|
|
open_flags = O_CREAT | O_EXCL | O_BINARY | O_CLOEXEC;
|
2009-05-13 13:03:47 +02:00
|
|
|
if (readable)
|
|
|
|
open_flags |= O_RDWR;
|
|
|
|
else
|
|
|
|
open_flags |= O_WRONLY;
|
|
|
|
|
2013-05-09 19:39:50 +02:00
|
|
|
return output_stream_open (filename, open_flags, mode, cancellable, error);
|
2007-11-26 17:13:05 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
GFileOutputStream *
|
2007-11-30 06:11:25 +01:00
|
|
|
_g_local_file_output_stream_append (const char *filename,
|
|
|
|
GFileCreateFlags flags,
|
|
|
|
GCancellable *cancellable,
|
|
|
|
GError **error)
|
2007-11-26 17:13:05 +01:00
|
|
|
{
|
|
|
|
int mode;
|
|
|
|
|
|
|
|
if (g_cancellable_set_error_if_cancelled (cancellable, error))
|
|
|
|
return NULL;
|
|
|
|
|
2007-12-14 12:07:31 +01:00
|
|
|
if (flags & G_FILE_CREATE_PRIVATE)
|
2007-11-26 17:13:05 +01:00
|
|
|
mode = 0600;
|
|
|
|
else
|
|
|
|
mode = 0666;
|
|
|
|
|
2023-02-19 16:22:22 +01:00
|
|
|
return output_stream_open (filename, O_CREAT | O_APPEND | O_WRONLY | O_BINARY | O_CLOEXEC, mode,
|
2013-05-09 19:39:50 +02:00
|
|
|
cancellable, error);
|
2007-11-26 17:13:05 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static char *
|
|
|
|
create_backup_filename (const char *filename)
|
|
|
|
{
|
|
|
|
return g_strconcat (filename, BACKUP_EXTENSION, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
#define BUFSIZE 8192 /* size of normal write buffer */
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
copy_file_data (gint sfd,
|
|
|
|
gint dfd,
|
|
|
|
GError **error)
|
|
|
|
{
|
|
|
|
gboolean ret = TRUE;
|
|
|
|
gpointer buffer;
|
|
|
|
const gchar *write_buffer;
|
2007-12-03 23:37:44 +01:00
|
|
|
gssize bytes_read;
|
|
|
|
gssize bytes_to_write;
|
|
|
|
gssize bytes_written;
|
2007-11-26 17:13:05 +01:00
|
|
|
|
|
|
|
buffer = g_malloc (BUFSIZE);
|
|
|
|
|
|
|
|
do
|
|
|
|
{
|
|
|
|
bytes_read = read (sfd, buffer, BUFSIZE);
|
|
|
|
if (bytes_read == -1)
|
|
|
|
{
|
Save errno before calling other funcs that potentially alter it. Bug
* gio/gdesktopappinfo.c: (ensure_dir):
* gio/glocalfile.c: (g_local_file_query_filesystem_info),
(g_local_file_read), (g_local_file_delete), (g_local_file_trash),
(g_local_file_move):
* gio/glocalfileinfo.c: (set_xattr), (_g_local_file_info_get),
(_g_local_file_info_get_from_fd), (set_unix_mode),
(set_unix_uid_gid), (set_symlink), (set_mtime_atime):
* gio/glocalfileinputstream.c: (g_local_file_input_stream_read),
(g_local_file_input_stream_skip),
(g_local_file_input_stream_close),
(g_local_file_input_stream_seek):
* gio/glocalfileoutputstream.c:
(g_local_file_output_stream_write),
(g_local_file_output_stream_close),
(g_local_file_output_stream_seek),
(g_local_file_output_stream_truncate), (copy_file_data),
(handle_overwrite_open):
* gio/gunixinputstream.c: (g_unix_input_stream_read),
(g_unix_input_stream_close), (read_async_cb), (close_async_cb):
* gio/gunixoutputstream.c: (g_unix_output_stream_write),
(g_unix_output_stream_close), (write_async_cb), (close_async_cb):
Save
errno before calling other funcs that potentially alter it. Bug
#514766.
svn path=/trunk/; revision=6466
2008-02-06 16:10:08 +01:00
|
|
|
int errsv = errno;
|
|
|
|
|
|
|
|
if (errsv == EINTR)
|
2007-11-26 17:13:05 +01:00
|
|
|
continue;
|
|
|
|
|
|
|
|
g_set_error (error, G_IO_ERROR,
|
Save errno before calling other funcs that potentially alter it. Bug
* gio/gdesktopappinfo.c: (ensure_dir):
* gio/glocalfile.c: (g_local_file_query_filesystem_info),
(g_local_file_read), (g_local_file_delete), (g_local_file_trash),
(g_local_file_move):
* gio/glocalfileinfo.c: (set_xattr), (_g_local_file_info_get),
(_g_local_file_info_get_from_fd), (set_unix_mode),
(set_unix_uid_gid), (set_symlink), (set_mtime_atime):
* gio/glocalfileinputstream.c: (g_local_file_input_stream_read),
(g_local_file_input_stream_skip),
(g_local_file_input_stream_close),
(g_local_file_input_stream_seek):
* gio/glocalfileoutputstream.c:
(g_local_file_output_stream_write),
(g_local_file_output_stream_close),
(g_local_file_output_stream_seek),
(g_local_file_output_stream_truncate), (copy_file_data),
(handle_overwrite_open):
* gio/gunixinputstream.c: (g_unix_input_stream_read),
(g_unix_input_stream_close), (read_async_cb), (close_async_cb):
* gio/gunixoutputstream.c: (g_unix_output_stream_write),
(g_unix_output_stream_close), (write_async_cb), (close_async_cb):
Save
errno before calling other funcs that potentially alter it. Bug
#514766.
svn path=/trunk/; revision=6466
2008-02-06 16:10:08 +01:00
|
|
|
g_io_error_from_errno (errsv),
|
2007-11-26 17:13:05 +01:00
|
|
|
_("Error reading from file: %s"),
|
Save errno before calling other funcs that potentially alter it. Bug
* gio/gdesktopappinfo.c: (ensure_dir):
* gio/glocalfile.c: (g_local_file_query_filesystem_info),
(g_local_file_read), (g_local_file_delete), (g_local_file_trash),
(g_local_file_move):
* gio/glocalfileinfo.c: (set_xattr), (_g_local_file_info_get),
(_g_local_file_info_get_from_fd), (set_unix_mode),
(set_unix_uid_gid), (set_symlink), (set_mtime_atime):
* gio/glocalfileinputstream.c: (g_local_file_input_stream_read),
(g_local_file_input_stream_skip),
(g_local_file_input_stream_close),
(g_local_file_input_stream_seek):
* gio/glocalfileoutputstream.c:
(g_local_file_output_stream_write),
(g_local_file_output_stream_close),
(g_local_file_output_stream_seek),
(g_local_file_output_stream_truncate), (copy_file_data),
(handle_overwrite_open):
* gio/gunixinputstream.c: (g_unix_input_stream_read),
(g_unix_input_stream_close), (read_async_cb), (close_async_cb):
* gio/gunixoutputstream.c: (g_unix_output_stream_write),
(g_unix_output_stream_close), (write_async_cb), (close_async_cb):
Save
errno before calling other funcs that potentially alter it. Bug
#514766.
svn path=/trunk/; revision=6466
2008-02-06 16:10:08 +01:00
|
|
|
g_strerror (errsv));
|
2007-11-26 17:13:05 +01:00
|
|
|
ret = FALSE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
bytes_to_write = bytes_read;
|
|
|
|
write_buffer = buffer;
|
|
|
|
|
|
|
|
do
|
|
|
|
{
|
|
|
|
bytes_written = write (dfd, write_buffer, bytes_to_write);
|
|
|
|
if (bytes_written == -1)
|
|
|
|
{
|
Save errno before calling other funcs that potentially alter it. Bug
* gio/gdesktopappinfo.c: (ensure_dir):
* gio/glocalfile.c: (g_local_file_query_filesystem_info),
(g_local_file_read), (g_local_file_delete), (g_local_file_trash),
(g_local_file_move):
* gio/glocalfileinfo.c: (set_xattr), (_g_local_file_info_get),
(_g_local_file_info_get_from_fd), (set_unix_mode),
(set_unix_uid_gid), (set_symlink), (set_mtime_atime):
* gio/glocalfileinputstream.c: (g_local_file_input_stream_read),
(g_local_file_input_stream_skip),
(g_local_file_input_stream_close),
(g_local_file_input_stream_seek):
* gio/glocalfileoutputstream.c:
(g_local_file_output_stream_write),
(g_local_file_output_stream_close),
(g_local_file_output_stream_seek),
(g_local_file_output_stream_truncate), (copy_file_data),
(handle_overwrite_open):
* gio/gunixinputstream.c: (g_unix_input_stream_read),
(g_unix_input_stream_close), (read_async_cb), (close_async_cb):
* gio/gunixoutputstream.c: (g_unix_output_stream_write),
(g_unix_output_stream_close), (write_async_cb), (close_async_cb):
Save
errno before calling other funcs that potentially alter it. Bug
#514766.
svn path=/trunk/; revision=6466
2008-02-06 16:10:08 +01:00
|
|
|
int errsv = errno;
|
|
|
|
|
|
|
|
if (errsv == EINTR)
|
2007-11-26 17:13:05 +01:00
|
|
|
continue;
|
|
|
|
|
|
|
|
g_set_error (error, G_IO_ERROR,
|
Save errno before calling other funcs that potentially alter it. Bug
* gio/gdesktopappinfo.c: (ensure_dir):
* gio/glocalfile.c: (g_local_file_query_filesystem_info),
(g_local_file_read), (g_local_file_delete), (g_local_file_trash),
(g_local_file_move):
* gio/glocalfileinfo.c: (set_xattr), (_g_local_file_info_get),
(_g_local_file_info_get_from_fd), (set_unix_mode),
(set_unix_uid_gid), (set_symlink), (set_mtime_atime):
* gio/glocalfileinputstream.c: (g_local_file_input_stream_read),
(g_local_file_input_stream_skip),
(g_local_file_input_stream_close),
(g_local_file_input_stream_seek):
* gio/glocalfileoutputstream.c:
(g_local_file_output_stream_write),
(g_local_file_output_stream_close),
(g_local_file_output_stream_seek),
(g_local_file_output_stream_truncate), (copy_file_data),
(handle_overwrite_open):
* gio/gunixinputstream.c: (g_unix_input_stream_read),
(g_unix_input_stream_close), (read_async_cb), (close_async_cb):
* gio/gunixoutputstream.c: (g_unix_output_stream_write),
(g_unix_output_stream_close), (write_async_cb), (close_async_cb):
Save
errno before calling other funcs that potentially alter it. Bug
#514766.
svn path=/trunk/; revision=6466
2008-02-06 16:10:08 +01:00
|
|
|
g_io_error_from_errno (errsv),
|
2007-11-26 17:13:05 +01:00
|
|
|
_("Error writing to file: %s"),
|
Save errno before calling other funcs that potentially alter it. Bug
* gio/gdesktopappinfo.c: (ensure_dir):
* gio/glocalfile.c: (g_local_file_query_filesystem_info),
(g_local_file_read), (g_local_file_delete), (g_local_file_trash),
(g_local_file_move):
* gio/glocalfileinfo.c: (set_xattr), (_g_local_file_info_get),
(_g_local_file_info_get_from_fd), (set_unix_mode),
(set_unix_uid_gid), (set_symlink), (set_mtime_atime):
* gio/glocalfileinputstream.c: (g_local_file_input_stream_read),
(g_local_file_input_stream_skip),
(g_local_file_input_stream_close),
(g_local_file_input_stream_seek):
* gio/glocalfileoutputstream.c:
(g_local_file_output_stream_write),
(g_local_file_output_stream_close),
(g_local_file_output_stream_seek),
(g_local_file_output_stream_truncate), (copy_file_data),
(handle_overwrite_open):
* gio/gunixinputstream.c: (g_unix_input_stream_read),
(g_unix_input_stream_close), (read_async_cb), (close_async_cb):
* gio/gunixoutputstream.c: (g_unix_output_stream_write),
(g_unix_output_stream_close), (write_async_cb), (close_async_cb):
Save
errno before calling other funcs that potentially alter it. Bug
#514766.
svn path=/trunk/; revision=6466
2008-02-06 16:10:08 +01:00
|
|
|
g_strerror (errsv));
|
2007-11-26 17:13:05 +01:00
|
|
|
ret = FALSE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
bytes_to_write -= bytes_written;
|
|
|
|
write_buffer += bytes_written;
|
|
|
|
}
|
|
|
|
while (bytes_to_write > 0);
|
|
|
|
|
|
|
|
} while ((bytes_read != 0) && (ret == TRUE));
|
|
|
|
|
|
|
|
g_free (buffer);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2007-11-30 06:11:25 +01:00
|
|
|
handle_overwrite_open (const char *filename,
|
2009-05-13 13:03:47 +02:00
|
|
|
gboolean readable,
|
2007-11-30 06:11:25 +01:00
|
|
|
const char *etag,
|
|
|
|
gboolean create_backup,
|
|
|
|
char **temp_filename,
|
2009-02-18 15:49:25 +01:00
|
|
|
GFileCreateFlags flags,
|
Ensure g_file_copy() does not temporarily expose private files
Previously, g_file_copy() would (on Unix) create files with the
default mode of 644. For applications which might at user request
copy arbitrary private files such as ~/.ssh or /etc/shadow, a
world-readable copy would be temporarily exposed.
This patch is suboptimal in that it *only* fixes g_file_copy()
for the case where both source and destination are instances of
GLocalFile on Unix.
The reason for this is that the public GFile APIs for creating files
allow very limited control over the access permissions for the created
file; one can either say a file is "private" or not. Fixing
this by adding e.g. g_file_create_with_attributes() would make sense,
except this would entail 8 new API calls for all the variants of
_create(), _create_async(), _replace(), _replace_async(),
_create_readwrite(), _create_readwrite_async(), _replace_readwrite(),
_replace_readwrite_async(). That can be done as a separate patch
later.
https://bugzilla.gnome.org/show_bug.cgi?id=699959
2013-05-12 08:28:01 +02:00
|
|
|
GFileInfo *reference_info,
|
2007-11-30 06:11:25 +01:00
|
|
|
GCancellable *cancellable,
|
|
|
|
GError **error)
|
2007-11-26 17:13:05 +01:00
|
|
|
{
|
|
|
|
int fd = -1;
|
2008-08-23 03:09:08 +02:00
|
|
|
GLocalFileStat original_stat;
|
2007-11-26 17:13:05 +01:00
|
|
|
char *current_etag;
|
|
|
|
gboolean is_symlink;
|
|
|
|
int open_flags;
|
2008-08-23 03:09:08 +02:00
|
|
|
int res;
|
2009-02-18 15:49:25 +01:00
|
|
|
int mode;
|
2022-01-19 19:38:41 +01:00
|
|
|
int errsv = 0;
|
2021-03-10 17:05:55 +01:00
|
|
|
gboolean replace_destination_set = (flags & G_FILE_CREATE_REPLACE_DESTINATION);
|
2009-02-18 15:49:25 +01:00
|
|
|
|
Ensure g_file_copy() does not temporarily expose private files
Previously, g_file_copy() would (on Unix) create files with the
default mode of 644. For applications which might at user request
copy arbitrary private files such as ~/.ssh or /etc/shadow, a
world-readable copy would be temporarily exposed.
This patch is suboptimal in that it *only* fixes g_file_copy()
for the case where both source and destination are instances of
GLocalFile on Unix.
The reason for this is that the public GFile APIs for creating files
allow very limited control over the access permissions for the created
file; one can either say a file is "private" or not. Fixing
this by adding e.g. g_file_create_with_attributes() would make sense,
except this would entail 8 new API calls for all the variants of
_create(), _create_async(), _replace(), _replace_async(),
_create_readwrite(), _create_readwrite_async(), _replace_readwrite(),
_replace_readwrite_async(). That can be done as a separate patch
later.
https://bugzilla.gnome.org/show_bug.cgi?id=699959
2013-05-12 08:28:01 +02:00
|
|
|
mode = mode_from_flags_or_info (flags, reference_info);
|
2007-11-26 17:13:05 +01:00
|
|
|
|
|
|
|
/* We only need read access to the original file if we are creating a backup.
|
2021-02-24 18:33:38 +01:00
|
|
|
* We also add O_CREAT to avoid a race if the file was just removed */
|
2009-05-13 13:03:47 +02:00
|
|
|
if (create_backup || readable)
|
2023-02-19 16:22:22 +01:00
|
|
|
open_flags = O_RDWR | O_CREAT | O_BINARY | O_CLOEXEC;
|
2007-11-26 17:13:05 +01:00
|
|
|
else
|
2023-02-19 16:22:22 +01:00
|
|
|
open_flags = O_WRONLY | O_CREAT | O_BINARY | O_CLOEXEC;
|
2007-11-26 17:13:05 +01:00
|
|
|
|
|
|
|
/* Some systems have O_NOFOLLOW, which lets us avoid some races
|
|
|
|
* when finding out if the file we opened was a symlink */
|
|
|
|
#ifdef O_NOFOLLOW
|
|
|
|
is_symlink = FALSE;
|
2009-02-18 15:49:25 +01:00
|
|
|
fd = g_open (filename, open_flags | O_NOFOLLOW, mode);
|
2017-07-31 12:30:55 +02:00
|
|
|
errsv = errno;
|
2016-12-04 08:02:54 +01:00
|
|
|
#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
|
2017-07-31 12:30:55 +02:00
|
|
|
if (fd == -1 && errsv == EMLINK)
|
2016-12-04 08:02:54 +01:00
|
|
|
#elif defined(__NetBSD__)
|
2017-07-31 12:30:55 +02:00
|
|
|
if (fd == -1 && errsv == EFTYPE)
|
2016-12-04 08:02:54 +01:00
|
|
|
#else
|
2017-07-31 12:30:55 +02:00
|
|
|
if (fd == -1 && errsv == ELOOP)
|
2016-12-04 08:02:54 +01:00
|
|
|
#endif
|
2007-11-26 17:13:05 +01:00
|
|
|
{
|
|
|
|
/* Could be a symlink, or it could be a regular ELOOP error,
|
|
|
|
* but then the next open will fail too. */
|
|
|
|
is_symlink = TRUE;
|
glocalfileoutputstream: Fix CREATE_REPLACE_DESTINATION with symlinks
The `G_FILE_CREATE_REPLACE_DESTINATION` flag is equivalent to unlinking
the destination file and re-creating it from scratch. That did
previously work, but in the process the code would call `open(O_CREAT)`
on the file. If the file was a dangling symlink, this would create the
destination file (empty). That’s not an intended side-effect, and has
security implications if the symlink is controlled by a lower-privileged
process.
Fix that by not opening the destination file if it’s a symlink, and
adjusting the rest of the code to cope with
- the fact that `fd == -1` is not an error iff `is_symlink` is true,
- and that `original_stat` will contain the `lstat()` results for the
symlink now, rather than the `stat()` results for its target (again,
iff `is_symlink` is true).
This means that the target of the dangling symlink is no longer created,
which was the bug. The symlink itself continues to be replaced (as
before) with the new file — this is the intended behaviour of
`g_file_replace()`.
The behaviour for non-symlink cases, or cases where the symlink was not
dangling, should be unchanged.
Includes a unit test.
Signed-off-by: Philip Withnall <pwithnall@endlessos.org>
Fixes: #2325
2021-02-24 18:36:07 +01:00
|
|
|
if (!replace_destination_set)
|
|
|
|
fd = g_open (filename, open_flags, mode);
|
2007-11-26 17:13:05 +01:00
|
|
|
}
|
glocalfileoutputstream: Fix CREATE_REPLACE_DESTINATION with symlinks
The `G_FILE_CREATE_REPLACE_DESTINATION` flag is equivalent to unlinking
the destination file and re-creating it from scratch. That did
previously work, but in the process the code would call `open(O_CREAT)`
on the file. If the file was a dangling symlink, this would create the
destination file (empty). That’s not an intended side-effect, and has
security implications if the symlink is controlled by a lower-privileged
process.
Fix that by not opening the destination file if it’s a symlink, and
adjusting the rest of the code to cope with
- the fact that `fd == -1` is not an error iff `is_symlink` is true,
- and that `original_stat` will contain the `lstat()` results for the
symlink now, rather than the `stat()` results for its target (again,
iff `is_symlink` is true).
This means that the target of the dangling symlink is no longer created,
which was the bug. The symlink itself continues to be replaced (as
before) with the new file — this is the intended behaviour of
`g_file_replace()`.
The behaviour for non-symlink cases, or cases where the symlink was not
dangling, should be unchanged.
Includes a unit test.
Signed-off-by: Philip Withnall <pwithnall@endlessos.org>
Fixes: #2325
2021-02-24 18:36:07 +01:00
|
|
|
#else /* if !O_NOFOLLOW */
|
2007-11-26 17:13:05 +01:00
|
|
|
/* This is racy, but we do it as soon as possible to minimize the race */
|
|
|
|
is_symlink = g_file_test (filename, G_FILE_TEST_IS_SYMLINK);
|
glocalfileoutputstream: Fix CREATE_REPLACE_DESTINATION with symlinks
The `G_FILE_CREATE_REPLACE_DESTINATION` flag is equivalent to unlinking
the destination file and re-creating it from scratch. That did
previously work, but in the process the code would call `open(O_CREAT)`
on the file. If the file was a dangling symlink, this would create the
destination file (empty). That’s not an intended side-effect, and has
security implications if the symlink is controlled by a lower-privileged
process.
Fix that by not opening the destination file if it’s a symlink, and
adjusting the rest of the code to cope with
- the fact that `fd == -1` is not an error iff `is_symlink` is true,
- and that `original_stat` will contain the `lstat()` results for the
symlink now, rather than the `stat()` results for its target (again,
iff `is_symlink` is true).
This means that the target of the dangling symlink is no longer created,
which was the bug. The symlink itself continues to be replaced (as
before) with the new file — this is the intended behaviour of
`g_file_replace()`.
The behaviour for non-symlink cases, or cases where the symlink was not
dangling, should be unchanged.
Includes a unit test.
Signed-off-by: Philip Withnall <pwithnall@endlessos.org>
Fixes: #2325
2021-02-24 18:36:07 +01:00
|
|
|
|
|
|
|
if (!is_symlink || !replace_destination_set)
|
|
|
|
{
|
|
|
|
fd = g_open (filename, open_flags, mode);
|
|
|
|
errsv = errno;
|
|
|
|
}
|
2007-11-26 17:13:05 +01:00
|
|
|
#endif
|
2017-07-31 12:30:55 +02:00
|
|
|
|
glocalfileoutputstream: Fix CREATE_REPLACE_DESTINATION with symlinks
The `G_FILE_CREATE_REPLACE_DESTINATION` flag is equivalent to unlinking
the destination file and re-creating it from scratch. That did
previously work, but in the process the code would call `open(O_CREAT)`
on the file. If the file was a dangling symlink, this would create the
destination file (empty). That’s not an intended side-effect, and has
security implications if the symlink is controlled by a lower-privileged
process.
Fix that by not opening the destination file if it’s a symlink, and
adjusting the rest of the code to cope with
- the fact that `fd == -1` is not an error iff `is_symlink` is true,
- and that `original_stat` will contain the `lstat()` results for the
symlink now, rather than the `stat()` results for its target (again,
iff `is_symlink` is true).
This means that the target of the dangling symlink is no longer created,
which was the bug. The symlink itself continues to be replaced (as
before) with the new file — this is the intended behaviour of
`g_file_replace()`.
The behaviour for non-symlink cases, or cases where the symlink was not
dangling, should be unchanged.
Includes a unit test.
Signed-off-by: Philip Withnall <pwithnall@endlessos.org>
Fixes: #2325
2021-02-24 18:36:07 +01:00
|
|
|
if (fd == -1 &&
|
|
|
|
(!is_symlink || !replace_destination_set))
|
2007-11-26 17:13:05 +01:00
|
|
|
{
|
2008-02-18 16:35:16 +01:00
|
|
|
char *display_name = g_filename_display_name (filename);
|
2007-11-26 17:13:05 +01:00
|
|
|
g_set_error (error, G_IO_ERROR,
|
Save errno before calling other funcs that potentially alter it. Bug
* gio/gdesktopappinfo.c: (ensure_dir):
* gio/glocalfile.c: (g_local_file_query_filesystem_info),
(g_local_file_read), (g_local_file_delete), (g_local_file_trash),
(g_local_file_move):
* gio/glocalfileinfo.c: (set_xattr), (_g_local_file_info_get),
(_g_local_file_info_get_from_fd), (set_unix_mode),
(set_unix_uid_gid), (set_symlink), (set_mtime_atime):
* gio/glocalfileinputstream.c: (g_local_file_input_stream_read),
(g_local_file_input_stream_skip),
(g_local_file_input_stream_close),
(g_local_file_input_stream_seek):
* gio/glocalfileoutputstream.c:
(g_local_file_output_stream_write),
(g_local_file_output_stream_close),
(g_local_file_output_stream_seek),
(g_local_file_output_stream_truncate), (copy_file_data),
(handle_overwrite_open):
* gio/gunixinputstream.c: (g_unix_input_stream_read),
(g_unix_input_stream_close), (read_async_cb), (close_async_cb):
* gio/gunixoutputstream.c: (g_unix_output_stream_write),
(g_unix_output_stream_close), (write_async_cb), (close_async_cb):
Save
errno before calling other funcs that potentially alter it. Bug
#514766.
svn path=/trunk/; revision=6466
2008-02-06 16:10:08 +01:00
|
|
|
g_io_error_from_errno (errsv),
|
2016-09-30 05:47:15 +02:00
|
|
|
_("Error opening file “%s”: %s"),
|
2008-02-18 16:35:16 +01:00
|
|
|
display_name, g_strerror (errsv));
|
|
|
|
g_free (display_name);
|
2007-11-26 17:13:05 +01:00
|
|
|
return -1;
|
|
|
|
}
|
2020-08-14 16:52:03 +02:00
|
|
|
|
glocalfileoutputstream: Fix CREATE_REPLACE_DESTINATION with symlinks
The `G_FILE_CREATE_REPLACE_DESTINATION` flag is equivalent to unlinking
the destination file and re-creating it from scratch. That did
previously work, but in the process the code would call `open(O_CREAT)`
on the file. If the file was a dangling symlink, this would create the
destination file (empty). That’s not an intended side-effect, and has
security implications if the symlink is controlled by a lower-privileged
process.
Fix that by not opening the destination file if it’s a symlink, and
adjusting the rest of the code to cope with
- the fact that `fd == -1` is not an error iff `is_symlink` is true,
- and that `original_stat` will contain the `lstat()` results for the
symlink now, rather than the `stat()` results for its target (again,
iff `is_symlink` is true).
This means that the target of the dangling symlink is no longer created,
which was the bug. The symlink itself continues to be replaced (as
before) with the new file — this is the intended behaviour of
`g_file_replace()`.
The behaviour for non-symlink cases, or cases where the symlink was not
dangling, should be unchanged.
Includes a unit test.
Signed-off-by: Philip Withnall <pwithnall@endlessos.org>
Fixes: #2325
2021-02-24 18:36:07 +01:00
|
|
|
if (!is_symlink)
|
|
|
|
{
|
|
|
|
res = g_local_file_fstat (fd,
|
|
|
|
G_LOCAL_FILE_STAT_FIELD_TYPE |
|
|
|
|
G_LOCAL_FILE_STAT_FIELD_MODE |
|
|
|
|
G_LOCAL_FILE_STAT_FIELD_UID |
|
|
|
|
G_LOCAL_FILE_STAT_FIELD_GID |
|
|
|
|
G_LOCAL_FILE_STAT_FIELD_MTIME |
|
|
|
|
G_LOCAL_FILE_STAT_FIELD_NLINK,
|
|
|
|
G_LOCAL_FILE_STAT_FIELD_ALL, &original_stat);
|
|
|
|
errsv = errno;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
res = g_local_file_lstat (filename,
|
|
|
|
G_LOCAL_FILE_STAT_FIELD_TYPE |
|
|
|
|
G_LOCAL_FILE_STAT_FIELD_MODE |
|
|
|
|
G_LOCAL_FILE_STAT_FIELD_UID |
|
|
|
|
G_LOCAL_FILE_STAT_FIELD_GID |
|
|
|
|
G_LOCAL_FILE_STAT_FIELD_MTIME |
|
|
|
|
G_LOCAL_FILE_STAT_FIELD_NLINK,
|
|
|
|
G_LOCAL_FILE_STAT_FIELD_ALL, &original_stat);
|
|
|
|
errsv = errno;
|
|
|
|
}
|
2008-08-23 03:09:08 +02:00
|
|
|
|
2017-07-31 12:30:55 +02:00
|
|
|
if (res != 0)
|
2007-11-26 17:13:05 +01:00
|
|
|
{
|
2008-02-18 16:35:16 +01:00
|
|
|
char *display_name = g_filename_display_name (filename);
|
2007-11-26 17:13:05 +01:00
|
|
|
g_set_error (error, G_IO_ERROR,
|
Save errno before calling other funcs that potentially alter it. Bug
* gio/gdesktopappinfo.c: (ensure_dir):
* gio/glocalfile.c: (g_local_file_query_filesystem_info),
(g_local_file_read), (g_local_file_delete), (g_local_file_trash),
(g_local_file_move):
* gio/glocalfileinfo.c: (set_xattr), (_g_local_file_info_get),
(_g_local_file_info_get_from_fd), (set_unix_mode),
(set_unix_uid_gid), (set_symlink), (set_mtime_atime):
* gio/glocalfileinputstream.c: (g_local_file_input_stream_read),
(g_local_file_input_stream_skip),
(g_local_file_input_stream_close),
(g_local_file_input_stream_seek):
* gio/glocalfileoutputstream.c:
(g_local_file_output_stream_write),
(g_local_file_output_stream_close),
(g_local_file_output_stream_seek),
(g_local_file_output_stream_truncate), (copy_file_data),
(handle_overwrite_open):
* gio/gunixinputstream.c: (g_unix_input_stream_read),
(g_unix_input_stream_close), (read_async_cb), (close_async_cb):
* gio/gunixoutputstream.c: (g_unix_output_stream_write),
(g_unix_output_stream_close), (write_async_cb), (close_async_cb):
Save
errno before calling other funcs that potentially alter it. Bug
#514766.
svn path=/trunk/; revision=6466
2008-02-06 16:10:08 +01:00
|
|
|
g_io_error_from_errno (errsv),
|
2016-09-30 05:47:15 +02:00
|
|
|
_("Error when getting information for file “%s”: %s"),
|
2008-02-18 16:35:16 +01:00
|
|
|
display_name, g_strerror (errsv));
|
|
|
|
g_free (display_name);
|
2021-03-16 12:36:27 +01:00
|
|
|
goto error;
|
2007-11-26 17:13:05 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* not a regular file */
|
2020-08-14 15:46:14 +02:00
|
|
|
if (!S_ISREG (_g_stat_mode (&original_stat)))
|
2007-11-26 17:13:05 +01:00
|
|
|
{
|
2020-08-14 15:46:14 +02:00
|
|
|
if (S_ISDIR (_g_stat_mode (&original_stat)))
|
glocalfileoutputstream: Fix CREATE_REPLACE_DESTINATION with symlinks
The `G_FILE_CREATE_REPLACE_DESTINATION` flag is equivalent to unlinking
the destination file and re-creating it from scratch. That did
previously work, but in the process the code would call `open(O_CREAT)`
on the file. If the file was a dangling symlink, this would create the
destination file (empty). That’s not an intended side-effect, and has
security implications if the symlink is controlled by a lower-privileged
process.
Fix that by not opening the destination file if it’s a symlink, and
adjusting the rest of the code to cope with
- the fact that `fd == -1` is not an error iff `is_symlink` is true,
- and that `original_stat` will contain the `lstat()` results for the
symlink now, rather than the `stat()` results for its target (again,
iff `is_symlink` is true).
This means that the target of the dangling symlink is no longer created,
which was the bug. The symlink itself continues to be replaced (as
before) with the new file — this is the intended behaviour of
`g_file_replace()`.
The behaviour for non-symlink cases, or cases where the symlink was not
dangling, should be unchanged.
Includes a unit test.
Signed-off-by: Philip Withnall <pwithnall@endlessos.org>
Fixes: #2325
2021-02-24 18:36:07 +01:00
|
|
|
{
|
|
|
|
g_set_error_literal (error,
|
|
|
|
G_IO_ERROR,
|
|
|
|
G_IO_ERROR_IS_DIRECTORY,
|
|
|
|
_("Target file is a directory"));
|
2021-03-16 12:36:27 +01:00
|
|
|
goto error;
|
glocalfileoutputstream: Fix CREATE_REPLACE_DESTINATION with symlinks
The `G_FILE_CREATE_REPLACE_DESTINATION` flag is equivalent to unlinking
the destination file and re-creating it from scratch. That did
previously work, but in the process the code would call `open(O_CREAT)`
on the file. If the file was a dangling symlink, this would create the
destination file (empty). That’s not an intended side-effect, and has
security implications if the symlink is controlled by a lower-privileged
process.
Fix that by not opening the destination file if it’s a symlink, and
adjusting the rest of the code to cope with
- the fact that `fd == -1` is not an error iff `is_symlink` is true,
- and that `original_stat` will contain the `lstat()` results for the
symlink now, rather than the `stat()` results for its target (again,
iff `is_symlink` is true).
This means that the target of the dangling symlink is no longer created,
which was the bug. The symlink itself continues to be replaced (as
before) with the new file — this is the intended behaviour of
`g_file_replace()`.
The behaviour for non-symlink cases, or cases where the symlink was not
dangling, should be unchanged.
Includes a unit test.
Signed-off-by: Philip Withnall <pwithnall@endlessos.org>
Fixes: #2325
2021-02-24 18:36:07 +01:00
|
|
|
}
|
|
|
|
else if (!is_symlink ||
|
|
|
|
#ifdef S_ISLNK
|
|
|
|
!S_ISLNK (_g_stat_mode (&original_stat))
|
|
|
|
#else
|
|
|
|
FALSE
|
|
|
|
#endif
|
|
|
|
)
|
|
|
|
{
|
|
|
|
g_set_error_literal (error,
|
2008-06-16 18:53:58 +02:00
|
|
|
G_IO_ERROR,
|
|
|
|
G_IO_ERROR_NOT_REGULAR_FILE,
|
|
|
|
_("Target file is not a regular file"));
|
2021-03-16 12:36:27 +01:00
|
|
|
goto error;
|
glocalfileoutputstream: Fix CREATE_REPLACE_DESTINATION with symlinks
The `G_FILE_CREATE_REPLACE_DESTINATION` flag is equivalent to unlinking
the destination file and re-creating it from scratch. That did
previously work, but in the process the code would call `open(O_CREAT)`
on the file. If the file was a dangling symlink, this would create the
destination file (empty). That’s not an intended side-effect, and has
security implications if the symlink is controlled by a lower-privileged
process.
Fix that by not opening the destination file if it’s a symlink, and
adjusting the rest of the code to cope with
- the fact that `fd == -1` is not an error iff `is_symlink` is true,
- and that `original_stat` will contain the `lstat()` results for the
symlink now, rather than the `stat()` results for its target (again,
iff `is_symlink` is true).
This means that the target of the dangling symlink is no longer created,
which was the bug. The symlink itself continues to be replaced (as
before) with the new file — this is the intended behaviour of
`g_file_replace()`.
The behaviour for non-symlink cases, or cases where the symlink was not
dangling, should be unchanged.
Includes a unit test.
Signed-off-by: Philip Withnall <pwithnall@endlessos.org>
Fixes: #2325
2021-02-24 18:36:07 +01:00
|
|
|
}
|
2007-11-26 17:13:05 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (etag != NULL)
|
|
|
|
{
|
2021-06-07 13:42:16 +02:00
|
|
|
GLocalFileStat etag_stat;
|
|
|
|
GLocalFileStat *etag_stat_pointer;
|
|
|
|
|
|
|
|
/* The ETag is calculated on the details of the target file, for symlinks,
|
|
|
|
* so we might need to stat() again. */
|
|
|
|
if (is_symlink)
|
|
|
|
{
|
|
|
|
res = g_local_file_stat (filename,
|
|
|
|
G_LOCAL_FILE_STAT_FIELD_MTIME,
|
|
|
|
G_LOCAL_FILE_STAT_FIELD_ALL, &etag_stat);
|
|
|
|
errsv = errno;
|
|
|
|
|
|
|
|
if (res != 0)
|
|
|
|
{
|
|
|
|
char *display_name = g_filename_display_name (filename);
|
|
|
|
g_set_error (error, G_IO_ERROR,
|
|
|
|
g_io_error_from_errno (errsv),
|
|
|
|
_("Error when getting information for file “%s”: %s"),
|
|
|
|
display_name, g_strerror (errsv));
|
|
|
|
g_free (display_name);
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
etag_stat_pointer = &etag_stat;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
etag_stat_pointer = &original_stat;
|
|
|
|
|
|
|
|
/* Compare the ETags */
|
|
|
|
current_etag = _g_local_file_info_create_etag (etag_stat_pointer);
|
2007-11-26 17:13:05 +01:00
|
|
|
if (strcmp (etag, current_etag) != 0)
|
|
|
|
{
|
2008-06-16 18:53:58 +02:00
|
|
|
g_set_error_literal (error,
|
|
|
|
G_IO_ERROR,
|
|
|
|
G_IO_ERROR_WRONG_ETAG,
|
|
|
|
_("The file was externally modified"));
|
2007-11-26 17:13:05 +01:00
|
|
|
g_free (current_etag);
|
2021-03-16 12:36:27 +01:00
|
|
|
goto error;
|
2007-11-26 17:13:05 +01:00
|
|
|
}
|
|
|
|
g_free (current_etag);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* We use two backup strategies.
|
|
|
|
* The first one (which is faster) consist in saving to a
|
|
|
|
* tmp file then rename the original file to the backup and the
|
|
|
|
* tmp file to the original name. This is fast but doesn't work
|
|
|
|
* when the file is a link (hard or symbolic) or when we can't
|
|
|
|
* write to the current dir or can't set the permissions on the
|
|
|
|
* new file.
|
|
|
|
* The second strategy consist simply in copying the old file
|
|
|
|
* to a backup file and rewrite the contents of the file.
|
|
|
|
*/
|
|
|
|
|
2021-03-10 17:05:55 +01:00
|
|
|
if (replace_destination_set ||
|
2020-08-14 15:46:14 +02:00
|
|
|
(!(_g_stat_nlink (&original_stat) > 1) && !is_symlink))
|
2007-11-26 17:13:05 +01:00
|
|
|
{
|
|
|
|
char *dirname, *tmp_filename;
|
|
|
|
int tmpfd;
|
|
|
|
|
|
|
|
dirname = g_path_get_dirname (filename);
|
|
|
|
tmp_filename = g_build_filename (dirname, ".goutputstream-XXXXXX", NULL);
|
|
|
|
g_free (dirname);
|
|
|
|
|
2023-02-22 00:38:13 +01:00
|
|
|
tmpfd = g_mkstemp_full (tmp_filename, (readable ? O_RDWR : O_WRONLY) | O_BINARY | O_CLOEXEC, mode);
|
2007-11-26 17:13:05 +01:00
|
|
|
if (tmpfd == -1)
|
|
|
|
{
|
|
|
|
g_free (tmp_filename);
|
|
|
|
goto fallback_strategy;
|
|
|
|
}
|
|
|
|
|
2009-02-18 15:49:25 +01:00
|
|
|
/* try to keep permissions (unless replacing) */
|
2007-11-26 17:13:05 +01:00
|
|
|
|
2021-03-10 17:05:55 +01:00
|
|
|
if (!replace_destination_set &&
|
2009-02-18 15:49:25 +01:00
|
|
|
(
|
2008-02-14 17:47:56 +01:00
|
|
|
#ifdef HAVE_FCHOWN
|
2020-08-14 15:46:14 +02:00
|
|
|
fchown (tmpfd, _g_stat_uid (&original_stat), _g_stat_gid (&original_stat)) == -1 ||
|
2007-11-26 17:13:05 +01:00
|
|
|
#endif
|
2008-02-14 17:47:56 +01:00
|
|
|
#ifdef HAVE_FCHMOD
|
2020-08-14 15:46:14 +02:00
|
|
|
fchmod (tmpfd, _g_stat_mode (&original_stat) & ~S_IFMT) == -1 ||
|
2007-11-26 17:13:05 +01:00
|
|
|
#endif
|
2009-02-18 15:49:25 +01:00
|
|
|
0
|
|
|
|
)
|
2007-11-26 17:13:05 +01:00
|
|
|
)
|
|
|
|
{
|
2011-04-15 10:15:04 +02:00
|
|
|
GLocalFileStat tmp_statbuf;
|
|
|
|
int tres;
|
|
|
|
|
2020-08-14 16:52:03 +02:00
|
|
|
tres = g_local_file_fstat (tmpfd,
|
|
|
|
G_LOCAL_FILE_STAT_FIELD_TYPE |
|
|
|
|
G_LOCAL_FILE_STAT_FIELD_MODE |
|
|
|
|
G_LOCAL_FILE_STAT_FIELD_UID |
|
|
|
|
G_LOCAL_FILE_STAT_FIELD_GID,
|
|
|
|
G_LOCAL_FILE_STAT_FIELD_ALL, &tmp_statbuf);
|
|
|
|
|
2007-11-26 17:13:05 +01:00
|
|
|
/* Check that we really needed to change something */
|
2011-04-15 10:15:04 +02:00
|
|
|
if (tres != 0 ||
|
2020-08-14 15:46:14 +02:00
|
|
|
_g_stat_uid (&original_stat) != _g_stat_uid (&tmp_statbuf) ||
|
|
|
|
_g_stat_gid (&original_stat) != _g_stat_gid (&tmp_statbuf) ||
|
|
|
|
_g_stat_mode (&original_stat) != _g_stat_mode (&tmp_statbuf))
|
2007-11-26 17:13:05 +01:00
|
|
|
{
|
2016-06-16 20:45:21 +02:00
|
|
|
g_close (tmpfd, NULL);
|
2008-02-18 16:35:16 +01:00
|
|
|
g_unlink (tmp_filename);
|
2007-11-26 17:13:05 +01:00
|
|
|
g_free (tmp_filename);
|
|
|
|
goto fallback_strategy;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
glocalfileoutputstream: Fix CREATE_REPLACE_DESTINATION with symlinks
The `G_FILE_CREATE_REPLACE_DESTINATION` flag is equivalent to unlinking
the destination file and re-creating it from scratch. That did
previously work, but in the process the code would call `open(O_CREAT)`
on the file. If the file was a dangling symlink, this would create the
destination file (empty). That’s not an intended side-effect, and has
security implications if the symlink is controlled by a lower-privileged
process.
Fix that by not opening the destination file if it’s a symlink, and
adjusting the rest of the code to cope with
- the fact that `fd == -1` is not an error iff `is_symlink` is true,
- and that `original_stat` will contain the `lstat()` results for the
symlink now, rather than the `stat()` results for its target (again,
iff `is_symlink` is true).
This means that the target of the dangling symlink is no longer created,
which was the bug. The symlink itself continues to be replaced (as
before) with the new file — this is the intended behaviour of
`g_file_replace()`.
The behaviour for non-symlink cases, or cases where the symlink was not
dangling, should be unchanged.
Includes a unit test.
Signed-off-by: Philip Withnall <pwithnall@endlessos.org>
Fixes: #2325
2021-02-24 18:36:07 +01:00
|
|
|
if (fd >= 0)
|
|
|
|
g_close (fd, NULL);
|
2007-11-26 17:13:05 +01:00
|
|
|
*temp_filename = tmp_filename;
|
|
|
|
return tmpfd;
|
|
|
|
}
|
|
|
|
|
|
|
|
fallback_strategy:
|
|
|
|
|
|
|
|
if (create_backup)
|
|
|
|
{
|
2008-08-23 03:09:08 +02:00
|
|
|
#if defined(HAVE_FCHOWN) && defined(HAVE_FCHMOD)
|
2020-08-14 16:52:03 +02:00
|
|
|
GLocalFileStat tmp_statbuf;
|
2008-08-23 03:09:08 +02:00
|
|
|
#endif
|
2007-11-26 17:13:05 +01:00
|
|
|
char *backup_filename;
|
|
|
|
int bfd;
|
|
|
|
|
|
|
|
backup_filename = create_backup_filename (filename);
|
|
|
|
|
2008-02-18 16:35:16 +01:00
|
|
|
if (g_unlink (backup_filename) == -1 && errno != ENOENT)
|
2007-11-26 17:13:05 +01:00
|
|
|
{
|
2008-06-16 18:53:58 +02:00
|
|
|
g_set_error_literal (error,
|
|
|
|
G_IO_ERROR,
|
|
|
|
G_IO_ERROR_CANT_CREATE_BACKUP,
|
|
|
|
_("Backup file creation failed"));
|
2007-11-26 17:13:05 +01:00
|
|
|
g_free (backup_filename);
|
2021-03-16 12:36:27 +01:00
|
|
|
goto error;
|
2007-11-26 17:13:05 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
bfd = g_open (backup_filename,
|
2023-02-19 16:22:22 +01:00
|
|
|
O_WRONLY | O_CREAT | O_EXCL | O_BINARY | O_CLOEXEC,
|
2020-08-14 15:46:14 +02:00
|
|
|
_g_stat_mode (&original_stat) & 0777);
|
2007-11-26 17:13:05 +01:00
|
|
|
|
|
|
|
if (bfd == -1)
|
|
|
|
{
|
2008-06-16 18:53:58 +02:00
|
|
|
g_set_error_literal (error,
|
|
|
|
G_IO_ERROR,
|
|
|
|
G_IO_ERROR_CANT_CREATE_BACKUP,
|
|
|
|
_("Backup file creation failed"));
|
2007-11-26 17:13:05 +01:00
|
|
|
g_free (backup_filename);
|
2021-03-16 12:36:27 +01:00
|
|
|
goto error;
|
2007-11-26 17:13:05 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* If needed, Try to set the group of the backup same as the
|
|
|
|
* original file. If this fails, set the protection
|
|
|
|
* bits for the group same as the protection bits for
|
|
|
|
* others. */
|
2008-02-14 17:47:56 +01:00
|
|
|
#if defined(HAVE_FCHOWN) && defined(HAVE_FCHMOD)
|
2020-08-14 16:52:03 +02:00
|
|
|
if (g_local_file_fstat (bfd, G_LOCAL_FILE_STAT_FIELD_GID, G_LOCAL_FILE_STAT_FIELD_ALL, &tmp_statbuf) != 0)
|
2007-11-26 17:13:05 +01:00
|
|
|
{
|
2008-06-16 18:53:58 +02:00
|
|
|
g_set_error_literal (error,
|
|
|
|
G_IO_ERROR,
|
|
|
|
G_IO_ERROR_CANT_CREATE_BACKUP,
|
|
|
|
_("Backup file creation failed"));
|
2008-02-18 16:35:16 +01:00
|
|
|
g_unlink (backup_filename);
|
2016-06-16 20:45:21 +02:00
|
|
|
g_close (bfd, NULL);
|
2007-11-26 17:13:05 +01:00
|
|
|
g_free (backup_filename);
|
2021-03-16 12:36:27 +01:00
|
|
|
goto error;
|
2007-11-26 17:13:05 +01:00
|
|
|
}
|
|
|
|
|
2020-08-14 15:46:14 +02:00
|
|
|
if ((_g_stat_gid (&original_stat) != _g_stat_gid (&tmp_statbuf)) &&
|
|
|
|
fchown (bfd, (uid_t) -1, _g_stat_gid (&original_stat)) != 0)
|
2007-11-26 17:13:05 +01:00
|
|
|
{
|
|
|
|
if (fchmod (bfd,
|
2020-08-14 15:46:14 +02:00
|
|
|
(_g_stat_mode (&original_stat) & 0707) |
|
|
|
|
((_g_stat_mode (&original_stat) & 07) << 3)) != 0)
|
2007-11-26 17:13:05 +01:00
|
|
|
{
|
2008-06-16 18:53:58 +02:00
|
|
|
g_set_error_literal (error,
|
|
|
|
G_IO_ERROR,
|
|
|
|
G_IO_ERROR_CANT_CREATE_BACKUP,
|
|
|
|
_("Backup file creation failed"));
|
2008-02-18 16:35:16 +01:00
|
|
|
g_unlink (backup_filename);
|
2016-06-16 20:45:21 +02:00
|
|
|
g_close (bfd, NULL);
|
2007-11-26 17:13:05 +01:00
|
|
|
g_free (backup_filename);
|
2021-03-16 12:36:27 +01:00
|
|
|
goto error;
|
2007-11-26 17:13:05 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (!copy_file_data (fd, bfd, NULL))
|
|
|
|
{
|
2008-06-16 18:53:58 +02:00
|
|
|
g_set_error_literal (error,
|
|
|
|
G_IO_ERROR,
|
|
|
|
G_IO_ERROR_CANT_CREATE_BACKUP,
|
|
|
|
_("Backup file creation failed"));
|
2008-02-18 16:35:16 +01:00
|
|
|
g_unlink (backup_filename);
|
2016-06-16 20:45:21 +02:00
|
|
|
g_close (bfd, NULL);
|
2007-11-26 17:13:05 +01:00
|
|
|
g_free (backup_filename);
|
|
|
|
|
2021-03-16 12:36:27 +01:00
|
|
|
goto error;
|
2007-11-26 17:13:05 +01:00
|
|
|
}
|
|
|
|
|
2016-06-16 20:45:21 +02:00
|
|
|
g_close (bfd, NULL);
|
2007-11-26 17:13:05 +01:00
|
|
|
g_free (backup_filename);
|
|
|
|
|
|
|
|
/* Seek back to the start of the file after the backup copy */
|
|
|
|
if (lseek (fd, 0, SEEK_SET) == -1)
|
|
|
|
{
|
2022-01-19 19:38:41 +01:00
|
|
|
errsv = errno;
|
Save errno before calling other funcs that potentially alter it. Bug
* gio/gdesktopappinfo.c: (ensure_dir):
* gio/glocalfile.c: (g_local_file_query_filesystem_info),
(g_local_file_read), (g_local_file_delete), (g_local_file_trash),
(g_local_file_move):
* gio/glocalfileinfo.c: (set_xattr), (_g_local_file_info_get),
(_g_local_file_info_get_from_fd), (set_unix_mode),
(set_unix_uid_gid), (set_symlink), (set_mtime_atime):
* gio/glocalfileinputstream.c: (g_local_file_input_stream_read),
(g_local_file_input_stream_skip),
(g_local_file_input_stream_close),
(g_local_file_input_stream_seek):
* gio/glocalfileoutputstream.c:
(g_local_file_output_stream_write),
(g_local_file_output_stream_close),
(g_local_file_output_stream_seek),
(g_local_file_output_stream_truncate), (copy_file_data),
(handle_overwrite_open):
* gio/gunixinputstream.c: (g_unix_input_stream_read),
(g_unix_input_stream_close), (read_async_cb), (close_async_cb):
* gio/gunixoutputstream.c: (g_unix_output_stream_write),
(g_unix_output_stream_close), (write_async_cb), (close_async_cb):
Save
errno before calling other funcs that potentially alter it. Bug
#514766.
svn path=/trunk/; revision=6466
2008-02-06 16:10:08 +01:00
|
|
|
|
2007-11-26 17:13:05 +01:00
|
|
|
g_set_error (error, G_IO_ERROR,
|
Save errno before calling other funcs that potentially alter it. Bug
* gio/gdesktopappinfo.c: (ensure_dir):
* gio/glocalfile.c: (g_local_file_query_filesystem_info),
(g_local_file_read), (g_local_file_delete), (g_local_file_trash),
(g_local_file_move):
* gio/glocalfileinfo.c: (set_xattr), (_g_local_file_info_get),
(_g_local_file_info_get_from_fd), (set_unix_mode),
(set_unix_uid_gid), (set_symlink), (set_mtime_atime):
* gio/glocalfileinputstream.c: (g_local_file_input_stream_read),
(g_local_file_input_stream_skip),
(g_local_file_input_stream_close),
(g_local_file_input_stream_seek):
* gio/glocalfileoutputstream.c:
(g_local_file_output_stream_write),
(g_local_file_output_stream_close),
(g_local_file_output_stream_seek),
(g_local_file_output_stream_truncate), (copy_file_data),
(handle_overwrite_open):
* gio/gunixinputstream.c: (g_unix_input_stream_read),
(g_unix_input_stream_close), (read_async_cb), (close_async_cb):
* gio/gunixoutputstream.c: (g_unix_output_stream_write),
(g_unix_output_stream_close), (write_async_cb), (close_async_cb):
Save
errno before calling other funcs that potentially alter it. Bug
#514766.
svn path=/trunk/; revision=6466
2008-02-06 16:10:08 +01:00
|
|
|
g_io_error_from_errno (errsv),
|
2007-11-26 17:13:05 +01:00
|
|
|
_("Error seeking in file: %s"),
|
Save errno before calling other funcs that potentially alter it. Bug
* gio/gdesktopappinfo.c: (ensure_dir):
* gio/glocalfile.c: (g_local_file_query_filesystem_info),
(g_local_file_read), (g_local_file_delete), (g_local_file_trash),
(g_local_file_move):
* gio/glocalfileinfo.c: (set_xattr), (_g_local_file_info_get),
(_g_local_file_info_get_from_fd), (set_unix_mode),
(set_unix_uid_gid), (set_symlink), (set_mtime_atime):
* gio/glocalfileinputstream.c: (g_local_file_input_stream_read),
(g_local_file_input_stream_skip),
(g_local_file_input_stream_close),
(g_local_file_input_stream_seek):
* gio/glocalfileoutputstream.c:
(g_local_file_output_stream_write),
(g_local_file_output_stream_close),
(g_local_file_output_stream_seek),
(g_local_file_output_stream_truncate), (copy_file_data),
(handle_overwrite_open):
* gio/gunixinputstream.c: (g_unix_input_stream_read),
(g_unix_input_stream_close), (read_async_cb), (close_async_cb):
* gio/gunixoutputstream.c: (g_unix_output_stream_write),
(g_unix_output_stream_close), (write_async_cb), (close_async_cb):
Save
errno before calling other funcs that potentially alter it. Bug
#514766.
svn path=/trunk/; revision=6466
2008-02-06 16:10:08 +01:00
|
|
|
g_strerror (errsv));
|
2021-03-16 12:36:27 +01:00
|
|
|
goto error;
|
2007-11-26 17:13:05 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-10 17:05:55 +01:00
|
|
|
if (replace_destination_set)
|
2009-02-18 15:49:25 +01:00
|
|
|
{
|
2016-06-16 20:45:21 +02:00
|
|
|
g_close (fd, NULL);
|
2022-09-16 15:11:47 +02:00
|
|
|
fd = -1;
|
|
|
|
|
2009-02-18 15:49:25 +01:00
|
|
|
if (g_unlink (filename) != 0)
|
|
|
|
{
|
2022-01-19 19:38:41 +01:00
|
|
|
errsv = errno;
|
2009-02-18 15:49:25 +01:00
|
|
|
|
|
|
|
g_set_error (error, G_IO_ERROR,
|
|
|
|
g_io_error_from_errno (errsv),
|
|
|
|
_("Error removing old file: %s"),
|
|
|
|
g_strerror (errsv));
|
2021-03-16 12:36:27 +01:00
|
|
|
goto error;
|
2009-02-18 15:49:25 +01:00
|
|
|
}
|
2009-05-13 13:03:47 +02:00
|
|
|
|
|
|
|
if (readable)
|
2023-02-19 16:22:22 +01:00
|
|
|
open_flags = O_RDWR | O_CREAT | O_BINARY | O_CLOEXEC;
|
2009-05-13 13:03:47 +02:00
|
|
|
else
|
2023-02-19 16:22:22 +01:00
|
|
|
open_flags = O_WRONLY | O_CREAT | O_BINARY | O_CLOEXEC;
|
2009-05-13 13:03:47 +02:00
|
|
|
fd = g_open (filename, open_flags, mode);
|
2009-02-18 15:49:25 +01:00
|
|
|
if (fd == -1)
|
|
|
|
{
|
2022-01-19 19:38:41 +01:00
|
|
|
char *display_name;
|
|
|
|
errsv = errno;
|
|
|
|
display_name = g_filename_display_name (filename);
|
|
|
|
|
2009-02-18 15:49:25 +01:00
|
|
|
g_set_error (error, G_IO_ERROR,
|
|
|
|
g_io_error_from_errno (errsv),
|
2016-09-30 05:47:15 +02:00
|
|
|
_("Error opening file “%s”: %s"),
|
2009-02-18 15:49:25 +01:00
|
|
|
display_name, g_strerror (errsv));
|
|
|
|
g_free (display_name);
|
2021-03-16 12:36:27 +01:00
|
|
|
goto error;
|
2009-02-18 15:49:25 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Truncate the file at the start */
|
2007-12-08 13:01:06 +01:00
|
|
|
#ifdef G_OS_WIN32
|
2009-02-18 15:49:25 +01:00
|
|
|
if (g_win32_ftruncate (fd, 0) == -1)
|
2007-12-08 13:01:06 +01:00
|
|
|
#else
|
2009-02-18 15:49:25 +01:00
|
|
|
if (ftruncate (fd, 0) == -1)
|
2007-12-08 13:01:06 +01:00
|
|
|
#endif
|
2009-02-18 15:49:25 +01:00
|
|
|
{
|
2022-01-19 19:38:41 +01:00
|
|
|
errsv = errno;
|
2009-02-18 15:49:25 +01:00
|
|
|
|
|
|
|
g_set_error (error, G_IO_ERROR,
|
|
|
|
g_io_error_from_errno (errsv),
|
|
|
|
_("Error truncating file: %s"),
|
|
|
|
g_strerror (errsv));
|
2021-03-16 12:36:27 +01:00
|
|
|
goto error;
|
2009-02-18 15:49:25 +01:00
|
|
|
}
|
2007-11-26 17:13:05 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return fd;
|
|
|
|
|
2021-03-16 12:36:27 +01:00
|
|
|
error:
|
|
|
|
if (fd >= 0)
|
|
|
|
g_close (fd, NULL);
|
|
|
|
|
2007-11-26 17:13:05 +01:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
GFileOutputStream *
|
2007-11-28 17:01:59 +01:00
|
|
|
_g_local_file_output_stream_replace (const char *filename,
|
2009-05-13 13:03:47 +02:00
|
|
|
gboolean readable,
|
2007-11-28 17:01:59 +01:00
|
|
|
const char *etag,
|
|
|
|
gboolean create_backup,
|
|
|
|
GFileCreateFlags flags,
|
Ensure g_file_copy() does not temporarily expose private files
Previously, g_file_copy() would (on Unix) create files with the
default mode of 644. For applications which might at user request
copy arbitrary private files such as ~/.ssh or /etc/shadow, a
world-readable copy would be temporarily exposed.
This patch is suboptimal in that it *only* fixes g_file_copy()
for the case where both source and destination are instances of
GLocalFile on Unix.
The reason for this is that the public GFile APIs for creating files
allow very limited control over the access permissions for the created
file; one can either say a file is "private" or not. Fixing
this by adding e.g. g_file_create_with_attributes() would make sense,
except this would entail 8 new API calls for all the variants of
_create(), _create_async(), _replace(), _replace_async(),
_create_readwrite(), _create_readwrite_async(), _replace_readwrite(),
_replace_readwrite_async(). That can be done as a separate patch
later.
https://bugzilla.gnome.org/show_bug.cgi?id=699959
2013-05-12 08:28:01 +02:00
|
|
|
GFileInfo *reference_info,
|
2007-11-28 17:01:59 +01:00
|
|
|
GCancellable *cancellable,
|
|
|
|
GError **error)
|
2007-11-26 17:13:05 +01:00
|
|
|
{
|
|
|
|
GLocalFileOutputStream *stream;
|
|
|
|
int mode;
|
|
|
|
int fd;
|
|
|
|
char *temp_file;
|
2009-03-16 17:03:13 +01:00
|
|
|
gboolean sync_on_close;
|
2009-05-13 13:03:47 +02:00
|
|
|
int open_flags;
|
2007-11-26 17:13:05 +01:00
|
|
|
|
|
|
|
if (g_cancellable_set_error_if_cancelled (cancellable, error))
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
temp_file = NULL;
|
|
|
|
|
Ensure g_file_copy() does not temporarily expose private files
Previously, g_file_copy() would (on Unix) create files with the
default mode of 644. For applications which might at user request
copy arbitrary private files such as ~/.ssh or /etc/shadow, a
world-readable copy would be temporarily exposed.
This patch is suboptimal in that it *only* fixes g_file_copy()
for the case where both source and destination are instances of
GLocalFile on Unix.
The reason for this is that the public GFile APIs for creating files
allow very limited control over the access permissions for the created
file; one can either say a file is "private" or not. Fixing
this by adding e.g. g_file_create_with_attributes() would make sense,
except this would entail 8 new API calls for all the variants of
_create(), _create_async(), _replace(), _replace_async(),
_create_readwrite(), _create_readwrite_async(), _replace_readwrite(),
_replace_readwrite_async(). That can be done as a separate patch
later.
https://bugzilla.gnome.org/show_bug.cgi?id=699959
2013-05-12 08:28:01 +02:00
|
|
|
mode = mode_from_flags_or_info (flags, reference_info);
|
2009-03-16 17:03:13 +01:00
|
|
|
sync_on_close = FALSE;
|
2007-11-26 17:13:05 +01:00
|
|
|
|
|
|
|
/* If the file doesn't exist, create it */
|
2021-02-24 18:42:24 +01:00
|
|
|
open_flags = O_CREAT | O_EXCL | O_BINARY | O_CLOEXEC;
|
2009-05-13 13:03:47 +02:00
|
|
|
if (readable)
|
|
|
|
open_flags |= O_RDWR;
|
|
|
|
else
|
|
|
|
open_flags |= O_WRONLY;
|
|
|
|
fd = g_open (filename, open_flags, mode);
|
2007-11-26 17:13:05 +01:00
|
|
|
|
|
|
|
if (fd == -1 && errno == EEXIST)
|
|
|
|
{
|
|
|
|
/* The file already exists */
|
2009-05-13 13:03:47 +02:00
|
|
|
fd = handle_overwrite_open (filename, readable, etag,
|
Ensure g_file_copy() does not temporarily expose private files
Previously, g_file_copy() would (on Unix) create files with the
default mode of 644. For applications which might at user request
copy arbitrary private files such as ~/.ssh or /etc/shadow, a
world-readable copy would be temporarily exposed.
This patch is suboptimal in that it *only* fixes g_file_copy()
for the case where both source and destination are instances of
GLocalFile on Unix.
The reason for this is that the public GFile APIs for creating files
allow very limited control over the access permissions for the created
file; one can either say a file is "private" or not. Fixing
this by adding e.g. g_file_create_with_attributes() would make sense,
except this would entail 8 new API calls for all the variants of
_create(), _create_async(), _replace(), _replace_async(),
_create_readwrite(), _create_readwrite_async(), _replace_readwrite(),
_replace_readwrite_async(). That can be done as a separate patch
later.
https://bugzilla.gnome.org/show_bug.cgi?id=699959
2013-05-12 08:28:01 +02:00
|
|
|
create_backup, &temp_file,
|
|
|
|
flags, reference_info,
|
|
|
|
cancellable, error);
|
2007-11-26 17:13:05 +01:00
|
|
|
if (fd == -1)
|
|
|
|
return NULL;
|
2009-03-16 17:03:13 +01:00
|
|
|
|
|
|
|
/* If the final destination exists, we want to sync the newly written
|
|
|
|
* file to ensure the data is on disk when we rename over the destination.
|
|
|
|
* otherwise if we get a system crash we can lose both the new and the
|
|
|
|
* old file on some filesystems. (I.E. those that don't guarantee the
|
|
|
|
* data is written to the disk before the metadata.)
|
|
|
|
*/
|
|
|
|
sync_on_close = TRUE;
|
2007-11-26 17:13:05 +01:00
|
|
|
}
|
|
|
|
else if (fd == -1)
|
|
|
|
{
|
2013-05-09 19:47:50 +02:00
|
|
|
set_error_from_open_errno (filename, error);
|
2007-11-26 17:13:05 +01:00
|
|
|
return NULL;
|
|
|
|
}
|
2021-02-24 18:42:24 +01:00
|
|
|
#if !defined(HAVE_O_CLOEXEC) && defined(F_SETFD)
|
|
|
|
else
|
|
|
|
fcntl (fd, F_SETFD, FD_CLOEXEC);
|
|
|
|
#endif
|
|
|
|
|
2007-11-26 17:13:05 +01:00
|
|
|
stream = g_object_new (G_TYPE_LOCAL_FILE_OUTPUT_STREAM, NULL);
|
|
|
|
stream->priv->fd = fd;
|
2009-03-16 17:03:13 +01:00
|
|
|
stream->priv->sync_on_close = sync_on_close;
|
2007-11-26 17:13:05 +01:00
|
|
|
stream->priv->tmp_filename = temp_file;
|
|
|
|
if (create_backup)
|
|
|
|
stream->priv->backup_filename = create_backup_filename (filename);
|
|
|
|
stream->priv->original_filename = g_strdup (filename);
|
|
|
|
|
|
|
|
return G_FILE_OUTPUT_STREAM (stream);
|
|
|
|
}
|
2010-02-07 17:18:06 +01:00
|
|
|
|
2010-05-20 16:51:00 +02:00
|
|
|
gint
|
|
|
|
_g_local_file_output_stream_get_fd (GLocalFileOutputStream *stream)
|
|
|
|
{
|
|
|
|
g_return_val_if_fail (G_IS_LOCAL_FILE_OUTPUT_STREAM (stream), -1);
|
|
|
|
return stream->priv->fd;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef G_OS_UNIX
|
2010-02-07 17:18:06 +01:00
|
|
|
static int
|
|
|
|
g_local_file_output_stream_get_fd (GFileDescriptorBased *fd_based)
|
|
|
|
{
|
|
|
|
GLocalFileOutputStream *stream = G_LOCAL_FILE_OUTPUT_STREAM (fd_based);
|
2010-05-20 16:51:00 +02:00
|
|
|
return _g_local_file_output_stream_get_fd (stream);
|
2010-02-07 17:18:06 +01:00
|
|
|
}
|
2010-05-20 16:51:00 +02:00
|
|
|
#endif
|