2007-11-26 17:13:05 +01:00
|
|
|
|
/* GIO - GLib Input, Output and Streaming Library
|
|
|
|
|
*
|
|
|
|
|
* Copyright (C) 2006-2007 Red Hat, Inc.
|
|
|
|
|
*
|
|
|
|
|
* This library is free software; you can redistribute it and/or
|
|
|
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
|
|
|
* License as published by the Free Software Foundation; either
|
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>
|
|
|
|
|
*/
|
|
|
|
|
|
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 <string.h>
|
|
|
|
|
#include <errno.h>
|
|
|
|
|
#include <fcntl.h>
|
2013-09-12 17:44:11 +02:00
|
|
|
|
#if G_OS_UNIX
|
2013-07-28 19:41:17 +02:00
|
|
|
|
#include <dirent.h>
|
2007-11-26 17:13:05 +01:00
|
|
|
|
#include <unistd.h>
|
2007-12-08 13:01:06 +01:00
|
|
|
|
#endif
|
2007-11-26 17:13:05 +01:00
|
|
|
|
|
|
|
|
|
#if HAVE_SYS_STATFS_H
|
|
|
|
|
#include <sys/statfs.h>
|
|
|
|
|
#endif
|
|
|
|
|
#if HAVE_SYS_STATVFS_H
|
|
|
|
|
#include <sys/statvfs.h>
|
|
|
|
|
#endif
|
|
|
|
|
#if HAVE_SYS_VFS_H
|
|
|
|
|
#include <sys/vfs.h>
|
|
|
|
|
#elif HAVE_SYS_MOUNT_H
|
|
|
|
|
#if HAVE_SYS_PARAM_H
|
|
|
|
|
#include <sys/param.h>
|
|
|
|
|
#endif
|
|
|
|
|
#include <sys/mount.h>
|
|
|
|
|
#endif
|
|
|
|
|
|
2008-02-18 11:10:58 +01:00
|
|
|
|
#ifndef O_BINARY
|
|
|
|
|
#define O_BINARY 0
|
|
|
|
|
#endif
|
|
|
|
|
|
2008-07-01 08:32:35 +02:00
|
|
|
|
#include "gfileattribute.h"
|
2007-11-26 17:13:05 +01:00
|
|
|
|
#include "glocalfile.h"
|
|
|
|
|
#include "glocalfileinfo.h"
|
|
|
|
|
#include "glocalfileenumerator.h"
|
|
|
|
|
#include "glocalfileinputstream.h"
|
|
|
|
|
#include "glocalfileoutputstream.h"
|
2009-05-13 13:03:47 +02:00
|
|
|
|
#include "glocalfileiostream.h"
|
2007-11-26 17:13:05 +01:00
|
|
|
|
#include "glocalfilemonitor.h"
|
Rework how volumes, drives and volume monitoring is done. Previosly the
2007-12-11 David Zeuthen <davidz@redhat.com>
Rework how volumes, drives and volume monitoring is
done. Previosly the model was
GDrive <1-1> GVolume
where a GDrive instance represented a mount point and a GVolume
instance represented a mounted file system. This patch changes it
the model to
GDrive <1-N> GVolume <1-1> GMount
where GMount now serves the purpose of the old GVolume and the new
GVolume serves the purpose of the old GDrive. In addition the new
GDrive interface is used to represent a collection of GVolume
instances (typically partitions) and also contains utility to query
the state of the physical drive the GDrive object represents (such
as checking for media, polling the drive, ejecting the media etc.).
Also implement mounting and unmounting in the Unix volume monitor
backend. A subquent patch will introduce GDrive support for ejection
of media.
* Makefile.am:
* gdrive.c: (g_drive_is_media_check_automatic),
(g_drive_is_media_removable), (g_drive_has_media),
(g_drive_can_poll_for_media), (g_drive_eject),
(g_drive_eject_finish), (g_drive_poll_for_media),
(g_drive_poll_for_media_finish):
* gdrive.h:
* gfile.c: (g_file_find_enclosing_mount):
* gfile.h:
* gio.symbols:
* glocaldirectorymonitor.c:
(g_local_directory_monitor_constructor), (mounts_changed):
* glocalfile.c: (get_mount_info),
(g_local_file_find_enclosing_mount),
(g_local_file_file_iface_init):
* gnativevolumemonitor.h:
* gunionvolumemonitor.c: (get_mounts), (get_volumes),
(get_connected_drives), (g_union_volume_monitor_class_init),
(child_volume_added), (child_volume_removed),
(child_volume_changed), (child_mount_added), (child_mount_removed),
(child_mount_pre_unmount), (child_mount_changed),
(child_drive_changed), (g_union_volume_monitor_add_monitor),
(g_union_volume_monitor_remove_monitor),
(_g_mount_get_for_mount_path):
* gunixmounts.c: (g_unix_is_mount_path_system_internal),
(guess_system_internal), (_g_get_unix_mounts),
(_g_get_unix_mount_points), (g_get_unix_mount_at),
(g_unix_mount_free), (g_unix_mount_compare),
(g_unix_mount_get_mount_path), (g_unix_mount_get_device_path),
(g_unix_mount_get_fs_type), (g_unix_mount_is_readonly),
(g_unix_mount_is_system_internal), (g_unix_mount_guess_type),
(type_to_icon), (g_unix_mount_guess_name),
(g_unix_mount_guess_icon), (g_unix_mount_point_guess_name),
(g_unix_mount_point_guess_icon), (_canonicalize_filename),
(_resolve_symlink), (_resolve_dev_root):
* gunixmounts.h:
* gunixvolume.c: (g_unix_volume_finalize), (_g_unix_volume_new),
(_g_unix_volume_disconnected), (_g_unix_volume_set_mount),
(_g_unix_volume_unset_mount), (g_unix_volume_get_icon),
(g_unix_volume_get_name), (g_unix_volume_can_mount),
(g_unix_volume_get_drive), (g_unix_volume_get_mount),
(_g_unix_volume_has_mount_path), (mount_cb), (mount_read_error),
(g_unix_volume_mount), (g_unix_volume_mount_finish),
(g_unix_volume_volume_iface_init):
* gunixvolume.h:
* gunixvolumemonitor.c: (g_unix_volume_monitor_finalize),
(get_mounts), (get_volumes), (get_connected_drives),
(get_mount_for_mount_path), (g_unix_volume_monitor_class_init),
(mountpoints_changed), (mounts_changed),
(g_unix_volume_monitor_init),
(_g_unix_volume_monitor_lookup_volume_for_mount_path),
(find_mount_by_mountpath), (update_volumes), (update_mounts):
* gunixvolumemonitor.h:
* gvolume.c: (g_volume_get_mount), (g_volume_can_mount),
(g_volume_mount), (g_volume_mount_finish):
* gvolume.h:
* gvolumemonitor.c: (g_volume_monitor_class_init),
(g_volume_monitor_get_connected_drives),
(g_volume_monitor_get_volumes), (g_volume_monitor_get_mounts):
* gvolumemonitor.h:
svn path=/trunk/; revision=6095
2007-12-11 22:23:55 +01:00
|
|
|
|
#include "gmountprivate.h"
|
2008-07-01 08:32:35 +02:00
|
|
|
|
#include "gunixmounts.h"
|
|
|
|
|
#include "gioerror.h"
|
2007-11-26 17:13:05 +01:00
|
|
|
|
#include <glib/gstdio.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
|
|
|
|
#include <glib/gstdioprivate.h>
|
2007-11-26 17:13:05 +01:00
|
|
|
|
#include "glibintl.h"
|
2013-01-25 18:05:26 +01:00
|
|
|
|
#ifdef G_OS_UNIX
|
|
|
|
|
#include "glib-unix.h"
|
2018-08-23 06:24:42 +02:00
|
|
|
|
#include "gportalsupport.h"
|
|
|
|
|
#include "gtrashportal.h"
|
2013-01-25 18:05:26 +01:00
|
|
|
|
#endif
|
2007-11-26 17:13:05 +01:00
|
|
|
|
|
2013-07-25 20:05:36 +02:00
|
|
|
|
#include "glib-private.h"
|
|
|
|
|
|
2007-12-08 13:01:06 +01:00
|
|
|
|
#ifdef G_OS_WIN32
|
|
|
|
|
#include <windows.h>
|
|
|
|
|
#include <io.h>
|
|
|
|
|
#include <direct.h>
|
|
|
|
|
|
2008-03-08 17:06:00 +01:00
|
|
|
|
#ifndef FILE_READ_ONLY_VOLUME
|
|
|
|
|
#define FILE_READ_ONLY_VOLUME 0x00080000
|
|
|
|
|
#endif
|
|
|
|
|
|
2007-12-08 13:01:06 +01:00
|
|
|
|
#ifndef S_ISDIR
|
|
|
|
|
#define S_ISDIR(m) (((m) & _S_IFMT) == _S_IFDIR)
|
|
|
|
|
#endif
|
|
|
|
|
#ifndef S_ISLNK
|
|
|
|
|
#define S_ISLNK(m) (0)
|
|
|
|
|
#endif
|
2016-07-18 08:41:17 +02:00
|
|
|
|
|
|
|
|
|
#ifndef ECANCELED
|
|
|
|
|
#define ECANCELED 105
|
|
|
|
|
#endif
|
2007-12-08 13:01:06 +01:00
|
|
|
|
#endif
|
|
|
|
|
|
2007-11-28 13:39:07 +01:00
|
|
|
|
|
2008-01-21 15:02:19 +01:00
|
|
|
|
static void g_local_file_file_iface_init (GFileIface *iface);
|
2007-11-26 17:13:05 +01:00
|
|
|
|
|
|
|
|
|
static GFileAttributeInfoList *local_writable_attributes = NULL;
|
2009-06-18 09:05:27 +02:00
|
|
|
|
static /* GFileAttributeInfoList * */ gsize local_writable_namespaces = 0;
|
2007-11-26 17:13:05 +01:00
|
|
|
|
|
|
|
|
|
struct _GLocalFile
|
|
|
|
|
{
|
|
|
|
|
GObject parent_instance;
|
|
|
|
|
|
|
|
|
|
char *filename;
|
|
|
|
|
};
|
|
|
|
|
|
2007-11-28 17:01:59 +01:00
|
|
|
|
#define g_local_file_get_type _g_local_file_get_type
|
2007-11-26 17:13:05 +01:00
|
|
|
|
G_DEFINE_TYPE_WITH_CODE (GLocalFile, g_local_file, G_TYPE_OBJECT,
|
|
|
|
|
G_IMPLEMENT_INTERFACE (G_TYPE_FILE,
|
|
|
|
|
g_local_file_file_iface_init))
|
|
|
|
|
|
2018-09-13 17:33:59 +02:00
|
|
|
|
static char *find_mountpoint_for (const char *file, dev_t dev, gboolean resolve_basename_symlink);
|
2007-11-26 17:13:05 +01:00
|
|
|
|
|
2020-06-15 17:14:30 +02:00
|
|
|
|
#ifndef G_OS_WIN32
|
2020-06-15 17:24:37 +02:00
|
|
|
|
static gboolean is_remote_fs_type (const gchar *fsname);
|
2020-06-15 17:14:30 +02:00
|
|
|
|
#endif
|
|
|
|
|
|
2007-11-26 17:13:05 +01:00
|
|
|
|
static void
|
|
|
|
|
g_local_file_finalize (GObject *object)
|
|
|
|
|
{
|
|
|
|
|
GLocalFile *local;
|
|
|
|
|
|
|
|
|
|
local = G_LOCAL_FILE (object);
|
|
|
|
|
|
|
|
|
|
g_free (local->filename);
|
2008-06-16 11:54:04 +02:00
|
|
|
|
|
|
|
|
|
G_OBJECT_CLASS (g_local_file_parent_class)->finalize (object);
|
2007-11-26 17:13:05 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
g_local_file_class_init (GLocalFileClass *klass)
|
|
|
|
|
{
|
|
|
|
|
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
|
|
|
|
GFileAttributeInfoList *list;
|
|
|
|
|
|
|
|
|
|
gobject_class->finalize = g_local_file_finalize;
|
|
|
|
|
|
|
|
|
|
/* Set up attribute lists */
|
|
|
|
|
|
|
|
|
|
/* Writable attributes: */
|
|
|
|
|
|
|
|
|
|
list = g_file_attribute_info_list_new ();
|
|
|
|
|
|
|
|
|
|
g_file_attribute_info_list_add (list,
|
|
|
|
|
G_FILE_ATTRIBUTE_UNIX_MODE,
|
|
|
|
|
G_FILE_ATTRIBUTE_TYPE_UINT32,
|
2008-02-14 12:00:09 +01:00
|
|
|
|
G_FILE_ATTRIBUTE_INFO_COPY_WITH_FILE |
|
2007-12-14 12:07:31 +01:00
|
|
|
|
G_FILE_ATTRIBUTE_INFO_COPY_WHEN_MOVED);
|
2007-11-26 17:13:05 +01:00
|
|
|
|
|
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
|
|
|
|
g_file_attribute_info_list_add (list,
|
|
|
|
|
G_FILE_ATTRIBUTE_UNIX_UID,
|
|
|
|
|
G_FILE_ATTRIBUTE_TYPE_UINT32,
|
2007-12-14 12:07:31 +01:00
|
|
|
|
G_FILE_ATTRIBUTE_INFO_COPY_WHEN_MOVED);
|
2007-11-26 17:13:05 +01:00
|
|
|
|
g_file_attribute_info_list_add (list,
|
|
|
|
|
G_FILE_ATTRIBUTE_UNIX_GID,
|
|
|
|
|
G_FILE_ATTRIBUTE_TYPE_UINT32,
|
2007-12-14 12:07:31 +01:00
|
|
|
|
G_FILE_ATTRIBUTE_INFO_COPY_WHEN_MOVED);
|
2007-11-26 17:13:05 +01:00
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifdef HAVE_SYMLINK
|
|
|
|
|
g_file_attribute_info_list_add (list,
|
2007-12-20 14:30:47 +01:00
|
|
|
|
G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET,
|
2007-11-26 17:13:05 +01:00
|
|
|
|
G_FILE_ATTRIBUTE_TYPE_BYTE_STRING,
|
|
|
|
|
0);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifdef HAVE_UTIMES
|
|
|
|
|
g_file_attribute_info_list_add (list,
|
|
|
|
|
G_FILE_ATTRIBUTE_TIME_MODIFIED,
|
|
|
|
|
G_FILE_ATTRIBUTE_TYPE_UINT64,
|
2008-12-09 12:15:36 +01:00
|
|
|
|
G_FILE_ATTRIBUTE_INFO_COPY_WITH_FILE |
|
2007-12-14 12:07:31 +01:00
|
|
|
|
G_FILE_ATTRIBUTE_INFO_COPY_WHEN_MOVED);
|
2007-11-26 17:13:05 +01:00
|
|
|
|
g_file_attribute_info_list_add (list,
|
|
|
|
|
G_FILE_ATTRIBUTE_TIME_MODIFIED_USEC,
|
|
|
|
|
G_FILE_ATTRIBUTE_TYPE_UINT32,
|
2008-12-09 12:15:36 +01:00
|
|
|
|
G_FILE_ATTRIBUTE_INFO_COPY_WITH_FILE |
|
2007-12-14 12:07:31 +01:00
|
|
|
|
G_FILE_ATTRIBUTE_INFO_COPY_WHEN_MOVED);
|
2008-12-09 12:15:36 +01:00
|
|
|
|
/* When copying, the target file is accessed. Replicating
|
|
|
|
|
* the source access time does not make sense in this case.
|
|
|
|
|
*/
|
2007-11-26 17:13:05 +01:00
|
|
|
|
g_file_attribute_info_list_add (list,
|
|
|
|
|
G_FILE_ATTRIBUTE_TIME_ACCESS,
|
|
|
|
|
G_FILE_ATTRIBUTE_TYPE_UINT64,
|
2007-12-14 12:07:31 +01:00
|
|
|
|
G_FILE_ATTRIBUTE_INFO_COPY_WHEN_MOVED);
|
2007-11-26 17:13:05 +01:00
|
|
|
|
g_file_attribute_info_list_add (list,
|
|
|
|
|
G_FILE_ATTRIBUTE_TIME_ACCESS_USEC,
|
|
|
|
|
G_FILE_ATTRIBUTE_TYPE_UINT32,
|
2007-12-14 12:07:31 +01:00
|
|
|
|
G_FILE_ATTRIBUTE_INFO_COPY_WHEN_MOVED);
|
2007-11-26 17:13:05 +01:00
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
local_writable_attributes = list;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
g_local_file_init (GLocalFile *local)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
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
|
|
|
|
const char *
|
|
|
|
|
_g_local_file_get_filename (GLocalFile *file)
|
|
|
|
|
{
|
|
|
|
|
return file->filename;
|
|
|
|
|
}
|
2007-11-26 17:13:05 +01:00
|
|
|
|
|
|
|
|
|
GFile *
|
2007-11-28 17:01:59 +01:00
|
|
|
|
_g_local_file_new (const char *filename)
|
2007-11-26 17:13:05 +01:00
|
|
|
|
{
|
|
|
|
|
GLocalFile *local;
|
|
|
|
|
|
|
|
|
|
local = g_object_new (G_TYPE_LOCAL_FILE, NULL);
|
2018-04-27 18:01:45 +02:00
|
|
|
|
local->filename = g_canonicalize_filename (filename, NULL);
|
2007-11-26 17:13:05 +01:00
|
|
|
|
|
|
|
|
|
return G_FILE (local);
|
|
|
|
|
}
|
|
|
|
|
|
2015-03-06 03:05:06 +01:00
|
|
|
|
/*< internal >
|
|
|
|
|
* g_local_file_new_from_dirname_and_basename:
|
|
|
|
|
* @dirname: an absolute, canonical directory name
|
|
|
|
|
* @basename: the name of a child inside @dirname
|
|
|
|
|
*
|
|
|
|
|
* Creates a #GFile from @dirname and @basename.
|
|
|
|
|
*
|
|
|
|
|
* This is more efficient than pasting the fields together for yourself
|
|
|
|
|
* and creating a #GFile from the result, and also more efficient than
|
|
|
|
|
* creating a #GFile for the dirname and using g_file_get_child().
|
|
|
|
|
*
|
|
|
|
|
* @dirname must be canonical, as per GLocalFile's opinion of what
|
|
|
|
|
* canonical means. This means that you should only pass strings that
|
|
|
|
|
* were returned by _g_local_file_get_filename().
|
|
|
|
|
*
|
|
|
|
|
* Returns: a #GFile
|
|
|
|
|
*/
|
|
|
|
|
GFile *
|
|
|
|
|
g_local_file_new_from_dirname_and_basename (const gchar *dirname,
|
|
|
|
|
const gchar *basename)
|
|
|
|
|
{
|
|
|
|
|
GLocalFile *local;
|
|
|
|
|
|
|
|
|
|
g_return_val_if_fail (dirname != NULL, NULL);
|
|
|
|
|
g_return_val_if_fail (basename && basename[0] && !strchr (basename, '/'), NULL);
|
|
|
|
|
|
|
|
|
|
local = g_object_new (G_TYPE_LOCAL_FILE, NULL);
|
|
|
|
|
local->filename = g_build_filename (dirname, basename, NULL);
|
|
|
|
|
|
|
|
|
|
return G_FILE (local);
|
|
|
|
|
}
|
|
|
|
|
|
2007-11-26 17:13:05 +01:00
|
|
|
|
static gboolean
|
|
|
|
|
g_local_file_is_native (GFile *file)
|
|
|
|
|
{
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
2007-11-30 06:11:25 +01:00
|
|
|
|
g_local_file_has_uri_scheme (GFile *file,
|
2007-11-26 17:13:05 +01:00
|
|
|
|
const char *uri_scheme)
|
|
|
|
|
{
|
|
|
|
|
return g_ascii_strcasecmp (uri_scheme, "file") == 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static char *
|
|
|
|
|
g_local_file_get_uri_scheme (GFile *file)
|
|
|
|
|
{
|
|
|
|
|
return g_strdup ("file");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static char *
|
|
|
|
|
g_local_file_get_basename (GFile *file)
|
|
|
|
|
{
|
|
|
|
|
return g_path_get_basename (G_LOCAL_FILE (file)->filename);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static char *
|
|
|
|
|
g_local_file_get_path (GFile *file)
|
|
|
|
|
{
|
|
|
|
|
return g_strdup (G_LOCAL_FILE (file)->filename);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static char *
|
|
|
|
|
g_local_file_get_uri (GFile *file)
|
|
|
|
|
{
|
|
|
|
|
return g_filename_to_uri (G_LOCAL_FILE (file)->filename, NULL, NULL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
|
get_filename_charset (const gchar **filename_charset)
|
|
|
|
|
{
|
|
|
|
|
const gchar **charsets;
|
|
|
|
|
gboolean is_utf8;
|
|
|
|
|
|
|
|
|
|
is_utf8 = g_get_filename_charsets (&charsets);
|
|
|
|
|
|
|
|
|
|
if (filename_charset)
|
|
|
|
|
*filename_charset = charsets[0];
|
|
|
|
|
|
|
|
|
|
return is_utf8;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
|
name_is_valid_for_display (const char *string,
|
2007-11-30 06:11:25 +01:00
|
|
|
|
gboolean is_valid_utf8)
|
2007-11-26 17:13:05 +01:00
|
|
|
|
{
|
|
|
|
|
char c;
|
2008-01-22 10:13:28 +01:00
|
|
|
|
|
2007-11-26 17:13:05 +01:00
|
|
|
|
if (!is_valid_utf8 &&
|
|
|
|
|
!g_utf8_validate (string, -1, NULL))
|
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
|
|
while ((c = *string++) != 0)
|
|
|
|
|
{
|
2008-01-21 15:02:19 +01:00
|
|
|
|
if (g_ascii_iscntrl (c))
|
2007-11-26 17:13:05 +01:00
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static char *
|
|
|
|
|
g_local_file_get_parse_name (GFile *file)
|
|
|
|
|
{
|
|
|
|
|
const char *filename;
|
|
|
|
|
char *parse_name;
|
|
|
|
|
const gchar *charset;
|
|
|
|
|
char *utf8_filename;
|
|
|
|
|
char *roundtripped_filename;
|
|
|
|
|
gboolean free_utf8_filename;
|
|
|
|
|
gboolean is_valid_utf8;
|
2008-01-22 10:13:28 +01:00
|
|
|
|
char *escaped_path;
|
|
|
|
|
|
2007-11-26 17:13:05 +01:00
|
|
|
|
filename = G_LOCAL_FILE (file)->filename;
|
|
|
|
|
if (get_filename_charset (&charset))
|
|
|
|
|
{
|
|
|
|
|
utf8_filename = (char *)filename;
|
|
|
|
|
free_utf8_filename = FALSE;
|
|
|
|
|
is_valid_utf8 = FALSE; /* Can't guarantee this */
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
utf8_filename = g_convert (filename, -1,
|
|
|
|
|
"UTF-8", charset, NULL, NULL, NULL);
|
|
|
|
|
free_utf8_filename = TRUE;
|
|
|
|
|
is_valid_utf8 = TRUE;
|
|
|
|
|
|
|
|
|
|
if (utf8_filename != NULL)
|
|
|
|
|
{
|
|
|
|
|
/* Make sure we can roundtrip: */
|
|
|
|
|
roundtripped_filename = g_convert (utf8_filename, -1,
|
|
|
|
|
charset, "UTF-8", NULL, NULL, NULL);
|
|
|
|
|
|
|
|
|
|
if (roundtripped_filename == NULL ||
|
2010-01-06 19:23:48 +01:00
|
|
|
|
strcmp (filename, roundtripped_filename) != 0)
|
2007-11-26 17:13:05 +01:00
|
|
|
|
{
|
|
|
|
|
g_free (utf8_filename);
|
|
|
|
|
utf8_filename = NULL;
|
|
|
|
|
}
|
2008-10-27 21:42:32 +01:00
|
|
|
|
|
|
|
|
|
g_free (roundtripped_filename);
|
2007-11-26 17:13:05 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (utf8_filename != NULL &&
|
|
|
|
|
name_is_valid_for_display (utf8_filename, is_valid_utf8))
|
|
|
|
|
{
|
|
|
|
|
if (free_utf8_filename)
|
|
|
|
|
parse_name = utf8_filename;
|
|
|
|
|
else
|
|
|
|
|
parse_name = g_strdup (utf8_filename);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2012-02-03 17:10:50 +01:00
|
|
|
|
#ifdef G_OS_WIN32
|
|
|
|
|
char *dup_filename, *p, *backslash;
|
|
|
|
|
|
|
|
|
|
/* Turn backslashes into forward slashes like
|
|
|
|
|
* g_filename_to_uri() would do (but we can't use that because
|
|
|
|
|
* it doesn't output IRIs).
|
|
|
|
|
*/
|
|
|
|
|
dup_filename = g_strdup (filename);
|
|
|
|
|
filename = p = dup_filename;
|
|
|
|
|
|
|
|
|
|
while ((backslash = strchr (p, '\\')) != NULL)
|
|
|
|
|
{
|
|
|
|
|
*backslash = '/';
|
|
|
|
|
p = backslash + 1;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
2008-01-22 10:13:28 +01:00
|
|
|
|
escaped_path = g_uri_escape_string (filename,
|
|
|
|
|
G_URI_RESERVED_CHARS_ALLOWED_IN_PATH_ELEMENT "/",
|
|
|
|
|
TRUE);
|
|
|
|
|
parse_name = g_strconcat ("file://",
|
|
|
|
|
(*escaped_path != '/') ? "/" : "",
|
|
|
|
|
escaped_path,
|
|
|
|
|
NULL);
|
|
|
|
|
|
|
|
|
|
g_free (escaped_path);
|
2012-02-03 17:10:50 +01:00
|
|
|
|
#ifdef G_OS_WIN32
|
|
|
|
|
g_free (dup_filename);
|
|
|
|
|
#endif
|
2007-11-26 17:13:05 +01:00
|
|
|
|
if (free_utf8_filename)
|
|
|
|
|
g_free (utf8_filename);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return parse_name;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static GFile *
|
|
|
|
|
g_local_file_get_parent (GFile *file)
|
|
|
|
|
{
|
|
|
|
|
GLocalFile *local = G_LOCAL_FILE (file);
|
|
|
|
|
const char *non_root;
|
|
|
|
|
char *dirname;
|
|
|
|
|
GFile *parent;
|
|
|
|
|
|
2014-06-20 22:25:07 +02:00
|
|
|
|
/* Check for root; local->filename is guaranteed to be absolute, so
|
|
|
|
|
* g_path_skip_root() should never return NULL. */
|
2007-11-26 17:13:05 +01:00
|
|
|
|
non_root = g_path_skip_root (local->filename);
|
2014-06-20 22:25:07 +02:00
|
|
|
|
g_assert (non_root != NULL);
|
|
|
|
|
|
2007-11-26 17:13:05 +01:00
|
|
|
|
if (*non_root == 0)
|
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
|
|
dirname = g_path_get_dirname (local->filename);
|
2007-11-28 17:01:59 +01:00
|
|
|
|
parent = _g_local_file_new (dirname);
|
2007-11-26 17:13:05 +01:00
|
|
|
|
g_free (dirname);
|
|
|
|
|
return parent;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static GFile *
|
|
|
|
|
g_local_file_dup (GFile *file)
|
|
|
|
|
{
|
|
|
|
|
GLocalFile *local = G_LOCAL_FILE (file);
|
|
|
|
|
|
2007-11-28 17:01:59 +01:00
|
|
|
|
return _g_local_file_new (local->filename);
|
2007-11-26 17:13:05 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static guint
|
|
|
|
|
g_local_file_hash (GFile *file)
|
|
|
|
|
{
|
|
|
|
|
GLocalFile *local = G_LOCAL_FILE (file);
|
|
|
|
|
|
|
|
|
|
return g_str_hash (local->filename);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
|
g_local_file_equal (GFile *file1,
|
|
|
|
|
GFile *file2)
|
|
|
|
|
{
|
|
|
|
|
GLocalFile *local1 = G_LOCAL_FILE (file1);
|
|
|
|
|
GLocalFile *local2 = G_LOCAL_FILE (file2);
|
|
|
|
|
|
|
|
|
|
return g_str_equal (local1->filename, local2->filename);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static const char *
|
2007-11-30 06:11:25 +01:00
|
|
|
|
match_prefix (const char *path,
|
|
|
|
|
const char *prefix)
|
2007-11-26 17:13:05 +01:00
|
|
|
|
{
|
|
|
|
|
int prefix_len;
|
|
|
|
|
|
|
|
|
|
prefix_len = strlen (prefix);
|
|
|
|
|
if (strncmp (path, prefix, prefix_len) != 0)
|
|
|
|
|
return NULL;
|
2008-01-14 15:55:27 +01:00
|
|
|
|
|
|
|
|
|
/* Handle the case where prefix is the root, so that
|
|
|
|
|
* the IS_DIR_SEPRARATOR check below works */
|
|
|
|
|
if (prefix_len > 0 &&
|
|
|
|
|
G_IS_DIR_SEPARATOR (prefix[prefix_len-1]))
|
|
|
|
|
prefix_len--;
|
|
|
|
|
|
2007-11-26 17:13:05 +01:00
|
|
|
|
return path + prefix_len;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
2008-02-21 10:09:59 +01:00
|
|
|
|
g_local_file_prefix_matches (GFile *parent,
|
|
|
|
|
GFile *descendant)
|
2007-11-26 17:13:05 +01:00
|
|
|
|
{
|
|
|
|
|
GLocalFile *parent_local = G_LOCAL_FILE (parent);
|
|
|
|
|
GLocalFile *descendant_local = G_LOCAL_FILE (descendant);
|
|
|
|
|
const char *remainder;
|
|
|
|
|
|
|
|
|
|
remainder = match_prefix (descendant_local->filename, parent_local->filename);
|
|
|
|
|
if (remainder != NULL && G_IS_DIR_SEPARATOR (*remainder))
|
|
|
|
|
return TRUE;
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static char *
|
|
|
|
|
g_local_file_get_relative_path (GFile *parent,
|
|
|
|
|
GFile *descendant)
|
|
|
|
|
{
|
|
|
|
|
GLocalFile *parent_local = G_LOCAL_FILE (parent);
|
|
|
|
|
GLocalFile *descendant_local = G_LOCAL_FILE (descendant);
|
|
|
|
|
const char *remainder;
|
|
|
|
|
|
|
|
|
|
remainder = match_prefix (descendant_local->filename, parent_local->filename);
|
|
|
|
|
|
|
|
|
|
if (remainder != NULL && G_IS_DIR_SEPARATOR (*remainder))
|
|
|
|
|
return g_strdup (remainder + 1);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static GFile *
|
2007-11-30 06:11:25 +01:00
|
|
|
|
g_local_file_resolve_relative_path (GFile *file,
|
2007-11-26 17:13:05 +01:00
|
|
|
|
const char *relative_path)
|
|
|
|
|
{
|
|
|
|
|
GLocalFile *local = G_LOCAL_FILE (file);
|
|
|
|
|
char *filename;
|
|
|
|
|
GFile *child;
|
|
|
|
|
|
|
|
|
|
if (g_path_is_absolute (relative_path))
|
2007-11-28 17:01:59 +01:00
|
|
|
|
return _g_local_file_new (relative_path);
|
2007-11-26 17:13:05 +01:00
|
|
|
|
|
|
|
|
|
filename = g_build_filename (local->filename, relative_path, NULL);
|
2007-11-28 17:01:59 +01:00
|
|
|
|
child = _g_local_file_new (filename);
|
2007-11-26 17:13:05 +01:00
|
|
|
|
g_free (filename);
|
|
|
|
|
|
|
|
|
|
return child;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static GFileEnumerator *
|
2007-11-30 06:11:25 +01:00
|
|
|
|
g_local_file_enumerate_children (GFile *file,
|
|
|
|
|
const char *attributes,
|
|
|
|
|
GFileQueryInfoFlags flags,
|
|
|
|
|
GCancellable *cancellable,
|
|
|
|
|
GError **error)
|
2007-11-26 17:13:05 +01:00
|
|
|
|
{
|
|
|
|
|
GLocalFile *local = G_LOCAL_FILE (file);
|
2008-06-16 10:49:08 +02:00
|
|
|
|
return _g_local_file_enumerator_new (local,
|
2007-11-28 17:01:59 +01:00
|
|
|
|
attributes, flags,
|
|
|
|
|
cancellable, error);
|
2007-11-26 17:13:05 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static GFile *
|
|
|
|
|
g_local_file_get_child_for_display_name (GFile *file,
|
|
|
|
|
const char *display_name,
|
|
|
|
|
GError **error)
|
|
|
|
|
{
|
2008-10-31 03:23:55 +01:00
|
|
|
|
GFile *new_file;
|
2007-11-26 17:13:05 +01:00
|
|
|
|
char *basename;
|
|
|
|
|
|
|
|
|
|
basename = g_filename_from_utf8 (display_name, -1, NULL, NULL, NULL);
|
|
|
|
|
if (basename == NULL)
|
|
|
|
|
{
|
|
|
|
|
g_set_error (error, G_IO_ERROR,
|
|
|
|
|
G_IO_ERROR_INVALID_FILENAME,
|
|
|
|
|
_("Invalid filename %s"), display_name);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
new_file = g_file_get_child (file, basename);
|
|
|
|
|
g_free (basename);
|
|
|
|
|
|
|
|
|
|
return new_file;
|
|
|
|
|
}
|
|
|
|
|
|
2011-09-01 11:10:38 +02:00
|
|
|
|
#if defined(USE_STATFS) && !defined(HAVE_STRUCT_STATFS_F_FSTYPENAME)
|
2007-11-26 17:13:05 +01:00
|
|
|
|
static const char *
|
|
|
|
|
get_fs_type (long f_type)
|
|
|
|
|
{
|
|
|
|
|
/* filesystem ids taken from linux manpage */
|
2007-11-30 06:11:25 +01:00
|
|
|
|
switch (f_type)
|
|
|
|
|
{
|
|
|
|
|
case 0xadf5:
|
|
|
|
|
return "adfs";
|
2008-06-10 17:37:41 +02:00
|
|
|
|
case 0x5346414f:
|
|
|
|
|
return "afs";
|
|
|
|
|
case 0x0187:
|
|
|
|
|
return "autofs";
|
2007-11-30 06:11:25 +01:00
|
|
|
|
case 0xADFF:
|
|
|
|
|
return "affs";
|
2015-09-06 09:34:24 +02:00
|
|
|
|
case 0x62646576:
|
|
|
|
|
return "bdevfs";
|
2007-11-30 06:11:25 +01:00
|
|
|
|
case 0x42465331:
|
|
|
|
|
return "befs";
|
|
|
|
|
case 0x1BADFACE:
|
|
|
|
|
return "bfs";
|
2015-09-06 09:34:24 +02:00
|
|
|
|
case 0x42494e4d:
|
|
|
|
|
return "binfmt_misc";
|
2009-02-23 05:30:06 +01:00
|
|
|
|
case 0x9123683E:
|
|
|
|
|
return "btrfs";
|
2015-09-06 09:34:24 +02:00
|
|
|
|
case 0x73727279:
|
|
|
|
|
return "btrfs_test_fs";
|
|
|
|
|
case 0x27e0eb:
|
|
|
|
|
return "cgroup";
|
|
|
|
|
case 0x63677270:
|
|
|
|
|
return "cgroup2";
|
2007-11-30 06:11:25 +01:00
|
|
|
|
case 0xFF534D42:
|
|
|
|
|
return "cifs";
|
|
|
|
|
case 0x73757245:
|
|
|
|
|
return "coda";
|
|
|
|
|
case 0x012FF7B7:
|
|
|
|
|
return "coh";
|
2015-09-06 09:34:24 +02:00
|
|
|
|
case 0x62656570:
|
|
|
|
|
return "configfs";
|
2007-11-30 06:11:25 +01:00
|
|
|
|
case 0x28cd3d45:
|
|
|
|
|
return "cramfs";
|
2015-09-06 09:34:24 +02:00
|
|
|
|
case 0x64626720:
|
|
|
|
|
return "debugfs";
|
2007-11-30 06:11:25 +01:00
|
|
|
|
case 0x1373:
|
|
|
|
|
return "devfs";
|
2015-09-06 09:34:24 +02:00
|
|
|
|
case 0x1cd1:
|
|
|
|
|
return "devpts";
|
|
|
|
|
case 0xf15f:
|
|
|
|
|
return "ecryptfs";
|
|
|
|
|
case 0xde5e81e4:
|
|
|
|
|
return "efivarfs";
|
2007-11-30 06:11:25 +01:00
|
|
|
|
case 0x00414A53:
|
|
|
|
|
return "efs";
|
2021-03-29 19:29:15 +02:00
|
|
|
|
case 0x2011BAB0UL:
|
|
|
|
|
return "exfat";
|
2007-11-30 06:11:25 +01:00
|
|
|
|
case 0x137D:
|
|
|
|
|
return "ext";
|
|
|
|
|
case 0xEF51:
|
|
|
|
|
return "ext2";
|
|
|
|
|
case 0xEF53:
|
2009-04-03 06:23:54 +02:00
|
|
|
|
return "ext3/ext4";
|
2015-09-06 09:34:24 +02:00
|
|
|
|
case 0xF2F52010:
|
|
|
|
|
return "f2fs";
|
|
|
|
|
case 0x65735546:
|
|
|
|
|
return "fuse";
|
|
|
|
|
case 0x65735543:
|
|
|
|
|
return "fusectl";
|
|
|
|
|
case 0xBAD1DEA:
|
|
|
|
|
return "futexfs";
|
2007-11-30 06:11:25 +01:00
|
|
|
|
case 0x4244:
|
|
|
|
|
return "hfs";
|
2015-09-06 09:34:24 +02:00
|
|
|
|
case 0x00c0ffee:
|
|
|
|
|
return "hostfs";
|
2007-11-30 06:11:25 +01:00
|
|
|
|
case 0xF995E849:
|
|
|
|
|
return "hpfs";
|
|
|
|
|
case 0x958458f6:
|
|
|
|
|
return "hugetlbfs";
|
|
|
|
|
case 0x9660:
|
|
|
|
|
return "isofs";
|
|
|
|
|
case 0x72b6:
|
|
|
|
|
return "jffs2";
|
|
|
|
|
case 0x3153464a:
|
|
|
|
|
return "jfs";
|
|
|
|
|
case 0x137F:
|
|
|
|
|
return "minix";
|
|
|
|
|
case 0x138F:
|
|
|
|
|
return "minix2";
|
|
|
|
|
case 0x2468:
|
|
|
|
|
return "minix2";
|
|
|
|
|
case 0x2478:
|
|
|
|
|
return "minix22";
|
2015-09-06 09:34:24 +02:00
|
|
|
|
case 0x4d5a:
|
|
|
|
|
return "minix3";
|
|
|
|
|
case 0x19800202:
|
|
|
|
|
return "mqueue";
|
2007-11-30 06:11:25 +01:00
|
|
|
|
case 0x4d44:
|
|
|
|
|
return "msdos";
|
|
|
|
|
case 0x564c:
|
|
|
|
|
return "ncp";
|
|
|
|
|
case 0x6969:
|
|
|
|
|
return "nfs";
|
2015-09-06 09:34:24 +02:00
|
|
|
|
case 0x3434:
|
|
|
|
|
return "nilfs";
|
|
|
|
|
case 0x6e736673:
|
|
|
|
|
return "nsfs";
|
2007-11-30 06:11:25 +01:00
|
|
|
|
case 0x5346544e:
|
|
|
|
|
return "ntfs";
|
2015-09-06 09:34:24 +02:00
|
|
|
|
case 0x7461636f:
|
|
|
|
|
return "ocfs2";
|
2007-11-30 06:11:25 +01:00
|
|
|
|
case 0x9fa1:
|
|
|
|
|
return "openprom";
|
2015-09-06 09:34:24 +02:00
|
|
|
|
case 0x794c7630:
|
|
|
|
|
return "overlay";
|
|
|
|
|
case 0x50495045:
|
|
|
|
|
return "pipefs";
|
2007-11-30 06:11:25 +01:00
|
|
|
|
case 0x9fa0:
|
|
|
|
|
return "proc";
|
2015-09-06 09:34:24 +02:00
|
|
|
|
case 0x6165676C:
|
|
|
|
|
return "pstore";
|
2007-11-30 06:11:25 +01:00
|
|
|
|
case 0x002f:
|
|
|
|
|
return "qnx4";
|
2015-09-06 09:34:24 +02:00
|
|
|
|
case 0x68191122:
|
|
|
|
|
return "qnx6";
|
|
|
|
|
case 0x858458f6:
|
|
|
|
|
return "ramfs";
|
2007-11-30 06:11:25 +01:00
|
|
|
|
case 0x52654973:
|
|
|
|
|
return "reiserfs";
|
|
|
|
|
case 0x7275:
|
|
|
|
|
return "romfs";
|
2015-09-06 09:34:24 +02:00
|
|
|
|
case 0x67596969:
|
|
|
|
|
return "rpc_pipefs";
|
|
|
|
|
case 0x73636673:
|
|
|
|
|
return "securityfs";
|
|
|
|
|
case 0xf97cff8c:
|
|
|
|
|
return "selinuxfs";
|
|
|
|
|
case 0x43415d53:
|
|
|
|
|
return "smackfs";
|
2007-11-30 06:11:25 +01:00
|
|
|
|
case 0x517B:
|
|
|
|
|
return "smb";
|
2019-04-24 09:44:36 +02:00
|
|
|
|
case 0xfe534d42:
|
|
|
|
|
return "smb2";
|
2015-09-06 09:34:24 +02:00
|
|
|
|
case 0x534F434B:
|
|
|
|
|
return "sockfs";
|
2009-02-23 05:30:06 +01:00
|
|
|
|
case 0x73717368:
|
|
|
|
|
return "squashfs";
|
2015-09-06 09:34:24 +02:00
|
|
|
|
case 0x62656572:
|
|
|
|
|
return "sysfs";
|
2007-11-30 06:11:25 +01:00
|
|
|
|
case 0x012FF7B6:
|
|
|
|
|
return "sysv2";
|
|
|
|
|
case 0x012FF7B5:
|
|
|
|
|
return "sysv4";
|
|
|
|
|
case 0x01021994:
|
|
|
|
|
return "tmpfs";
|
2015-09-06 09:34:24 +02:00
|
|
|
|
case 0x74726163:
|
|
|
|
|
return "tracefs";
|
2007-11-30 06:11:25 +01:00
|
|
|
|
case 0x15013346:
|
|
|
|
|
return "udf";
|
|
|
|
|
case 0x00011954:
|
|
|
|
|
return "ufs";
|
|
|
|
|
case 0x9fa2:
|
|
|
|
|
return "usbdevice";
|
2015-09-06 09:34:24 +02:00
|
|
|
|
case 0x01021997:
|
|
|
|
|
return "v9fs";
|
2007-11-30 06:11:25 +01:00
|
|
|
|
case 0xa501FCF5:
|
|
|
|
|
return "vxfs";
|
2015-09-06 09:34:24 +02:00
|
|
|
|
case 0xabba1974:
|
|
|
|
|
return "xenfs";
|
2007-11-30 06:11:25 +01:00
|
|
|
|
case 0x012FF7B4:
|
|
|
|
|
return "xenix";
|
|
|
|
|
case 0x58465342:
|
|
|
|
|
return "xfs";
|
|
|
|
|
case 0x012FD16D:
|
|
|
|
|
return "xiafs";
|
2011-01-06 14:08:02 +01:00
|
|
|
|
case 0x52345362:
|
|
|
|
|
return "reiser4";
|
2007-11-30 06:11:25 +01:00
|
|
|
|
default:
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
2007-11-26 17:13:05 +01:00
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
2007-12-10 16:24:53 +01:00
|
|
|
|
#ifndef G_OS_WIN32
|
|
|
|
|
|
2007-11-26 17:13:05 +01:00
|
|
|
|
G_LOCK_DEFINE_STATIC(mount_info_hash);
|
|
|
|
|
static GHashTable *mount_info_hash = NULL;
|
2007-12-10 16:24:53 +01:00
|
|
|
|
static guint64 mount_info_hash_cache_time = 0;
|
2007-11-26 17:13:05 +01:00
|
|
|
|
|
|
|
|
|
typedef enum {
|
|
|
|
|
MOUNT_INFO_READONLY = 1<<0
|
|
|
|
|
} MountInfo;
|
|
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
|
device_equal (gconstpointer v1,
|
2007-11-30 06:11:25 +01:00
|
|
|
|
gconstpointer v2)
|
2007-11-26 17:13:05 +01:00
|
|
|
|
{
|
2008-01-21 15:02:19 +01:00
|
|
|
|
return *(dev_t *)v1 == *(dev_t *)v2;
|
2007-11-26 17:13:05 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static guint
|
2007-11-30 06:11:25 +01:00
|
|
|
|
device_hash (gconstpointer v)
|
2007-11-26 17:13:05 +01:00
|
|
|
|
{
|
|
|
|
|
return (guint) *(dev_t *)v;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2007-11-30 06:11:25 +01:00
|
|
|
|
get_mount_info (GFileInfo *fs_info,
|
|
|
|
|
const char *path,
|
2007-11-26 17:13:05 +01:00
|
|
|
|
GFileAttributeMatcher *matcher)
|
|
|
|
|
{
|
2010-03-21 19:04:18 +01:00
|
|
|
|
GStatBuf buf;
|
2007-11-26 17:13:05 +01:00
|
|
|
|
gboolean got_info;
|
|
|
|
|
gpointer info_as_ptr;
|
|
|
|
|
guint mount_info;
|
|
|
|
|
char *mountpoint;
|
|
|
|
|
dev_t *dev;
|
Rework how volumes, drives and volume monitoring is done. Previosly the
2007-12-11 David Zeuthen <davidz@redhat.com>
Rework how volumes, drives and volume monitoring is
done. Previosly the model was
GDrive <1-1> GVolume
where a GDrive instance represented a mount point and a GVolume
instance represented a mounted file system. This patch changes it
the model to
GDrive <1-N> GVolume <1-1> GMount
where GMount now serves the purpose of the old GVolume and the new
GVolume serves the purpose of the old GDrive. In addition the new
GDrive interface is used to represent a collection of GVolume
instances (typically partitions) and also contains utility to query
the state of the physical drive the GDrive object represents (such
as checking for media, polling the drive, ejecting the media etc.).
Also implement mounting and unmounting in the Unix volume monitor
backend. A subquent patch will introduce GDrive support for ejection
of media.
* Makefile.am:
* gdrive.c: (g_drive_is_media_check_automatic),
(g_drive_is_media_removable), (g_drive_has_media),
(g_drive_can_poll_for_media), (g_drive_eject),
(g_drive_eject_finish), (g_drive_poll_for_media),
(g_drive_poll_for_media_finish):
* gdrive.h:
* gfile.c: (g_file_find_enclosing_mount):
* gfile.h:
* gio.symbols:
* glocaldirectorymonitor.c:
(g_local_directory_monitor_constructor), (mounts_changed):
* glocalfile.c: (get_mount_info),
(g_local_file_find_enclosing_mount),
(g_local_file_file_iface_init):
* gnativevolumemonitor.h:
* gunionvolumemonitor.c: (get_mounts), (get_volumes),
(get_connected_drives), (g_union_volume_monitor_class_init),
(child_volume_added), (child_volume_removed),
(child_volume_changed), (child_mount_added), (child_mount_removed),
(child_mount_pre_unmount), (child_mount_changed),
(child_drive_changed), (g_union_volume_monitor_add_monitor),
(g_union_volume_monitor_remove_monitor),
(_g_mount_get_for_mount_path):
* gunixmounts.c: (g_unix_is_mount_path_system_internal),
(guess_system_internal), (_g_get_unix_mounts),
(_g_get_unix_mount_points), (g_get_unix_mount_at),
(g_unix_mount_free), (g_unix_mount_compare),
(g_unix_mount_get_mount_path), (g_unix_mount_get_device_path),
(g_unix_mount_get_fs_type), (g_unix_mount_is_readonly),
(g_unix_mount_is_system_internal), (g_unix_mount_guess_type),
(type_to_icon), (g_unix_mount_guess_name),
(g_unix_mount_guess_icon), (g_unix_mount_point_guess_name),
(g_unix_mount_point_guess_icon), (_canonicalize_filename),
(_resolve_symlink), (_resolve_dev_root):
* gunixmounts.h:
* gunixvolume.c: (g_unix_volume_finalize), (_g_unix_volume_new),
(_g_unix_volume_disconnected), (_g_unix_volume_set_mount),
(_g_unix_volume_unset_mount), (g_unix_volume_get_icon),
(g_unix_volume_get_name), (g_unix_volume_can_mount),
(g_unix_volume_get_drive), (g_unix_volume_get_mount),
(_g_unix_volume_has_mount_path), (mount_cb), (mount_read_error),
(g_unix_volume_mount), (g_unix_volume_mount_finish),
(g_unix_volume_volume_iface_init):
* gunixvolume.h:
* gunixvolumemonitor.c: (g_unix_volume_monitor_finalize),
(get_mounts), (get_volumes), (get_connected_drives),
(get_mount_for_mount_path), (g_unix_volume_monitor_class_init),
(mountpoints_changed), (mounts_changed),
(g_unix_volume_monitor_init),
(_g_unix_volume_monitor_lookup_volume_for_mount_path),
(find_mount_by_mountpath), (update_volumes), (update_mounts):
* gunixvolumemonitor.h:
* gvolume.c: (g_volume_get_mount), (g_volume_can_mount),
(g_volume_mount), (g_volume_mount_finish):
* gvolume.h:
* gvolumemonitor.c: (g_volume_monitor_class_init),
(g_volume_monitor_get_connected_drives),
(g_volume_monitor_get_volumes), (g_volume_monitor_get_mounts):
* gvolumemonitor.h:
svn path=/trunk/; revision=6095
2007-12-11 22:23:55 +01:00
|
|
|
|
GUnixMountEntry *mount;
|
2007-11-26 17:13:05 +01:00
|
|
|
|
guint64 cache_time;
|
|
|
|
|
|
2007-12-08 13:01:06 +01:00
|
|
|
|
if (g_lstat (path, &buf) != 0)
|
2007-11-26 17:13:05 +01:00
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
G_LOCK (mount_info_hash);
|
|
|
|
|
|
|
|
|
|
if (mount_info_hash == NULL)
|
|
|
|
|
mount_info_hash = g_hash_table_new_full (device_hash, device_equal,
|
|
|
|
|
g_free, NULL);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (g_unix_mounts_changed_since (mount_info_hash_cache_time))
|
|
|
|
|
g_hash_table_remove_all (mount_info_hash);
|
|
|
|
|
|
|
|
|
|
got_info = g_hash_table_lookup_extended (mount_info_hash,
|
|
|
|
|
&buf.st_dev,
|
|
|
|
|
NULL,
|
|
|
|
|
&info_as_ptr);
|
|
|
|
|
|
|
|
|
|
G_UNLOCK (mount_info_hash);
|
|
|
|
|
|
|
|
|
|
mount_info = GPOINTER_TO_UINT (info_as_ptr);
|
|
|
|
|
|
|
|
|
|
if (!got_info)
|
|
|
|
|
{
|
|
|
|
|
mount_info = 0;
|
|
|
|
|
|
2018-09-13 17:33:59 +02:00
|
|
|
|
mountpoint = find_mountpoint_for (path, buf.st_dev, FALSE);
|
2007-11-26 17:13:05 +01:00
|
|
|
|
if (mountpoint == NULL)
|
2008-11-21 01:01:22 +01:00
|
|
|
|
mountpoint = g_strdup ("/");
|
2007-11-26 17:13:05 +01:00
|
|
|
|
|
2007-12-17 15:59:23 +01:00
|
|
|
|
mount = g_unix_mount_at (mountpoint, &cache_time);
|
2007-11-26 17:13:05 +01:00
|
|
|
|
if (mount)
|
|
|
|
|
{
|
|
|
|
|
if (g_unix_mount_is_readonly (mount))
|
|
|
|
|
mount_info |= MOUNT_INFO_READONLY;
|
|
|
|
|
|
|
|
|
|
g_unix_mount_free (mount);
|
|
|
|
|
}
|
|
|
|
|
|
2008-11-21 01:01:22 +01:00
|
|
|
|
g_free (mountpoint);
|
|
|
|
|
|
2007-11-26 17:13:05 +01:00
|
|
|
|
dev = g_new0 (dev_t, 1);
|
|
|
|
|
*dev = buf.st_dev;
|
|
|
|
|
|
|
|
|
|
G_LOCK (mount_info_hash);
|
|
|
|
|
mount_info_hash_cache_time = cache_time;
|
|
|
|
|
g_hash_table_insert (mount_info_hash, dev, GUINT_TO_POINTER (mount_info));
|
|
|
|
|
G_UNLOCK (mount_info_hash);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (mount_info & MOUNT_INFO_READONLY)
|
2007-12-20 14:30:47 +01:00
|
|
|
|
g_file_info_set_attribute_boolean (fs_info, G_FILE_ATTRIBUTE_FILESYSTEM_READONLY, TRUE);
|
2007-11-26 17:13:05 +01:00
|
|
|
|
}
|
|
|
|
|
|
2007-12-10 16:24:53 +01:00
|
|
|
|
#endif
|
|
|
|
|
|
2008-02-25 09:15:35 +01:00
|
|
|
|
#ifdef G_OS_WIN32
|
|
|
|
|
|
|
|
|
|
static wchar_t *
|
|
|
|
|
get_volume_for_path (const char *path)
|
|
|
|
|
{
|
|
|
|
|
long len;
|
|
|
|
|
wchar_t *wpath;
|
|
|
|
|
wchar_t *result;
|
|
|
|
|
|
2008-09-30 12:47:06 +02:00
|
|
|
|
wpath = g_utf8_to_utf16 (path, -1, NULL, NULL, NULL);
|
|
|
|
|
result = g_new (wchar_t, MAX_PATH);
|
2008-02-25 09:15:35 +01:00
|
|
|
|
|
2008-09-30 12:47:06 +02:00
|
|
|
|
if (!GetVolumePathNameW (wpath, result, MAX_PATH))
|
2008-02-25 09:15:35 +01:00
|
|
|
|
{
|
|
|
|
|
char *msg = g_win32_error_message (GetLastError ());
|
|
|
|
|
g_critical ("GetVolumePathName failed: %s", msg);
|
|
|
|
|
g_free (msg);
|
|
|
|
|
g_free (result);
|
|
|
|
|
g_free (wpath);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
len = wcslen (result);
|
|
|
|
|
if (len > 0 && result[len-1] != L'\\')
|
|
|
|
|
{
|
|
|
|
|
result = g_renew (wchar_t, result, len + 2);
|
|
|
|
|
result[len] = L'\\';
|
|
|
|
|
result[len + 1] = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
g_free (wpath);
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static char *
|
2018-09-13 17:33:59 +02:00
|
|
|
|
find_mountpoint_for (const char *file, dev_t dev, gboolean resolve_basename_symlink)
|
2008-02-25 09:15:35 +01:00
|
|
|
|
{
|
|
|
|
|
wchar_t *wpath;
|
|
|
|
|
char *utf8_path;
|
|
|
|
|
|
|
|
|
|
wpath = get_volume_for_path (file);
|
|
|
|
|
if (!wpath)
|
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
|
|
utf8_path = g_utf16_to_utf8 (wpath, -1, NULL, NULL, NULL);
|
|
|
|
|
|
|
|
|
|
g_free (wpath);
|
|
|
|
|
return utf8_path;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
get_filesystem_readonly (GFileInfo *info,
|
|
|
|
|
const char *path)
|
|
|
|
|
{
|
|
|
|
|
wchar_t *rootdir;
|
|
|
|
|
|
|
|
|
|
rootdir = get_volume_for_path (path);
|
|
|
|
|
|
|
|
|
|
if (rootdir)
|
|
|
|
|
{
|
2020-06-03 02:28:41 +02:00
|
|
|
|
DWORD flags;
|
|
|
|
|
if (GetVolumeInformationW (rootdir, NULL, 0, NULL, NULL, &flags, NULL, 0))
|
|
|
|
|
g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_FILESYSTEM_READONLY,
|
|
|
|
|
(flags & FILE_READ_ONLY_VOLUME) != 0);
|
2008-02-25 09:15:35 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
g_free (rootdir);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endif /* G_OS_WIN32 */
|
|
|
|
|
|
2016-07-17 05:22:33 +02:00
|
|
|
|
#pragma GCC diagnostic push
|
|
|
|
|
#pragma GCC diagnostic ignored "-Wformat-nonliteral"
|
2015-08-26 17:06:31 +02:00
|
|
|
|
static void
|
|
|
|
|
g_set_io_error (GError **error,
|
|
|
|
|
const gchar *msg,
|
|
|
|
|
GFile *file,
|
|
|
|
|
gint errsv)
|
|
|
|
|
{
|
|
|
|
|
GLocalFile *local = G_LOCAL_FILE (file);
|
|
|
|
|
gchar *display_name;
|
|
|
|
|
|
|
|
|
|
display_name = g_filename_display_name (local->filename);
|
|
|
|
|
g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errsv),
|
|
|
|
|
msg, display_name, g_strerror (errsv));
|
|
|
|
|
g_free (display_name);
|
|
|
|
|
}
|
2016-07-17 05:22:33 +02:00
|
|
|
|
#pragma GCC diagnostic pop
|
2015-08-26 17:06:31 +02:00
|
|
|
|
|
2007-11-26 17:13:05 +01:00
|
|
|
|
static GFileInfo *
|
2007-11-30 06:11:25 +01:00
|
|
|
|
g_local_file_query_filesystem_info (GFile *file,
|
|
|
|
|
const char *attributes,
|
|
|
|
|
GCancellable *cancellable,
|
|
|
|
|
GError **error)
|
2007-11-26 17:13:05 +01:00
|
|
|
|
{
|
|
|
|
|
GLocalFile *local = G_LOCAL_FILE (file);
|
|
|
|
|
GFileInfo *info;
|
2008-06-16 15:23:14 +02:00
|
|
|
|
int statfs_result = 0;
|
2007-11-26 17:13:05 +01:00
|
|
|
|
gboolean no_size;
|
2007-12-10 16:24:53 +01:00
|
|
|
|
#ifndef G_OS_WIN32
|
2008-05-27 09:51:13 +02:00
|
|
|
|
const char *fstype;
|
2007-11-26 17:13:05 +01:00
|
|
|
|
#ifdef USE_STATFS
|
2011-07-21 02:56:05 +02:00
|
|
|
|
guint64 block_size;
|
2007-11-26 17:13:05 +01:00
|
|
|
|
struct statfs statfs_buffer;
|
|
|
|
|
#elif defined(USE_STATVFS)
|
2011-07-21 02:56:05 +02:00
|
|
|
|
guint64 block_size;
|
2007-11-26 17:13:05 +01:00
|
|
|
|
struct statvfs statfs_buffer;
|
2012-01-10 03:35:13 +01:00
|
|
|
|
#endif /* USE_STATFS */
|
|
|
|
|
#endif /* G_OS_WIN32 */
|
2007-11-26 17:13:05 +01:00
|
|
|
|
GFileAttributeMatcher *attribute_matcher;
|
|
|
|
|
|
|
|
|
|
no_size = FALSE;
|
|
|
|
|
|
|
|
|
|
#ifdef USE_STATFS
|
|
|
|
|
|
|
|
|
|
#if STATFS_ARGS == 2
|
|
|
|
|
statfs_result = statfs (local->filename, &statfs_buffer);
|
|
|
|
|
#elif STATFS_ARGS == 4
|
|
|
|
|
statfs_result = statfs (local->filename, &statfs_buffer,
|
|
|
|
|
sizeof (statfs_buffer), 0);
|
2012-01-10 03:35:13 +01:00
|
|
|
|
#endif /* STATFS_ARGS == 2 */
|
2007-11-26 17:13:05 +01:00
|
|
|
|
block_size = statfs_buffer.f_bsize;
|
|
|
|
|
|
2009-03-03 15:40:44 +01:00
|
|
|
|
/* Many backends can't report free size (for instance the gvfs fuse
|
2018-07-10 15:01:11 +02:00
|
|
|
|
* backend for backend not supporting this), and set f_bfree to 0,
|
|
|
|
|
* but it can be 0 for real too. We treat the available == 0 and
|
|
|
|
|
* free == 0 case as "both of these are invalid", but only on file systems
|
|
|
|
|
* which are known to not support this (otherwise we can omit metadata for
|
|
|
|
|
* systems which are legitimately full). */
|
|
|
|
|
#if defined(__linux__)
|
2009-09-07 12:50:58 +02:00
|
|
|
|
if (statfs_result == 0 &&
|
2018-07-10 15:01:11 +02:00
|
|
|
|
statfs_buffer.f_bavail == 0 && statfs_buffer.f_bfree == 0 &&
|
|
|
|
|
(/* linux/ncp_fs.h: NCP_SUPER_MAGIC == 0x564c */
|
|
|
|
|
statfs_buffer.f_type == 0x564c ||
|
|
|
|
|
/* man statfs: FUSE_SUPER_MAGIC == 0x65735546 */
|
|
|
|
|
statfs_buffer.f_type == 0x65735546))
|
2007-11-26 17:13:05 +01:00
|
|
|
|
no_size = TRUE;
|
2018-07-10 15:01:11 +02:00
|
|
|
|
#endif /* __linux__ */
|
2007-11-26 17:13:05 +01:00
|
|
|
|
|
|
|
|
|
#elif defined(USE_STATVFS)
|
|
|
|
|
statfs_result = statvfs (local->filename, &statfs_buffer);
|
|
|
|
|
block_size = statfs_buffer.f_frsize;
|
2012-01-10 03:35:13 +01:00
|
|
|
|
#endif /* USE_STATFS */
|
2007-11-26 17:13:05 +01:00
|
|
|
|
|
|
|
|
|
if (statfs_result == -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;
|
|
|
|
|
|
2015-08-26 17:06:31 +02:00
|
|
|
|
g_set_io_error (error,
|
|
|
|
|
_("Error getting filesystem info for %s: %s"),
|
|
|
|
|
file, errsv);
|
2007-11-26 17:13:05 +01:00
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
info = g_file_info_new ();
|
|
|
|
|
|
|
|
|
|
attribute_matcher = g_file_attribute_matcher_new (attributes);
|
|
|
|
|
|
2008-05-17 08:08:08 +02:00
|
|
|
|
if (!no_size &&
|
|
|
|
|
g_file_attribute_matcher_matches (attribute_matcher,
|
2007-12-20 14:30:47 +01:00
|
|
|
|
G_FILE_ATTRIBUTE_FILESYSTEM_FREE))
|
2007-12-08 13:01:06 +01:00
|
|
|
|
{
|
|
|
|
|
#ifdef G_OS_WIN32
|
|
|
|
|
gchar *localdir = g_path_get_dirname (local->filename);
|
|
|
|
|
wchar_t *wdirname = g_utf8_to_utf16 (localdir, -1, NULL, NULL, NULL);
|
|
|
|
|
ULARGE_INTEGER li;
|
|
|
|
|
|
|
|
|
|
g_free (localdir);
|
|
|
|
|
if (GetDiskFreeSpaceExW (wdirname, &li, NULL, NULL))
|
2007-12-20 14:30:47 +01:00
|
|
|
|
g_file_info_set_attribute_uint64 (info, G_FILE_ATTRIBUTE_FILESYSTEM_FREE, (guint64)li.QuadPart);
|
2007-12-08 13:01:06 +01:00
|
|
|
|
g_free (wdirname);
|
|
|
|
|
#else
|
2011-07-21 02:56:05 +02:00
|
|
|
|
#if defined(USE_STATFS) || defined(USE_STATVFS)
|
2007-12-20 14:30:47 +01:00
|
|
|
|
g_file_info_set_attribute_uint64 (info, G_FILE_ATTRIBUTE_FILESYSTEM_FREE, block_size * statfs_buffer.f_bavail);
|
2011-07-21 02:56:05 +02:00
|
|
|
|
#endif
|
2007-12-08 13:01:06 +01:00
|
|
|
|
#endif
|
|
|
|
|
}
|
2008-05-17 08:08:08 +02:00
|
|
|
|
if (!no_size &&
|
|
|
|
|
g_file_attribute_matcher_matches (attribute_matcher,
|
2007-12-20 14:30:47 +01:00
|
|
|
|
G_FILE_ATTRIBUTE_FILESYSTEM_SIZE))
|
2007-12-08 13:01:06 +01:00
|
|
|
|
{
|
|
|
|
|
#ifdef G_OS_WIN32
|
|
|
|
|
gchar *localdir = g_path_get_dirname (local->filename);
|
|
|
|
|
wchar_t *wdirname = g_utf8_to_utf16 (localdir, -1, NULL, NULL, NULL);
|
|
|
|
|
ULARGE_INTEGER li;
|
|
|
|
|
|
|
|
|
|
g_free (localdir);
|
|
|
|
|
if (GetDiskFreeSpaceExW (wdirname, NULL, &li, NULL))
|
2007-12-20 14:30:47 +01:00
|
|
|
|
g_file_info_set_attribute_uint64 (info, G_FILE_ATTRIBUTE_FILESYSTEM_SIZE, (guint64)li.QuadPart);
|
2007-12-08 13:01:06 +01:00
|
|
|
|
g_free (wdirname);
|
|
|
|
|
#else
|
2011-07-21 02:56:05 +02:00
|
|
|
|
#if defined(USE_STATFS) || defined(USE_STATVFS)
|
2007-12-20 14:30:47 +01:00
|
|
|
|
g_file_info_set_attribute_uint64 (info, G_FILE_ATTRIBUTE_FILESYSTEM_SIZE, block_size * statfs_buffer.f_blocks);
|
2011-07-21 02:56:05 +02:00
|
|
|
|
#endif
|
2012-01-10 03:49:08 +01:00
|
|
|
|
#endif /* G_OS_WIN32 */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!no_size &&
|
|
|
|
|
g_file_attribute_matcher_matches (attribute_matcher,
|
|
|
|
|
G_FILE_ATTRIBUTE_FILESYSTEM_USED))
|
|
|
|
|
{
|
|
|
|
|
#ifdef G_OS_WIN32
|
|
|
|
|
gchar *localdir = g_path_get_dirname (local->filename);
|
|
|
|
|
wchar_t *wdirname = g_utf8_to_utf16 (localdir, -1, NULL, NULL, NULL);
|
|
|
|
|
ULARGE_INTEGER li_free;
|
|
|
|
|
ULARGE_INTEGER li_total;
|
|
|
|
|
|
|
|
|
|
g_free (localdir);
|
|
|
|
|
if (GetDiskFreeSpaceExW (wdirname, &li_free, &li_total, NULL))
|
|
|
|
|
g_file_info_set_attribute_uint64 (info, G_FILE_ATTRIBUTE_FILESYSTEM_USED, (guint64)li_total.QuadPart - (guint64)li_free.QuadPart);
|
|
|
|
|
g_free (wdirname);
|
|
|
|
|
#else
|
|
|
|
|
g_file_info_set_attribute_uint64 (info, G_FILE_ATTRIBUTE_FILESYSTEM_USED, block_size * (statfs_buffer.f_blocks - statfs_buffer.f_bfree));
|
2012-01-10 03:35:13 +01:00
|
|
|
|
#endif /* G_OS_WIN32 */
|
2007-12-08 13:01:06 +01:00
|
|
|
|
}
|
2011-07-26 11:20:12 +02:00
|
|
|
|
|
|
|
|
|
#ifndef G_OS_WIN32
|
2007-11-26 17:13:05 +01:00
|
|
|
|
#ifdef USE_STATFS
|
2008-03-14 10:19:09 +01:00
|
|
|
|
#if defined(HAVE_STRUCT_STATFS_F_FSTYPENAME)
|
2017-11-15 14:08:11 +01:00
|
|
|
|
fstype = statfs_buffer.f_fstypename;
|
2008-03-07 15:36:15 +01:00
|
|
|
|
#else
|
2007-11-26 17:13:05 +01:00
|
|
|
|
fstype = get_fs_type (statfs_buffer.f_type);
|
2008-03-07 15:36:15 +01:00
|
|
|
|
#endif
|
2008-05-27 09:51:13 +02:00
|
|
|
|
|
2011-09-01 11:10:38 +02:00
|
|
|
|
#elif defined(USE_STATVFS)
|
|
|
|
|
#if defined(HAVE_STRUCT_STATVFS_F_FSTYPENAME)
|
2017-11-15 14:08:11 +01:00
|
|
|
|
fstype = statfs_buffer.f_fstypename;
|
2011-09-01 11:10:38 +02:00
|
|
|
|
#elif defined(HAVE_STRUCT_STATVFS_F_BASETYPE)
|
2017-11-15 14:08:11 +01:00
|
|
|
|
fstype = statfs_buffer.f_basetype;
|
2011-07-21 02:56:05 +02:00
|
|
|
|
#else
|
|
|
|
|
fstype = NULL;
|
2011-09-01 11:10:38 +02:00
|
|
|
|
#endif
|
2012-01-10 03:35:13 +01:00
|
|
|
|
#endif /* USE_STATFS */
|
2008-05-27 09:51:13 +02:00
|
|
|
|
|
2007-11-26 17:13:05 +01:00
|
|
|
|
if (fstype &&
|
|
|
|
|
g_file_attribute_matcher_matches (attribute_matcher,
|
2007-12-20 14:30:47 +01:00
|
|
|
|
G_FILE_ATTRIBUTE_FILESYSTEM_TYPE))
|
|
|
|
|
g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_FILESYSTEM_TYPE, fstype);
|
2012-01-10 03:35:13 +01:00
|
|
|
|
#endif /* G_OS_WIN32 */
|
2007-11-26 17:13:05 +01:00
|
|
|
|
|
|
|
|
|
if (g_file_attribute_matcher_matches (attribute_matcher,
|
2007-12-20 14:30:47 +01:00
|
|
|
|
G_FILE_ATTRIBUTE_FILESYSTEM_READONLY))
|
2007-11-26 17:13:05 +01:00
|
|
|
|
{
|
2007-12-08 13:01:06 +01:00
|
|
|
|
#ifdef G_OS_WIN32
|
2008-02-25 09:15:35 +01:00
|
|
|
|
get_filesystem_readonly (info, local->filename);
|
2007-12-08 13:01:06 +01:00
|
|
|
|
#else
|
2007-11-26 17:13:05 +01:00
|
|
|
|
get_mount_info (info, local->filename, attribute_matcher);
|
2012-01-10 03:35:13 +01:00
|
|
|
|
#endif /* G_OS_WIN32 */
|
2007-11-26 17:13:05 +01:00
|
|
|
|
}
|
2020-06-15 17:14:30 +02:00
|
|
|
|
|
|
|
|
|
#ifndef G_OS_WIN32
|
2016-06-22 14:03:57 +02:00
|
|
|
|
if (g_file_attribute_matcher_matches (attribute_matcher,
|
2020-06-15 17:14:30 +02:00
|
|
|
|
G_FILE_ATTRIBUTE_FILESYSTEM_REMOTE))
|
|
|
|
|
g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_FILESYSTEM_REMOTE,
|
2020-06-15 17:24:37 +02:00
|
|
|
|
is_remote_fs_type (fstype));
|
2020-06-15 17:14:30 +02:00
|
|
|
|
#endif
|
2016-06-22 14:03:57 +02:00
|
|
|
|
|
2007-11-26 17:13:05 +01:00
|
|
|
|
g_file_attribute_matcher_unref (attribute_matcher);
|
|
|
|
|
|
|
|
|
|
return info;
|
|
|
|
|
}
|
|
|
|
|
|
Rework how volumes, drives and volume monitoring is done. Previosly the
2007-12-11 David Zeuthen <davidz@redhat.com>
Rework how volumes, drives and volume monitoring is
done. Previosly the model was
GDrive <1-1> GVolume
where a GDrive instance represented a mount point and a GVolume
instance represented a mounted file system. This patch changes it
the model to
GDrive <1-N> GVolume <1-1> GMount
where GMount now serves the purpose of the old GVolume and the new
GVolume serves the purpose of the old GDrive. In addition the new
GDrive interface is used to represent a collection of GVolume
instances (typically partitions) and also contains utility to query
the state of the physical drive the GDrive object represents (such
as checking for media, polling the drive, ejecting the media etc.).
Also implement mounting and unmounting in the Unix volume monitor
backend. A subquent patch will introduce GDrive support for ejection
of media.
* Makefile.am:
* gdrive.c: (g_drive_is_media_check_automatic),
(g_drive_is_media_removable), (g_drive_has_media),
(g_drive_can_poll_for_media), (g_drive_eject),
(g_drive_eject_finish), (g_drive_poll_for_media),
(g_drive_poll_for_media_finish):
* gdrive.h:
* gfile.c: (g_file_find_enclosing_mount):
* gfile.h:
* gio.symbols:
* glocaldirectorymonitor.c:
(g_local_directory_monitor_constructor), (mounts_changed):
* glocalfile.c: (get_mount_info),
(g_local_file_find_enclosing_mount),
(g_local_file_file_iface_init):
* gnativevolumemonitor.h:
* gunionvolumemonitor.c: (get_mounts), (get_volumes),
(get_connected_drives), (g_union_volume_monitor_class_init),
(child_volume_added), (child_volume_removed),
(child_volume_changed), (child_mount_added), (child_mount_removed),
(child_mount_pre_unmount), (child_mount_changed),
(child_drive_changed), (g_union_volume_monitor_add_monitor),
(g_union_volume_monitor_remove_monitor),
(_g_mount_get_for_mount_path):
* gunixmounts.c: (g_unix_is_mount_path_system_internal),
(guess_system_internal), (_g_get_unix_mounts),
(_g_get_unix_mount_points), (g_get_unix_mount_at),
(g_unix_mount_free), (g_unix_mount_compare),
(g_unix_mount_get_mount_path), (g_unix_mount_get_device_path),
(g_unix_mount_get_fs_type), (g_unix_mount_is_readonly),
(g_unix_mount_is_system_internal), (g_unix_mount_guess_type),
(type_to_icon), (g_unix_mount_guess_name),
(g_unix_mount_guess_icon), (g_unix_mount_point_guess_name),
(g_unix_mount_point_guess_icon), (_canonicalize_filename),
(_resolve_symlink), (_resolve_dev_root):
* gunixmounts.h:
* gunixvolume.c: (g_unix_volume_finalize), (_g_unix_volume_new),
(_g_unix_volume_disconnected), (_g_unix_volume_set_mount),
(_g_unix_volume_unset_mount), (g_unix_volume_get_icon),
(g_unix_volume_get_name), (g_unix_volume_can_mount),
(g_unix_volume_get_drive), (g_unix_volume_get_mount),
(_g_unix_volume_has_mount_path), (mount_cb), (mount_read_error),
(g_unix_volume_mount), (g_unix_volume_mount_finish),
(g_unix_volume_volume_iface_init):
* gunixvolume.h:
* gunixvolumemonitor.c: (g_unix_volume_monitor_finalize),
(get_mounts), (get_volumes), (get_connected_drives),
(get_mount_for_mount_path), (g_unix_volume_monitor_class_init),
(mountpoints_changed), (mounts_changed),
(g_unix_volume_monitor_init),
(_g_unix_volume_monitor_lookup_volume_for_mount_path),
(find_mount_by_mountpath), (update_volumes), (update_mounts):
* gunixvolumemonitor.h:
* gvolume.c: (g_volume_get_mount), (g_volume_can_mount),
(g_volume_mount), (g_volume_mount_finish):
* gvolume.h:
* gvolumemonitor.c: (g_volume_monitor_class_init),
(g_volume_monitor_get_connected_drives),
(g_volume_monitor_get_volumes), (g_volume_monitor_get_mounts):
* gvolumemonitor.h:
svn path=/trunk/; revision=6095
2007-12-11 22:23:55 +01:00
|
|
|
|
static GMount *
|
|
|
|
|
g_local_file_find_enclosing_mount (GFile *file,
|
|
|
|
|
GCancellable *cancellable,
|
|
|
|
|
GError **error)
|
2007-11-26 17:13:05 +01:00
|
|
|
|
{
|
|
|
|
|
GLocalFile *local = G_LOCAL_FILE (file);
|
2010-03-21 19:04:18 +01:00
|
|
|
|
GStatBuf buf;
|
2007-11-26 17:13:05 +01:00
|
|
|
|
char *mountpoint;
|
Rework how volumes, drives and volume monitoring is done. Previosly the
2007-12-11 David Zeuthen <davidz@redhat.com>
Rework how volumes, drives and volume monitoring is
done. Previosly the model was
GDrive <1-1> GVolume
where a GDrive instance represented a mount point and a GVolume
instance represented a mounted file system. This patch changes it
the model to
GDrive <1-N> GVolume <1-1> GMount
where GMount now serves the purpose of the old GVolume and the new
GVolume serves the purpose of the old GDrive. In addition the new
GDrive interface is used to represent a collection of GVolume
instances (typically partitions) and also contains utility to query
the state of the physical drive the GDrive object represents (such
as checking for media, polling the drive, ejecting the media etc.).
Also implement mounting and unmounting in the Unix volume monitor
backend. A subquent patch will introduce GDrive support for ejection
of media.
* Makefile.am:
* gdrive.c: (g_drive_is_media_check_automatic),
(g_drive_is_media_removable), (g_drive_has_media),
(g_drive_can_poll_for_media), (g_drive_eject),
(g_drive_eject_finish), (g_drive_poll_for_media),
(g_drive_poll_for_media_finish):
* gdrive.h:
* gfile.c: (g_file_find_enclosing_mount):
* gfile.h:
* gio.symbols:
* glocaldirectorymonitor.c:
(g_local_directory_monitor_constructor), (mounts_changed):
* glocalfile.c: (get_mount_info),
(g_local_file_find_enclosing_mount),
(g_local_file_file_iface_init):
* gnativevolumemonitor.h:
* gunionvolumemonitor.c: (get_mounts), (get_volumes),
(get_connected_drives), (g_union_volume_monitor_class_init),
(child_volume_added), (child_volume_removed),
(child_volume_changed), (child_mount_added), (child_mount_removed),
(child_mount_pre_unmount), (child_mount_changed),
(child_drive_changed), (g_union_volume_monitor_add_monitor),
(g_union_volume_monitor_remove_monitor),
(_g_mount_get_for_mount_path):
* gunixmounts.c: (g_unix_is_mount_path_system_internal),
(guess_system_internal), (_g_get_unix_mounts),
(_g_get_unix_mount_points), (g_get_unix_mount_at),
(g_unix_mount_free), (g_unix_mount_compare),
(g_unix_mount_get_mount_path), (g_unix_mount_get_device_path),
(g_unix_mount_get_fs_type), (g_unix_mount_is_readonly),
(g_unix_mount_is_system_internal), (g_unix_mount_guess_type),
(type_to_icon), (g_unix_mount_guess_name),
(g_unix_mount_guess_icon), (g_unix_mount_point_guess_name),
(g_unix_mount_point_guess_icon), (_canonicalize_filename),
(_resolve_symlink), (_resolve_dev_root):
* gunixmounts.h:
* gunixvolume.c: (g_unix_volume_finalize), (_g_unix_volume_new),
(_g_unix_volume_disconnected), (_g_unix_volume_set_mount),
(_g_unix_volume_unset_mount), (g_unix_volume_get_icon),
(g_unix_volume_get_name), (g_unix_volume_can_mount),
(g_unix_volume_get_drive), (g_unix_volume_get_mount),
(_g_unix_volume_has_mount_path), (mount_cb), (mount_read_error),
(g_unix_volume_mount), (g_unix_volume_mount_finish),
(g_unix_volume_volume_iface_init):
* gunixvolume.h:
* gunixvolumemonitor.c: (g_unix_volume_monitor_finalize),
(get_mounts), (get_volumes), (get_connected_drives),
(get_mount_for_mount_path), (g_unix_volume_monitor_class_init),
(mountpoints_changed), (mounts_changed),
(g_unix_volume_monitor_init),
(_g_unix_volume_monitor_lookup_volume_for_mount_path),
(find_mount_by_mountpath), (update_volumes), (update_mounts):
* gunixvolumemonitor.h:
* gvolume.c: (g_volume_get_mount), (g_volume_can_mount),
(g_volume_mount), (g_volume_mount_finish):
* gvolume.h:
* gvolumemonitor.c: (g_volume_monitor_class_init),
(g_volume_monitor_get_connected_drives),
(g_volume_monitor_get_volumes), (g_volume_monitor_get_mounts):
* gvolumemonitor.h:
svn path=/trunk/; revision=6095
2007-12-11 22:23:55 +01:00
|
|
|
|
GMount *mount;
|
2007-11-26 17:13:05 +01:00
|
|
|
|
|
2007-12-08 13:01:06 +01:00
|
|
|
|
if (g_lstat (local->filename, &buf) != 0)
|
2015-08-26 17:06:31 +02:00
|
|
|
|
goto error;
|
2007-11-26 17:13:05 +01:00
|
|
|
|
|
2018-09-13 17:33:59 +02:00
|
|
|
|
mountpoint = find_mountpoint_for (local->filename, buf.st_dev, FALSE);
|
2007-11-26 17:13:05 +01:00
|
|
|
|
if (mountpoint == NULL)
|
2015-08-26 17:06:31 +02:00
|
|
|
|
goto error;
|
2007-11-26 17:13:05 +01:00
|
|
|
|
|
2007-12-17 17:00:33 +01:00
|
|
|
|
mount = _g_mount_get_for_mount_path (mountpoint, cancellable);
|
2007-11-26 17:13:05 +01:00
|
|
|
|
g_free (mountpoint);
|
Rework how volumes, drives and volume monitoring is done. Previosly the
2007-12-11 David Zeuthen <davidz@redhat.com>
Rework how volumes, drives and volume monitoring is
done. Previosly the model was
GDrive <1-1> GVolume
where a GDrive instance represented a mount point and a GVolume
instance represented a mounted file system. This patch changes it
the model to
GDrive <1-N> GVolume <1-1> GMount
where GMount now serves the purpose of the old GVolume and the new
GVolume serves the purpose of the old GDrive. In addition the new
GDrive interface is used to represent a collection of GVolume
instances (typically partitions) and also contains utility to query
the state of the physical drive the GDrive object represents (such
as checking for media, polling the drive, ejecting the media etc.).
Also implement mounting and unmounting in the Unix volume monitor
backend. A subquent patch will introduce GDrive support for ejection
of media.
* Makefile.am:
* gdrive.c: (g_drive_is_media_check_automatic),
(g_drive_is_media_removable), (g_drive_has_media),
(g_drive_can_poll_for_media), (g_drive_eject),
(g_drive_eject_finish), (g_drive_poll_for_media),
(g_drive_poll_for_media_finish):
* gdrive.h:
* gfile.c: (g_file_find_enclosing_mount):
* gfile.h:
* gio.symbols:
* glocaldirectorymonitor.c:
(g_local_directory_monitor_constructor), (mounts_changed):
* glocalfile.c: (get_mount_info),
(g_local_file_find_enclosing_mount),
(g_local_file_file_iface_init):
* gnativevolumemonitor.h:
* gunionvolumemonitor.c: (get_mounts), (get_volumes),
(get_connected_drives), (g_union_volume_monitor_class_init),
(child_volume_added), (child_volume_removed),
(child_volume_changed), (child_mount_added), (child_mount_removed),
(child_mount_pre_unmount), (child_mount_changed),
(child_drive_changed), (g_union_volume_monitor_add_monitor),
(g_union_volume_monitor_remove_monitor),
(_g_mount_get_for_mount_path):
* gunixmounts.c: (g_unix_is_mount_path_system_internal),
(guess_system_internal), (_g_get_unix_mounts),
(_g_get_unix_mount_points), (g_get_unix_mount_at),
(g_unix_mount_free), (g_unix_mount_compare),
(g_unix_mount_get_mount_path), (g_unix_mount_get_device_path),
(g_unix_mount_get_fs_type), (g_unix_mount_is_readonly),
(g_unix_mount_is_system_internal), (g_unix_mount_guess_type),
(type_to_icon), (g_unix_mount_guess_name),
(g_unix_mount_guess_icon), (g_unix_mount_point_guess_name),
(g_unix_mount_point_guess_icon), (_canonicalize_filename),
(_resolve_symlink), (_resolve_dev_root):
* gunixmounts.h:
* gunixvolume.c: (g_unix_volume_finalize), (_g_unix_volume_new),
(_g_unix_volume_disconnected), (_g_unix_volume_set_mount),
(_g_unix_volume_unset_mount), (g_unix_volume_get_icon),
(g_unix_volume_get_name), (g_unix_volume_can_mount),
(g_unix_volume_get_drive), (g_unix_volume_get_mount),
(_g_unix_volume_has_mount_path), (mount_cb), (mount_read_error),
(g_unix_volume_mount), (g_unix_volume_mount_finish),
(g_unix_volume_volume_iface_init):
* gunixvolume.h:
* gunixvolumemonitor.c: (g_unix_volume_monitor_finalize),
(get_mounts), (get_volumes), (get_connected_drives),
(get_mount_for_mount_path), (g_unix_volume_monitor_class_init),
(mountpoints_changed), (mounts_changed),
(g_unix_volume_monitor_init),
(_g_unix_volume_monitor_lookup_volume_for_mount_path),
(find_mount_by_mountpath), (update_volumes), (update_mounts):
* gunixvolumemonitor.h:
* gvolume.c: (g_volume_get_mount), (g_volume_can_mount),
(g_volume_mount), (g_volume_mount_finish):
* gvolume.h:
* gvolumemonitor.c: (g_volume_monitor_class_init),
(g_volume_monitor_get_connected_drives),
(g_volume_monitor_get_volumes), (g_volume_monitor_get_mounts):
* gvolumemonitor.h:
svn path=/trunk/; revision=6095
2007-12-11 22:23:55 +01:00
|
|
|
|
if (mount)
|
|
|
|
|
return mount;
|
2007-11-26 17:13:05 +01:00
|
|
|
|
|
2015-08-26 17:06:31 +02:00
|
|
|
|
error:
|
|
|
|
|
g_set_io_error (error,
|
2017-01-26 04:52:17 +01:00
|
|
|
|
/* Translators: This is an error message when trying to find
|
|
|
|
|
* the enclosing (user visible) mount of a file, but none
|
|
|
|
|
* exists.
|
|
|
|
|
*/
|
2015-08-26 17:06:31 +02:00
|
|
|
|
_("Containing mount for file %s not found"),
|
|
|
|
|
file, 0);
|
|
|
|
|
|
2007-11-26 17:13:05 +01:00
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static GFile *
|
2007-11-30 06:11:25 +01:00
|
|
|
|
g_local_file_set_display_name (GFile *file,
|
|
|
|
|
const char *display_name,
|
|
|
|
|
GCancellable *cancellable,
|
|
|
|
|
GError **error)
|
2007-11-26 17:13:05 +01:00
|
|
|
|
{
|
|
|
|
|
GLocalFile *local, *new_local;
|
|
|
|
|
GFile *new_file, *parent;
|
2010-03-21 19:04:18 +01:00
|
|
|
|
GStatBuf statbuf;
|
2010-02-22 12:37:16 +01:00
|
|
|
|
GVfsClass *class;
|
|
|
|
|
GVfs *vfs;
|
2007-11-26 17:13:05 +01:00
|
|
|
|
int errsv;
|
|
|
|
|
|
|
|
|
|
parent = g_file_get_parent (file);
|
|
|
|
|
if (parent == NULL)
|
|
|
|
|
{
|
2015-08-26 17:06:31 +02:00
|
|
|
|
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
2016-09-30 05:47:15 +02:00
|
|
|
|
_("Can’t rename root directory"));
|
2007-11-26 17:13:05 +01:00
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2008-01-21 15:02:19 +01:00
|
|
|
|
new_file = g_file_get_child_for_display_name (parent, display_name, error);
|
2007-11-26 17:13:05 +01:00
|
|
|
|
g_object_unref (parent);
|
|
|
|
|
|
|
|
|
|
if (new_file == NULL)
|
|
|
|
|
return NULL;
|
|
|
|
|
local = G_LOCAL_FILE (file);
|
|
|
|
|
new_local = G_LOCAL_FILE (new_file);
|
|
|
|
|
|
2008-11-28 09:05:49 +01:00
|
|
|
|
if (g_lstat (new_local->filename, &statbuf) == -1)
|
|
|
|
|
{
|
|
|
|
|
errsv = errno;
|
|
|
|
|
|
|
|
|
|
if (errsv != ENOENT)
|
|
|
|
|
{
|
2015-08-26 17:06:31 +02:00
|
|
|
|
g_set_io_error (error, _("Error renaming file %s: %s"), new_file, errsv);
|
2008-11-28 09:05:49 +01:00
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
2007-11-26 17:13:05 +01:00
|
|
|
|
{
|
2015-08-26 17:06:31 +02:00
|
|
|
|
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_EXISTS,
|
2016-09-30 05:47:15 +02:00
|
|
|
|
_("Can’t rename file, filename already exists"));
|
2007-11-26 17:13:05 +01:00
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2008-02-18 16:35:16 +01:00
|
|
|
|
if (g_rename (local->filename, new_local->filename) == -1)
|
2007-11-26 17:13:05 +01:00
|
|
|
|
{
|
|
|
|
|
errsv = errno;
|
|
|
|
|
|
|
|
|
|
if (errsv == EINVAL)
|
2015-08-26 17:06:31 +02:00
|
|
|
|
/* We can't get a rename file into itself error here,
|
|
|
|
|
* so this must be an invalid filename, on e.g. FAT
|
|
|
|
|
*/
|
|
|
|
|
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_FILENAME,
|
2008-06-16 18:53:58 +02:00
|
|
|
|
_("Invalid filename"));
|
2007-11-26 17:13:05 +01:00
|
|
|
|
else
|
2015-08-26 17:06:31 +02:00
|
|
|
|
g_set_io_error (error,
|
|
|
|
|
_("Error renaming file %s: %s"),
|
|
|
|
|
file, errsv);
|
2007-11-26 17:13:05 +01:00
|
|
|
|
g_object_unref (new_file);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
2010-02-22 12:37:16 +01:00
|
|
|
|
|
|
|
|
|
vfs = g_vfs_get_default ();
|
|
|
|
|
class = G_VFS_GET_CLASS (vfs);
|
|
|
|
|
if (class->local_file_moved)
|
|
|
|
|
class->local_file_moved (vfs, local->filename, new_local->filename);
|
|
|
|
|
|
2007-11-26 17:13:05 +01:00
|
|
|
|
return new_file;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static GFileInfo *
|
|
|
|
|
g_local_file_query_info (GFile *file,
|
|
|
|
|
const char *attributes,
|
|
|
|
|
GFileQueryInfoFlags flags,
|
|
|
|
|
GCancellable *cancellable,
|
|
|
|
|
GError **error)
|
|
|
|
|
{
|
|
|
|
|
GLocalFile *local = G_LOCAL_FILE (file);
|
|
|
|
|
GFileInfo *info;
|
|
|
|
|
GFileAttributeMatcher *matcher;
|
|
|
|
|
char *basename, *dirname;
|
|
|
|
|
GLocalParentFileInfo parent_info;
|
|
|
|
|
|
|
|
|
|
matcher = g_file_attribute_matcher_new (attributes);
|
|
|
|
|
|
|
|
|
|
basename = g_path_get_basename (local->filename);
|
|
|
|
|
|
|
|
|
|
dirname = g_path_get_dirname (local->filename);
|
|
|
|
|
_g_local_file_info_get_parent_info (dirname, matcher, &parent_info);
|
|
|
|
|
g_free (dirname);
|
|
|
|
|
|
|
|
|
|
info = _g_local_file_info_get (basename, local->filename,
|
|
|
|
|
matcher, flags, &parent_info,
|
|
|
|
|
error);
|
|
|
|
|
|
2009-06-18 09:05:27 +02:00
|
|
|
|
|
|
|
|
|
_g_local_file_info_free_parent_info (&parent_info);
|
2007-11-26 17:13:05 +01:00
|
|
|
|
g_free (basename);
|
|
|
|
|
|
|
|
|
|
g_file_attribute_matcher_unref (matcher);
|
|
|
|
|
|
|
|
|
|
return info;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static GFileAttributeInfoList *
|
2007-11-30 06:11:25 +01:00
|
|
|
|
g_local_file_query_settable_attributes (GFile *file,
|
|
|
|
|
GCancellable *cancellable,
|
|
|
|
|
GError **error)
|
2007-11-26 17:13:05 +01:00
|
|
|
|
{
|
|
|
|
|
return g_file_attribute_info_list_ref (local_writable_attributes);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static GFileAttributeInfoList *
|
2007-11-30 06:11:25 +01:00
|
|
|
|
g_local_file_query_writable_namespaces (GFile *file,
|
|
|
|
|
GCancellable *cancellable,
|
|
|
|
|
GError **error)
|
2007-11-26 17:13:05 +01:00
|
|
|
|
{
|
2009-06-18 09:05:27 +02:00
|
|
|
|
GFileAttributeInfoList *list;
|
|
|
|
|
GVfsClass *class;
|
|
|
|
|
GVfs *vfs;
|
|
|
|
|
|
|
|
|
|
if (g_once_init_enter (&local_writable_namespaces))
|
|
|
|
|
{
|
|
|
|
|
/* Writable namespaces: */
|
|
|
|
|
|
|
|
|
|
list = g_file_attribute_info_list_new ();
|
|
|
|
|
|
|
|
|
|
#ifdef HAVE_XATTR
|
|
|
|
|
g_file_attribute_info_list_add (list,
|
|
|
|
|
"xattr",
|
|
|
|
|
G_FILE_ATTRIBUTE_TYPE_STRING,
|
|
|
|
|
G_FILE_ATTRIBUTE_INFO_COPY_WITH_FILE |
|
|
|
|
|
G_FILE_ATTRIBUTE_INFO_COPY_WHEN_MOVED);
|
|
|
|
|
g_file_attribute_info_list_add (list,
|
|
|
|
|
"xattr-sys",
|
|
|
|
|
G_FILE_ATTRIBUTE_TYPE_STRING,
|
|
|
|
|
G_FILE_ATTRIBUTE_INFO_COPY_WHEN_MOVED);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
vfs = g_vfs_get_default ();
|
|
|
|
|
class = G_VFS_GET_CLASS (vfs);
|
|
|
|
|
if (class->add_writable_namespaces)
|
|
|
|
|
class->add_writable_namespaces (vfs, list);
|
|
|
|
|
|
|
|
|
|
g_once_init_leave (&local_writable_namespaces, (gsize)list);
|
|
|
|
|
}
|
|
|
|
|
list = (GFileAttributeInfoList *)local_writable_namespaces;
|
|
|
|
|
|
|
|
|
|
return g_file_attribute_info_list_ref (list);
|
2007-11-26 17:13:05 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
2008-01-21 15:02:19 +01:00
|
|
|
|
g_local_file_set_attribute (GFile *file,
|
|
|
|
|
const char *attribute,
|
|
|
|
|
GFileAttributeType type,
|
|
|
|
|
gpointer value_p,
|
|
|
|
|
GFileQueryInfoFlags flags,
|
|
|
|
|
GCancellable *cancellable,
|
|
|
|
|
GError **error)
|
2007-11-26 17:13:05 +01:00
|
|
|
|
{
|
|
|
|
|
GLocalFile *local = G_LOCAL_FILE (file);
|
|
|
|
|
|
|
|
|
|
return _g_local_file_info_set_attribute (local->filename,
|
|
|
|
|
attribute,
|
2007-12-14 16:56:56 +01:00
|
|
|
|
type,
|
|
|
|
|
value_p,
|
2007-11-26 17:13:05 +01:00
|
|
|
|
flags,
|
|
|
|
|
cancellable,
|
|
|
|
|
error);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
2007-11-30 06:11:25 +01:00
|
|
|
|
g_local_file_set_attributes_from_info (GFile *file,
|
|
|
|
|
GFileInfo *info,
|
|
|
|
|
GFileQueryInfoFlags flags,
|
|
|
|
|
GCancellable *cancellable,
|
|
|
|
|
GError **error)
|
2007-11-26 17:13:05 +01:00
|
|
|
|
{
|
|
|
|
|
GLocalFile *local = G_LOCAL_FILE (file);
|
|
|
|
|
int res, chained_res;
|
2008-01-21 15:02:19 +01:00
|
|
|
|
GFileIface *default_iface;
|
2007-11-26 17:13:05 +01:00
|
|
|
|
|
|
|
|
|
res = _g_local_file_info_set_attributes (local->filename,
|
|
|
|
|
info, flags,
|
|
|
|
|
cancellable,
|
|
|
|
|
error);
|
|
|
|
|
|
|
|
|
|
if (!res)
|
|
|
|
|
error = NULL; /* Don't write over error if further errors */
|
|
|
|
|
|
|
|
|
|
default_iface = g_type_default_interface_peek (G_TYPE_FILE);
|
|
|
|
|
|
|
|
|
|
chained_res = (default_iface->set_attributes_from_info) (file, info, flags, cancellable, error);
|
|
|
|
|
|
|
|
|
|
return res && chained_res;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static GFileInputStream *
|
2007-11-30 06:11:25 +01:00
|
|
|
|
g_local_file_read (GFile *file,
|
|
|
|
|
GCancellable *cancellable,
|
|
|
|
|
GError **error)
|
2007-11-26 17:13:05 +01:00
|
|
|
|
{
|
|
|
|
|
GLocalFile *local = G_LOCAL_FILE (file);
|
2011-04-15 10:15:04 +02:00
|
|
|
|
int fd, ret;
|
|
|
|
|
GLocalFileStat buf;
|
2007-11-26 17:13:05 +01:00
|
|
|
|
|
2008-02-18 11:10:58 +01:00
|
|
|
|
fd = g_open (local->filename, O_RDONLY|O_BINARY, 0);
|
2007-11-26 17:13:05 +01:00
|
|
|
|
if (fd == -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;
|
|
|
|
|
|
2012-02-03 17:45:51 +01:00
|
|
|
|
#ifdef G_OS_WIN32
|
|
|
|
|
if (errsv == EACCES)
|
|
|
|
|
{
|
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
|
|
|
|
/* Exploit the fact that on W32 the glib filename encoding is UTF8 */
|
|
|
|
|
ret = GLIB_PRIVATE_CALL (g_win32_stat_utf8) (local->filename, &buf);
|
2012-02-03 17:45:51 +01:00
|
|
|
|
if (ret == 0 && S_ISDIR (buf.st_mode))
|
2015-08-26 17:06:31 +02:00
|
|
|
|
errsv = EISDIR;
|
2012-02-03 17:45:51 +01:00
|
|
|
|
}
|
|
|
|
|
#endif
|
2015-08-26 17:06:31 +02:00
|
|
|
|
g_set_io_error (error,
|
|
|
|
|
_("Error opening file %s: %s"),
|
|
|
|
|
file, errsv);
|
2007-11-26 17:13:05 +01:00
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-14 16:52:03 +02:00
|
|
|
|
ret = g_local_file_fstat (fd, G_LOCAL_FILE_STAT_FIELD_TYPE, G_LOCAL_FILE_STAT_FIELD_ALL, &buf);
|
2011-04-15 10:15:04 +02:00
|
|
|
|
|
2020-08-14 15:46:14 +02:00
|
|
|
|
if (ret == 0 && S_ISDIR (_g_stat_mode (&buf)))
|
2007-11-26 17:13:05 +01:00
|
|
|
|
{
|
2013-01-25 18:05:26 +01:00
|
|
|
|
(void) g_close (fd, NULL);
|
2015-08-26 17:06:31 +02:00
|
|
|
|
g_set_io_error (error,
|
|
|
|
|
_("Error opening file %s: %s"),
|
|
|
|
|
file, EISDIR);
|
2007-11-26 17:13:05 +01:00
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2007-11-28 17:01:59 +01:00
|
|
|
|
return _g_local_file_input_stream_new (fd);
|
2007-11-26 17:13:05 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static GFileOutputStream *
|
2007-11-30 06:11:25 +01:00
|
|
|
|
g_local_file_append_to (GFile *file,
|
|
|
|
|
GFileCreateFlags flags,
|
|
|
|
|
GCancellable *cancellable,
|
|
|
|
|
GError **error)
|
2007-11-26 17:13:05 +01:00
|
|
|
|
{
|
2007-11-28 17:01:59 +01:00
|
|
|
|
return _g_local_file_output_stream_append (G_LOCAL_FILE (file)->filename,
|
|
|
|
|
flags, cancellable, error);
|
2007-11-26 17:13:05 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static GFileOutputStream *
|
2007-11-30 06:11:25 +01:00
|
|
|
|
g_local_file_create (GFile *file,
|
|
|
|
|
GFileCreateFlags flags,
|
|
|
|
|
GCancellable *cancellable,
|
|
|
|
|
GError **error)
|
2007-11-26 17:13:05 +01:00
|
|
|
|
{
|
2007-11-28 17:01:59 +01:00
|
|
|
|
return _g_local_file_output_stream_create (G_LOCAL_FILE (file)->filename,
|
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
|
|
|
|
FALSE, flags, NULL,
|
|
|
|
|
cancellable, error);
|
2007-11-26 17:13:05 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static GFileOutputStream *
|
2007-11-30 06:11:25 +01:00
|
|
|
|
g_local_file_replace (GFile *file,
|
|
|
|
|
const char *etag,
|
|
|
|
|
gboolean make_backup,
|
|
|
|
|
GFileCreateFlags flags,
|
|
|
|
|
GCancellable *cancellable,
|
|
|
|
|
GError **error)
|
2007-11-26 17:13:05 +01:00
|
|
|
|
{
|
2007-11-28 17:01:59 +01:00
|
|
|
|
return _g_local_file_output_stream_replace (G_LOCAL_FILE (file)->filename,
|
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
|
|
|
|
FALSE,
|
|
|
|
|
etag, make_backup, flags, NULL,
|
|
|
|
|
cancellable, error);
|
2007-11-26 17:13:05 +01:00
|
|
|
|
}
|
|
|
|
|
|
2009-05-13 13:03:47 +02:00
|
|
|
|
static GFileIOStream *
|
|
|
|
|
g_local_file_open_readwrite (GFile *file,
|
|
|
|
|
GCancellable *cancellable,
|
|
|
|
|
GError **error)
|
|
|
|
|
{
|
|
|
|
|
GFileOutputStream *output;
|
|
|
|
|
GFileIOStream *res;
|
|
|
|
|
|
|
|
|
|
output = _g_local_file_output_stream_open (G_LOCAL_FILE (file)->filename,
|
|
|
|
|
TRUE,
|
|
|
|
|
cancellable, error);
|
|
|
|
|
if (output == NULL)
|
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
|
|
res = _g_local_file_io_stream_new (G_LOCAL_FILE_OUTPUT_STREAM (output));
|
|
|
|
|
g_object_unref (output);
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static GFileIOStream *
|
|
|
|
|
g_local_file_create_readwrite (GFile *file,
|
|
|
|
|
GFileCreateFlags flags,
|
|
|
|
|
GCancellable *cancellable,
|
|
|
|
|
GError **error)
|
|
|
|
|
{
|
|
|
|
|
GFileOutputStream *output;
|
|
|
|
|
GFileIOStream *res;
|
|
|
|
|
|
|
|
|
|
output = _g_local_file_output_stream_create (G_LOCAL_FILE (file)->filename,
|
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
|
|
|
|
TRUE, flags, NULL,
|
2009-05-13 13:03:47 +02:00
|
|
|
|
cancellable, error);
|
|
|
|
|
if (output == NULL)
|
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
|
|
res = _g_local_file_io_stream_new (G_LOCAL_FILE_OUTPUT_STREAM (output));
|
|
|
|
|
g_object_unref (output);
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static GFileIOStream *
|
|
|
|
|
g_local_file_replace_readwrite (GFile *file,
|
|
|
|
|
const char *etag,
|
|
|
|
|
gboolean make_backup,
|
|
|
|
|
GFileCreateFlags flags,
|
|
|
|
|
GCancellable *cancellable,
|
|
|
|
|
GError **error)
|
|
|
|
|
{
|
|
|
|
|
GFileOutputStream *output;
|
|
|
|
|
GFileIOStream *res;
|
|
|
|
|
|
|
|
|
|
output = _g_local_file_output_stream_replace (G_LOCAL_FILE (file)->filename,
|
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
|
|
|
|
TRUE,
|
|
|
|
|
etag, make_backup, flags, NULL,
|
|
|
|
|
cancellable, error);
|
2009-05-13 13:03:47 +02:00
|
|
|
|
if (output == NULL)
|
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
|
|
res = _g_local_file_io_stream_new (G_LOCAL_FILE_OUTPUT_STREAM (output));
|
|
|
|
|
g_object_unref (output);
|
|
|
|
|
return res;
|
|
|
|
|
}
|
2007-11-26 17:13:05 +01:00
|
|
|
|
|
|
|
|
|
static gboolean
|
2007-11-30 06:11:25 +01:00
|
|
|
|
g_local_file_delete (GFile *file,
|
|
|
|
|
GCancellable *cancellable,
|
|
|
|
|
GError **error)
|
2007-11-26 17:13:05 +01:00
|
|
|
|
{
|
|
|
|
|
GLocalFile *local = G_LOCAL_FILE (file);
|
2009-06-18 09:05:27 +02:00
|
|
|
|
GVfsClass *class;
|
|
|
|
|
GVfs *vfs;
|
|
|
|
|
|
2007-11-26 17:13:05 +01:00
|
|
|
|
if (g_remove (local->filename) == -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;
|
|
|
|
|
|
2020-06-23 11:49:44 +02:00
|
|
|
|
/* Posix allows EEXIST too, but the clearer error
|
2009-02-23 05:30:06 +01:00
|
|
|
|
is G_IO_ERROR_NOT_FOUND, and it's what nautilus
|
2008-02-27 12:16:58 +01:00
|
|
|
|
expects */
|
|
|
|
|
if (errsv == EEXIST)
|
|
|
|
|
errsv = ENOTEMPTY;
|
|
|
|
|
|
2015-08-26 17:06:31 +02:00
|
|
|
|
g_set_io_error (error,
|
|
|
|
|
_("Error removing file %s: %s"),
|
|
|
|
|
file, errsv);
|
2007-11-26 17:13:05 +01:00
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
2009-06-18 09:05:27 +02:00
|
|
|
|
|
|
|
|
|
vfs = g_vfs_get_default ();
|
|
|
|
|
class = G_VFS_GET_CLASS (vfs);
|
|
|
|
|
if (class->local_file_removed)
|
|
|
|
|
class->local_file_removed (vfs, local->filename);
|
|
|
|
|
|
2007-11-26 17:13:05 +01:00
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
2010-06-22 11:10:34 +02:00
|
|
|
|
#ifndef G_OS_WIN32
|
|
|
|
|
|
2007-11-26 17:13:05 +01:00
|
|
|
|
static char *
|
|
|
|
|
strip_trailing_slashes (const char *path)
|
|
|
|
|
{
|
|
|
|
|
char *path_copy;
|
|
|
|
|
int len;
|
|
|
|
|
|
|
|
|
|
path_copy = g_strdup (path);
|
|
|
|
|
len = strlen (path_copy);
|
|
|
|
|
while (len > 1 && path_copy[len-1] == '/')
|
|
|
|
|
path_copy[--len] = 0;
|
|
|
|
|
|
|
|
|
|
return path_copy;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static char *
|
|
|
|
|
expand_symlink (const char *link)
|
|
|
|
|
{
|
|
|
|
|
char *resolved, *canonical, *parent, *link2;
|
|
|
|
|
char symlink_value[4096];
|
2007-12-08 13:01:06 +01:00
|
|
|
|
#ifdef G_OS_WIN32
|
|
|
|
|
#else
|
2017-10-10 13:59:14 +02:00
|
|
|
|
gssize res;
|
2007-12-08 13:01:06 +01:00
|
|
|
|
#endif
|
2007-11-26 17:13:05 +01:00
|
|
|
|
|
2007-12-08 13:01:06 +01:00
|
|
|
|
#ifdef G_OS_WIN32
|
|
|
|
|
#else
|
2007-11-26 17:13:05 +01:00
|
|
|
|
res = readlink (link, symlink_value, sizeof (symlink_value) - 1);
|
2007-12-08 13:01:06 +01:00
|
|
|
|
|
2007-11-26 17:13:05 +01:00
|
|
|
|
if (res == -1)
|
|
|
|
|
return g_strdup (link);
|
|
|
|
|
symlink_value[res] = 0;
|
2007-12-08 13:01:06 +01:00
|
|
|
|
#endif
|
2007-11-26 17:13:05 +01:00
|
|
|
|
|
|
|
|
|
if (g_path_is_absolute (symlink_value))
|
2018-04-27 18:01:45 +02:00
|
|
|
|
return g_canonicalize_filename (symlink_value, NULL);
|
2007-11-26 17:13:05 +01:00
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
link2 = strip_trailing_slashes (link);
|
|
|
|
|
parent = g_path_get_dirname (link2);
|
|
|
|
|
g_free (link2);
|
|
|
|
|
|
|
|
|
|
resolved = g_build_filename (parent, symlink_value, NULL);
|
|
|
|
|
g_free (parent);
|
|
|
|
|
|
2018-04-27 18:01:45 +02:00
|
|
|
|
canonical = g_canonicalize_filename (resolved, NULL);
|
2007-11-26 17:13:05 +01:00
|
|
|
|
|
|
|
|
|
g_free (resolved);
|
|
|
|
|
|
|
|
|
|
return canonical;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static char *
|
2018-09-13 17:33:59 +02:00
|
|
|
|
expand_symlinks (const char *path,
|
|
|
|
|
dev_t *dev)
|
2007-11-26 17:13:05 +01:00
|
|
|
|
{
|
2018-09-13 17:33:59 +02:00
|
|
|
|
char *tmp, *target;
|
|
|
|
|
GStatBuf target_stat;
|
2007-11-26 17:13:05 +01:00
|
|
|
|
int num_recursions;
|
2018-09-13 17:33:59 +02:00
|
|
|
|
|
|
|
|
|
target = g_strdup (path);
|
|
|
|
|
|
|
|
|
|
num_recursions = 0;
|
|
|
|
|
do
|
|
|
|
|
{
|
|
|
|
|
if (g_lstat (target, &target_stat) != 0)
|
|
|
|
|
{
|
|
|
|
|
g_free (target);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (S_ISLNK (target_stat.st_mode))
|
|
|
|
|
{
|
|
|
|
|
tmp = target;
|
|
|
|
|
target = expand_symlink (target);
|
|
|
|
|
g_free (tmp);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
num_recursions++;
|
2018-09-21 16:12:51 +02:00
|
|
|
|
|
|
|
|
|
#ifdef MAXSYMLINKS
|
|
|
|
|
if (num_recursions > MAXSYMLINKS)
|
|
|
|
|
#else
|
|
|
|
|
/* 40 is used in kernel sources currently:
|
|
|
|
|
* https://github.com/torvalds/linux/include/linux/namei.h
|
|
|
|
|
*/
|
|
|
|
|
if (num_recursions > 40)
|
|
|
|
|
#endif
|
2018-09-13 17:33:59 +02:00
|
|
|
|
{
|
|
|
|
|
g_free (target);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
while (S_ISLNK (target_stat.st_mode));
|
|
|
|
|
|
|
|
|
|
if (dev)
|
|
|
|
|
*dev = target_stat.st_dev;
|
|
|
|
|
|
|
|
|
|
return target;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static char *
|
|
|
|
|
get_parent (const char *path,
|
|
|
|
|
dev_t *parent_dev)
|
|
|
|
|
{
|
|
|
|
|
char *parent, *res;
|
2007-11-26 17:13:05 +01:00
|
|
|
|
char *path_copy;
|
|
|
|
|
|
|
|
|
|
path_copy = strip_trailing_slashes (path);
|
|
|
|
|
|
|
|
|
|
parent = g_path_get_dirname (path_copy);
|
2018-09-21 17:02:05 +02:00
|
|
|
|
if (strcmp (parent, ".") == 0)
|
2007-11-26 17:13:05 +01:00
|
|
|
|
{
|
2008-03-31 22:04:24 +02:00
|
|
|
|
g_free (parent);
|
2007-11-26 17:13:05 +01:00
|
|
|
|
g_free (path_copy);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
g_free (path_copy);
|
|
|
|
|
|
2018-09-13 17:33:59 +02:00
|
|
|
|
res = expand_symlinks (parent, parent_dev);
|
|
|
|
|
g_free (parent);
|
|
|
|
|
|
|
|
|
|
return res;
|
2007-11-26 17:13:05 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static char *
|
|
|
|
|
expand_all_symlinks (const char *path)
|
|
|
|
|
{
|
|
|
|
|
char *parent, *parent_expanded;
|
|
|
|
|
char *basename, *res;
|
|
|
|
|
dev_t parent_dev;
|
|
|
|
|
|
|
|
|
|
parent = get_parent (path, &parent_dev);
|
2018-09-21 17:02:05 +02:00
|
|
|
|
if (parent == NULL)
|
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
|
|
if (g_strcmp0 (parent, "/") != 0)
|
2007-11-26 17:13:05 +01:00
|
|
|
|
{
|
|
|
|
|
parent_expanded = expand_all_symlinks (parent);
|
|
|
|
|
basename = g_path_get_basename (path);
|
|
|
|
|
res = g_build_filename (parent_expanded, basename, NULL);
|
|
|
|
|
g_free (basename);
|
|
|
|
|
g_free (parent_expanded);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
res = g_strdup (path);
|
2018-09-21 17:02:05 +02:00
|
|
|
|
|
|
|
|
|
g_free (parent);
|
|
|
|
|
|
2007-11-26 17:13:05 +01:00
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static char *
|
2018-09-13 17:33:59 +02:00
|
|
|
|
find_mountpoint_for (const char *file,
|
|
|
|
|
dev_t dev,
|
|
|
|
|
gboolean resolve_basename_symlink)
|
2007-11-26 17:13:05 +01:00
|
|
|
|
{
|
|
|
|
|
char *dir, *parent;
|
|
|
|
|
dev_t dir_dev, parent_dev;
|
|
|
|
|
|
2018-09-13 17:33:59 +02:00
|
|
|
|
if (resolve_basename_symlink)
|
|
|
|
|
{
|
|
|
|
|
dir = expand_symlinks (file, NULL);
|
|
|
|
|
if (dir == NULL)
|
2018-09-21 17:02:05 +02:00
|
|
|
|
return NULL;
|
2018-09-13 17:33:59 +02:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
dir = g_strdup (file);
|
|
|
|
|
|
2007-11-26 17:13:05 +01:00
|
|
|
|
dir_dev = dev;
|
|
|
|
|
|
2018-09-21 17:02:05 +02:00
|
|
|
|
while (g_strcmp0 (dir, "/") != 0)
|
2007-11-30 06:11:25 +01:00
|
|
|
|
{
|
|
|
|
|
parent = get_parent (dir, &parent_dev);
|
|
|
|
|
if (parent == NULL)
|
2018-09-21 17:02:05 +02:00
|
|
|
|
{
|
|
|
|
|
g_free (dir);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2007-11-30 06:11:25 +01:00
|
|
|
|
if (parent_dev != dir_dev)
|
|
|
|
|
{
|
|
|
|
|
g_free (parent);
|
|
|
|
|
return dir;
|
|
|
|
|
}
|
2007-11-26 17:13:05 +01:00
|
|
|
|
|
2007-11-30 06:11:25 +01:00
|
|
|
|
g_free (dir);
|
|
|
|
|
dir = parent;
|
|
|
|
|
}
|
2018-09-21 17:02:05 +02:00
|
|
|
|
|
|
|
|
|
return dir;
|
2007-11-26 17:13:05 +01:00
|
|
|
|
}
|
|
|
|
|
|
2018-08-13 18:23:10 +02:00
|
|
|
|
char *
|
|
|
|
|
_g_local_file_find_topdir_for (const char *file)
|
2007-11-26 17:13:05 +01:00
|
|
|
|
{
|
|
|
|
|
char *dir;
|
2015-10-03 11:48:46 +02:00
|
|
|
|
char *mountpoint = NULL;
|
2007-11-26 17:13:05 +01:00
|
|
|
|
dev_t dir_dev;
|
|
|
|
|
|
|
|
|
|
dir = get_parent (file, &dir_dev);
|
2018-08-13 18:23:10 +02:00
|
|
|
|
if (dir == NULL)
|
|
|
|
|
return NULL;
|
2007-11-26 17:13:05 +01:00
|
|
|
|
|
2018-09-13 17:33:59 +02:00
|
|
|
|
mountpoint = find_mountpoint_for (dir, dir_dev, TRUE);
|
2015-10-03 11:48:46 +02:00
|
|
|
|
g_free (dir);
|
|
|
|
|
|
|
|
|
|
return mountpoint;
|
2007-11-26 17:13:05 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static char *
|
2007-11-30 06:11:25 +01:00
|
|
|
|
get_unique_filename (const char *basename,
|
|
|
|
|
int id)
|
2007-11-26 17:13:05 +01:00
|
|
|
|
{
|
|
|
|
|
const char *dot;
|
|
|
|
|
|
|
|
|
|
if (id == 1)
|
|
|
|
|
return g_strdup (basename);
|
|
|
|
|
|
|
|
|
|
dot = strchr (basename, '.');
|
|
|
|
|
if (dot)
|
2008-01-09 17:39:42 +01:00
|
|
|
|
return g_strdup_printf ("%.*s.%d%s", (int)(dot - basename), basename, id, dot);
|
2007-11-26 17:13:05 +01:00
|
|
|
|
else
|
|
|
|
|
return g_strdup_printf ("%s.%d", basename, id);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
2007-11-30 06:11:25 +01:00
|
|
|
|
path_has_prefix (const char *path,
|
|
|
|
|
const char *prefix)
|
2007-11-26 17:13:05 +01:00
|
|
|
|
{
|
|
|
|
|
int prefix_len;
|
|
|
|
|
|
|
|
|
|
if (prefix == NULL)
|
|
|
|
|
return TRUE;
|
|
|
|
|
|
|
|
|
|
prefix_len = strlen (prefix);
|
|
|
|
|
|
|
|
|
|
if (strncmp (path, prefix, prefix_len) == 0 &&
|
|
|
|
|
(prefix_len == 0 || /* empty prefix always matches */
|
|
|
|
|
prefix[prefix_len - 1] == '/' || /* last char in prefix was a /, so it must be in path too */
|
|
|
|
|
path[prefix_len] == 0 ||
|
|
|
|
|
path[prefix_len] == '/'))
|
|
|
|
|
return TRUE;
|
|
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static char *
|
2007-11-30 06:11:25 +01:00
|
|
|
|
try_make_relative (const char *path,
|
|
|
|
|
const char *base)
|
2007-11-26 17:13:05 +01:00
|
|
|
|
{
|
|
|
|
|
char *path2, *base2;
|
|
|
|
|
char *relative;
|
|
|
|
|
|
|
|
|
|
path2 = expand_all_symlinks (path);
|
|
|
|
|
base2 = expand_all_symlinks (base);
|
|
|
|
|
|
|
|
|
|
relative = NULL;
|
2018-09-21 17:02:05 +02:00
|
|
|
|
if (path2 != NULL && base2 != NULL && path_has_prefix (path2, base2))
|
2007-11-26 17:13:05 +01:00
|
|
|
|
{
|
|
|
|
|
relative = path2 + strlen (base2);
|
|
|
|
|
while (*relative == '/')
|
|
|
|
|
relative ++;
|
|
|
|
|
relative = g_strdup (relative);
|
|
|
|
|
}
|
|
|
|
|
g_free (path2);
|
|
|
|
|
g_free (base2);
|
|
|
|
|
|
|
|
|
|
if (relative)
|
|
|
|
|
return relative;
|
|
|
|
|
|
|
|
|
|
/* Failed, use abs path */
|
|
|
|
|
return g_strdup (path);
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-23 08:36:26 +02:00
|
|
|
|
static gboolean
|
|
|
|
|
ignore_trash_mount (GUnixMountEntry *mount)
|
|
|
|
|
{
|
|
|
|
|
GUnixMountPoint *mount_point = NULL;
|
|
|
|
|
const gchar *mount_options;
|
|
|
|
|
gboolean retval = TRUE;
|
|
|
|
|
|
|
|
|
|
if (g_unix_mount_is_system_internal (mount))
|
|
|
|
|
return TRUE;
|
|
|
|
|
|
|
|
|
|
mount_options = g_unix_mount_get_options (mount);
|
|
|
|
|
if (mount_options == NULL)
|
|
|
|
|
{
|
|
|
|
|
mount_point = g_unix_mount_point_at (g_unix_mount_get_mount_path (mount),
|
|
|
|
|
NULL);
|
|
|
|
|
if (mount_point != NULL)
|
|
|
|
|
mount_options = g_unix_mount_point_get_options (mount_point);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (mount_options == NULL ||
|
|
|
|
|
strstr (mount_options, "x-gvfs-notrash") == NULL)
|
|
|
|
|
retval = FALSE;
|
|
|
|
|
|
|
|
|
|
g_clear_pointer (&mount_point, g_unix_mount_point_free);
|
|
|
|
|
|
|
|
|
|
return retval;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
|
ignore_trash_path (const gchar *topdir)
|
|
|
|
|
{
|
|
|
|
|
GUnixMountEntry *mount;
|
|
|
|
|
gboolean retval = TRUE;
|
|
|
|
|
|
|
|
|
|
mount = g_unix_mount_at (topdir, NULL);
|
|
|
|
|
if (mount == NULL)
|
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
|
|
retval = ignore_trash_mount (mount);
|
|
|
|
|
|
|
|
|
|
out:
|
|
|
|
|
g_clear_pointer (&mount, g_unix_mount_free);
|
|
|
|
|
|
|
|
|
|
return retval;
|
|
|
|
|
}
|
|
|
|
|
|
2008-03-11 15:48:28 +01:00
|
|
|
|
gboolean
|
|
|
|
|
_g_local_file_has_trash_dir (const char *dirname, dev_t dir_dev)
|
|
|
|
|
{
|
2008-05-17 08:01:43 +02:00
|
|
|
|
static gsize home_dev_set = 0;
|
|
|
|
|
static dev_t home_dev;
|
2020-12-03 15:17:55 +01:00
|
|
|
|
static gboolean home_dev_valid = FALSE;
|
2008-03-11 15:48:28 +01:00
|
|
|
|
char *topdir, *globaldir, *trashdir, *tmpname;
|
|
|
|
|
uid_t uid;
|
|
|
|
|
char uid_str[32];
|
2010-03-21 19:04:18 +01:00
|
|
|
|
GStatBuf global_stat, trash_stat;
|
2008-03-11 15:48:28 +01:00
|
|
|
|
gboolean res;
|
2009-09-09 06:18:23 +02:00
|
|
|
|
|
2008-05-17 08:01:43 +02:00
|
|
|
|
if (g_once_init_enter (&home_dev_set))
|
2008-03-11 15:48:28 +01:00
|
|
|
|
{
|
2010-03-21 19:04:18 +01:00
|
|
|
|
GStatBuf home_stat;
|
2009-09-09 06:18:23 +02:00
|
|
|
|
|
2020-12-03 15:17:55 +01:00
|
|
|
|
if (g_stat (g_get_home_dir (), &home_stat) == 0)
|
|
|
|
|
{
|
|
|
|
|
home_dev = home_stat.st_dev;
|
|
|
|
|
home_dev_valid = TRUE;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
home_dev_valid = FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
2008-05-17 08:01:43 +02:00
|
|
|
|
g_once_init_leave (&home_dev_set, 1);
|
2008-03-11 15:48:28 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Assume we can trash to the home */
|
2020-12-03 15:17:55 +01:00
|
|
|
|
if (!home_dev_valid)
|
|
|
|
|
return FALSE;
|
|
|
|
|
else if (dir_dev == home_dev)
|
2008-03-11 15:48:28 +01:00
|
|
|
|
return TRUE;
|
|
|
|
|
|
2018-09-13 17:33:59 +02:00
|
|
|
|
topdir = find_mountpoint_for (dirname, dir_dev, TRUE);
|
2008-03-11 15:48:28 +01:00
|
|
|
|
if (topdir == NULL)
|
|
|
|
|
return FALSE;
|
|
|
|
|
|
2020-06-23 08:36:26 +02:00
|
|
|
|
if (ignore_trash_path (topdir))
|
2018-06-05 09:04:20 +02:00
|
|
|
|
{
|
2018-06-27 10:53:26 +02:00
|
|
|
|
g_free (topdir);
|
2018-06-05 09:04:20 +02:00
|
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
2009-09-09 06:18:23 +02:00
|
|
|
|
globaldir = g_build_filename (topdir, ".Trash", NULL);
|
|
|
|
|
if (g_lstat (globaldir, &global_stat) == 0 &&
|
2008-03-11 15:48:28 +01:00
|
|
|
|
S_ISDIR (global_stat.st_mode) &&
|
|
|
|
|
(global_stat.st_mode & S_ISVTX) != 0)
|
|
|
|
|
{
|
|
|
|
|
/* got a toplevel sysadmin created dir, assume we
|
|
|
|
|
* can trash to it (we should be able to create a dir)
|
|
|
|
|
* This fails for the FAT case where the ownership of
|
|
|
|
|
* that dir would be wrong though..
|
|
|
|
|
*/
|
|
|
|
|
g_free (globaldir);
|
|
|
|
|
g_free (topdir);
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
g_free (globaldir);
|
|
|
|
|
|
|
|
|
|
/* No global trash dir, or it failed the tests, fall back to $topdir/.Trash-$uid */
|
|
|
|
|
uid = geteuid ();
|
|
|
|
|
g_snprintf (uid_str, sizeof (uid_str), "%lu", (unsigned long) uid);
|
2009-09-09 06:18:23 +02:00
|
|
|
|
|
2008-03-11 15:48:28 +01:00
|
|
|
|
tmpname = g_strdup_printf (".Trash-%s", uid_str);
|
|
|
|
|
trashdir = g_build_filename (topdir, tmpname, NULL);
|
|
|
|
|
g_free (tmpname);
|
|
|
|
|
|
|
|
|
|
if (g_lstat (trashdir, &trash_stat) == 0)
|
|
|
|
|
{
|
|
|
|
|
g_free (topdir);
|
|
|
|
|
g_free (trashdir);
|
2009-09-09 06:18:23 +02:00
|
|
|
|
return S_ISDIR (trash_stat.st_mode) &&
|
|
|
|
|
trash_stat.st_uid == uid;
|
2008-03-11 15:48:28 +01:00
|
|
|
|
}
|
|
|
|
|
g_free (trashdir);
|
|
|
|
|
|
|
|
|
|
/* User specific trash didn't exist, can we create it? */
|
|
|
|
|
res = g_access (topdir, W_OK) == 0;
|
|
|
|
|
g_free (topdir);
|
2009-09-09 06:18:23 +02:00
|
|
|
|
|
2008-03-11 15:48:28 +01:00
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
|
2012-12-06 22:21:58 +01:00
|
|
|
|
#ifdef G_OS_UNIX
|
|
|
|
|
gboolean
|
|
|
|
|
_g_local_file_is_lost_found_dir (const char *path, dev_t path_dev)
|
|
|
|
|
{
|
|
|
|
|
gboolean ret = FALSE;
|
|
|
|
|
gchar *mount_dir = NULL;
|
|
|
|
|
size_t mount_dir_len;
|
|
|
|
|
GStatBuf statbuf;
|
|
|
|
|
|
|
|
|
|
if (!g_str_has_suffix (path, "/lost+found"))
|
|
|
|
|
goto out;
|
|
|
|
|
|
2018-09-13 17:33:59 +02:00
|
|
|
|
mount_dir = find_mountpoint_for (path, path_dev, FALSE);
|
2012-12-06 22:21:58 +01:00
|
|
|
|
if (mount_dir == NULL)
|
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
|
|
mount_dir_len = strlen (mount_dir);
|
|
|
|
|
/* We special-case rootfs ('/') since it's the only case where
|
|
|
|
|
* mount_dir ends in '/'
|
|
|
|
|
*/
|
|
|
|
|
if (mount_dir_len == 1)
|
|
|
|
|
mount_dir_len--;
|
|
|
|
|
if (mount_dir_len + strlen ("/lost+found") != strlen (path))
|
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
|
|
if (g_lstat (path, &statbuf) != 0)
|
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
|
|
if (!(S_ISDIR (statbuf.st_mode) &&
|
|
|
|
|
statbuf.st_uid == 0 &&
|
|
|
|
|
statbuf.st_gid == 0))
|
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
|
|
ret = TRUE;
|
|
|
|
|
|
|
|
|
|
out:
|
|
|
|
|
g_free (mount_dir);
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
2008-03-11 15:48:28 +01:00
|
|
|
|
|
2007-11-26 17:13:05 +01:00
|
|
|
|
static gboolean
|
2007-11-30 06:11:25 +01:00
|
|
|
|
g_local_file_trash (GFile *file,
|
|
|
|
|
GCancellable *cancellable,
|
|
|
|
|
GError **error)
|
2007-11-26 17:13:05 +01:00
|
|
|
|
{
|
|
|
|
|
GLocalFile *local = G_LOCAL_FILE (file);
|
2010-03-21 19:04:18 +01:00
|
|
|
|
GStatBuf file_stat, home_stat;
|
2007-11-26 17:13:05 +01:00
|
|
|
|
const char *homedir;
|
2007-12-10 16:24:53 +01:00
|
|
|
|
char *trashdir, *topdir, *infodir, *filesdir;
|
2007-11-26 17:13:05 +01:00
|
|
|
|
char *basename, *trashname, *trashfile, *infoname, *infofile;
|
|
|
|
|
char *original_name, *original_name_escaped;
|
|
|
|
|
int i;
|
|
|
|
|
char *data;
|
2018-07-16 17:31:10 +02:00
|
|
|
|
char *path;
|
2007-11-26 17:13:05 +01:00
|
|
|
|
gboolean is_homedir_trash;
|
2018-07-06 14:49:55 +02:00
|
|
|
|
char *delete_time = NULL;
|
2007-11-26 17:13:05 +01:00
|
|
|
|
int fd;
|
2010-03-21 19:04:18 +01:00
|
|
|
|
GStatBuf trash_stat, global_stat;
|
2007-12-10 16:24:53 +01:00
|
|
|
|
char *dirname, *globaldir;
|
2010-02-22 12:37:16 +01:00
|
|
|
|
GVfsClass *class;
|
|
|
|
|
GVfs *vfs;
|
2017-07-31 12:30:55 +02:00
|
|
|
|
int errsv;
|
2010-02-22 12:37:16 +01:00
|
|
|
|
|
2018-08-23 06:24:42 +02:00
|
|
|
|
if (glib_should_use_portal ())
|
|
|
|
|
return g_trash_portal_trash_file (file, error);
|
|
|
|
|
|
2007-11-26 17:13:05 +01:00
|
|
|
|
if (g_lstat (local->filename, &file_stat) != 0)
|
|
|
|
|
{
|
2017-07-31 12:30:55 +02: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
|
|
|
|
|
2015-08-26 17:06:31 +02:00
|
|
|
|
g_set_io_error (error,
|
|
|
|
|
_("Error trashing file %s: %s"),
|
|
|
|
|
file, errsv);
|
2007-11-26 17:13:05 +01:00
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
homedir = g_get_home_dir ();
|
2020-12-03 15:17:55 +01:00
|
|
|
|
if (g_stat (homedir, &home_stat) != 0)
|
|
|
|
|
{
|
|
|
|
|
errsv = errno;
|
|
|
|
|
|
|
|
|
|
g_set_io_error (error,
|
|
|
|
|
_("Error trashing file %s: %s"),
|
|
|
|
|
file, errsv);
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
2007-11-26 17:13:05 +01:00
|
|
|
|
|
|
|
|
|
is_homedir_trash = FALSE;
|
|
|
|
|
trashdir = NULL;
|
2018-07-16 17:31:10 +02:00
|
|
|
|
|
|
|
|
|
/* On overlayfs, a file's st_dev will be different to the home directory's.
|
|
|
|
|
* We still want to create our trash directory under the home directory, so
|
|
|
|
|
* instead we should stat the directory that the file we're deleting is in as
|
|
|
|
|
* this will have the same st_dev.
|
|
|
|
|
*/
|
|
|
|
|
if (!S_ISDIR (file_stat.st_mode))
|
|
|
|
|
{
|
|
|
|
|
path = g_path_get_dirname (local->filename);
|
|
|
|
|
/* If the parent is a symlink to a different device then it might have
|
|
|
|
|
* st_dev equal to the home directory's, in which case we will end up
|
|
|
|
|
* trying to rename across a filesystem boundary, which doesn't work. So
|
|
|
|
|
* we use g_stat here instead of g_lstat, to know where the symlink
|
|
|
|
|
* points to. */
|
|
|
|
|
g_stat (path, &file_stat);
|
|
|
|
|
g_free (path);
|
|
|
|
|
}
|
|
|
|
|
|
2007-11-26 17:13:05 +01:00
|
|
|
|
if (file_stat.st_dev == home_stat.st_dev)
|
|
|
|
|
{
|
|
|
|
|
is_homedir_trash = TRUE;
|
|
|
|
|
errno = 0;
|
|
|
|
|
trashdir = g_build_filename (g_get_user_data_dir (), "Trash", NULL);
|
|
|
|
|
if (g_mkdir_with_parents (trashdir, 0700) < 0)
|
|
|
|
|
{
|
|
|
|
|
char *display_name;
|
2017-07-31 12:30:55 +02:00
|
|
|
|
errsv = errno;
|
2007-11-26 17:13:05 +01:00
|
|
|
|
|
|
|
|
|
display_name = g_filename_display_name (trashdir);
|
|
|
|
|
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),
|
2020-12-22 19:22:37 +01:00
|
|
|
|
_("Unable to create trash directory %s: %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
|
|
|
|
display_name, g_strerror (errsv));
|
2007-11-26 17:13:05 +01:00
|
|
|
|
g_free (display_name);
|
|
|
|
|
g_free (trashdir);
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
topdir = g_strdup (g_get_user_data_dir ());
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2007-12-08 13:01:06 +01:00
|
|
|
|
uid_t uid;
|
|
|
|
|
char uid_str[32];
|
2021-01-06 17:36:32 +01:00
|
|
|
|
gboolean success = FALSE;
|
2007-12-08 13:01:06 +01:00
|
|
|
|
|
2007-11-26 17:13:05 +01:00
|
|
|
|
uid = geteuid ();
|
|
|
|
|
g_snprintf (uid_str, sizeof (uid_str), "%lu", (unsigned long)uid);
|
2016-09-29 10:45:50 +02:00
|
|
|
|
|
2018-08-13 18:23:10 +02:00
|
|
|
|
topdir = _g_local_file_find_topdir_for (local->filename);
|
2007-11-26 17:13:05 +01:00
|
|
|
|
if (topdir == NULL)
|
|
|
|
|
{
|
2015-08-26 17:06:31 +02:00
|
|
|
|
g_set_io_error (error,
|
|
|
|
|
_("Unable to find toplevel directory to trash %s"),
|
2018-06-08 13:40:04 +02:00
|
|
|
|
file, ENOTSUP);
|
2007-11-26 17:13:05 +01:00
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
2018-06-01 13:41:53 +02:00
|
|
|
|
|
2020-06-23 08:36:26 +02:00
|
|
|
|
if (ignore_trash_path (topdir))
|
2018-06-01 13:41:53 +02:00
|
|
|
|
{
|
|
|
|
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
|
|
|
|
|
_("Trashing on system internal mounts is not supported"));
|
|
|
|
|
g_free (topdir);
|
|
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
2007-11-26 17:13:05 +01:00
|
|
|
|
/* Try looking for global trash dir $topdir/.Trash/$uid */
|
|
|
|
|
globaldir = g_build_filename (topdir, ".Trash", NULL);
|
|
|
|
|
if (g_lstat (globaldir, &global_stat) == 0 &&
|
|
|
|
|
S_ISDIR (global_stat.st_mode) &&
|
|
|
|
|
(global_stat.st_mode & S_ISVTX) != 0)
|
|
|
|
|
{
|
|
|
|
|
trashdir = g_build_filename (globaldir, uid_str, NULL);
|
2021-07-12 00:16:20 +02:00
|
|
|
|
success = TRUE;
|
2007-11-26 17:13:05 +01:00
|
|
|
|
|
|
|
|
|
if (g_lstat (trashdir, &trash_stat) == 0)
|
|
|
|
|
{
|
|
|
|
|
if (!S_ISDIR (trash_stat.st_mode) ||
|
|
|
|
|
trash_stat.st_uid != uid)
|
|
|
|
|
{
|
|
|
|
|
/* Not a directory or not owned by user, ignore */
|
|
|
|
|
g_free (trashdir);
|
|
|
|
|
trashdir = NULL;
|
2021-07-12 00:16:20 +02:00
|
|
|
|
success = FALSE;
|
2007-11-26 17:13:05 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (g_mkdir (trashdir, 0700) == -1)
|
|
|
|
|
{
|
|
|
|
|
g_free (trashdir);
|
|
|
|
|
trashdir = NULL;
|
2021-07-12 00:16:20 +02:00
|
|
|
|
success = FALSE;
|
2007-11-26 17:13:05 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
g_free (globaldir);
|
|
|
|
|
|
|
|
|
|
if (trashdir == NULL)
|
|
|
|
|
{
|
2008-02-06 11:06:54 +01:00
|
|
|
|
gboolean tried_create;
|
|
|
|
|
|
2007-11-26 17:13:05 +01:00
|
|
|
|
/* No global trash dir, or it failed the tests, fall back to $topdir/.Trash-$uid */
|
|
|
|
|
dirname = g_strdup_printf (".Trash-%s", uid_str);
|
|
|
|
|
trashdir = g_build_filename (topdir, dirname, NULL);
|
2020-12-22 19:22:50 +01:00
|
|
|
|
success = TRUE;
|
2007-11-26 17:13:05 +01:00
|
|
|
|
g_free (dirname);
|
2008-02-06 11:06:54 +01:00
|
|
|
|
|
|
|
|
|
tried_create = FALSE;
|
|
|
|
|
|
|
|
|
|
retry:
|
2007-11-26 17:13:05 +01:00
|
|
|
|
if (g_lstat (trashdir, &trash_stat) == 0)
|
|
|
|
|
{
|
|
|
|
|
if (!S_ISDIR (trash_stat.st_mode) ||
|
|
|
|
|
trash_stat.st_uid != uid)
|
|
|
|
|
{
|
2008-02-06 11:06:54 +01:00
|
|
|
|
/* Remove the failed directory */
|
|
|
|
|
if (tried_create)
|
|
|
|
|
g_remove (trashdir);
|
|
|
|
|
|
2007-11-26 17:13:05 +01:00
|
|
|
|
/* Not a directory or not owned by user, ignore */
|
2020-12-22 19:22:50 +01:00
|
|
|
|
success = FALSE;
|
2007-11-26 17:13:05 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
2008-02-06 11:06:54 +01:00
|
|
|
|
else
|
2007-11-26 17:13:05 +01:00
|
|
|
|
{
|
2008-02-06 11:06:54 +01:00
|
|
|
|
if (!tried_create &&
|
|
|
|
|
g_mkdir (trashdir, 0700) != -1)
|
|
|
|
|
{
|
|
|
|
|
/* Ensure that the created dir has the right uid etc.
|
|
|
|
|
This might fail on e.g. a FAT dir */
|
|
|
|
|
tried_create = TRUE;
|
|
|
|
|
goto retry;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2020-12-22 19:22:50 +01:00
|
|
|
|
success = FALSE;
|
2008-02-06 11:06:54 +01:00
|
|
|
|
}
|
2007-11-26 17:13:05 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-12-22 19:22:50 +01:00
|
|
|
|
if (!success)
|
2007-11-26 17:13:05 +01:00
|
|
|
|
{
|
2020-12-22 19:22:50 +01:00
|
|
|
|
gchar *trashdir_display_name = NULL, *file_display_name = NULL;
|
|
|
|
|
|
|
|
|
|
trashdir_display_name = g_filename_display_name (trashdir);
|
|
|
|
|
file_display_name = g_filename_display_name (local->filename);
|
|
|
|
|
g_set_error (error, G_IO_ERROR,
|
|
|
|
|
G_IO_ERROR_NOT_SUPPORTED,
|
|
|
|
|
_("Unable to find or create trash directory %s to trash %s"),
|
|
|
|
|
trashdir_display_name, file_display_name);
|
|
|
|
|
|
|
|
|
|
g_free (trashdir_display_name);
|
|
|
|
|
g_free (file_display_name);
|
|
|
|
|
|
|
|
|
|
g_free (topdir);
|
|
|
|
|
g_free (trashdir);
|
|
|
|
|
|
2007-11-26 17:13:05 +01:00
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Trashdir points to the trash dir with the "info" and "files" subdirectories */
|
|
|
|
|
|
|
|
|
|
infodir = g_build_filename (trashdir, "info", NULL);
|
|
|
|
|
filesdir = g_build_filename (trashdir, "files", NULL);
|
|
|
|
|
|
|
|
|
|
/* Make sure we have the subdirectories */
|
|
|
|
|
if ((g_mkdir (infodir, 0700) == -1 && errno != EEXIST) ||
|
|
|
|
|
(g_mkdir (filesdir, 0700) == -1 && errno != EEXIST))
|
|
|
|
|
{
|
2020-12-22 19:22:50 +01:00
|
|
|
|
gchar *trashdir_display_name = NULL, *file_display_name = NULL;
|
|
|
|
|
|
|
|
|
|
trashdir_display_name = g_filename_display_name (trashdir);
|
|
|
|
|
file_display_name = g_filename_display_name (local->filename);
|
|
|
|
|
g_set_error (error, G_IO_ERROR,
|
|
|
|
|
G_IO_ERROR_NOT_SUPPORTED,
|
|
|
|
|
_("Unable to find or create trash directory %s to trash %s"),
|
|
|
|
|
trashdir_display_name, file_display_name);
|
|
|
|
|
|
|
|
|
|
g_free (trashdir_display_name);
|
|
|
|
|
g_free (file_display_name);
|
|
|
|
|
|
2007-11-26 17:13:05 +01:00
|
|
|
|
g_free (topdir);
|
2020-12-22 19:22:50 +01:00
|
|
|
|
g_free (trashdir);
|
2007-11-26 17:13:05 +01:00
|
|
|
|
g_free (infodir);
|
|
|
|
|
g_free (filesdir);
|
2020-12-22 19:22:50 +01:00
|
|
|
|
|
2007-11-26 17:13:05 +01:00
|
|
|
|
return FALSE;
|
2015-08-26 17:06:31 +02:00
|
|
|
|
}
|
2007-11-26 17:13:05 +01:00
|
|
|
|
|
2020-12-22 19:22:50 +01:00
|
|
|
|
g_free (trashdir);
|
|
|
|
|
|
2007-11-26 17:13:05 +01:00
|
|
|
|
basename = g_path_get_basename (local->filename);
|
|
|
|
|
i = 1;
|
|
|
|
|
trashname = NULL;
|
|
|
|
|
infofile = NULL;
|
|
|
|
|
do {
|
|
|
|
|
g_free (trashname);
|
|
|
|
|
g_free (infofile);
|
|
|
|
|
|
|
|
|
|
trashname = get_unique_filename (basename, i++);
|
|
|
|
|
infoname = g_strconcat (trashname, ".trashinfo", NULL);
|
|
|
|
|
infofile = g_build_filename (infodir, infoname, NULL);
|
|
|
|
|
g_free (infoname);
|
|
|
|
|
|
2012-08-28 00:30:06 +02:00
|
|
|
|
fd = g_open (infofile, O_CREAT | O_EXCL, 0666);
|
2017-07-31 12:30:55 +02:00
|
|
|
|
errsv = errno;
|
|
|
|
|
} while (fd == -1 && errsv == EEXIST);
|
2007-11-26 17:13:05 +01:00
|
|
|
|
|
|
|
|
|
g_free (basename);
|
|
|
|
|
g_free (infodir);
|
|
|
|
|
|
|
|
|
|
if (fd == -1)
|
|
|
|
|
{
|
2017-07-31 12:30:55 +02: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_free (filesdir);
|
|
|
|
|
g_free (topdir);
|
|
|
|
|
g_free (trashname);
|
|
|
|
|
g_free (infofile);
|
2015-08-26 17:06:31 +02:00
|
|
|
|
|
|
|
|
|
g_set_io_error (error,
|
|
|
|
|
_("Unable to create trashing info file for %s: %s"),
|
|
|
|
|
file, errsv);
|
2007-11-26 17:13:05 +01:00
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
2013-01-25 18:05:26 +01:00
|
|
|
|
(void) g_close (fd, NULL);
|
2007-11-26 17:13:05 +01:00
|
|
|
|
|
2015-09-29 16:16:52 +02:00
|
|
|
|
/* Write the full content of the info file before trashing to make
|
|
|
|
|
* sure someone doesn't read an empty file. See #749314
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/* Use absolute names for homedir */
|
|
|
|
|
if (is_homedir_trash)
|
|
|
|
|
original_name = g_strdup (local->filename);
|
|
|
|
|
else
|
|
|
|
|
original_name = try_make_relative (local->filename, topdir);
|
|
|
|
|
original_name_escaped = g_uri_escape_string (original_name, "/", FALSE);
|
|
|
|
|
|
|
|
|
|
g_free (original_name);
|
|
|
|
|
g_free (topdir);
|
|
|
|
|
|
|
|
|
|
{
|
2018-07-06 14:49:55 +02:00
|
|
|
|
GDateTime *now = g_date_time_new_now_local ();
|
|
|
|
|
if (now != NULL)
|
|
|
|
|
delete_time = g_date_time_format (now, "%Y-%m-%dT%H:%M:%S");
|
|
|
|
|
else
|
|
|
|
|
delete_time = g_strdup ("9999-12-31T23:59:59");
|
|
|
|
|
g_date_time_unref (now);
|
2015-09-29 16:16:52 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
data = g_strdup_printf ("[Trash Info]\nPath=%s\nDeletionDate=%s\n",
|
|
|
|
|
original_name_escaped, delete_time);
|
2018-07-06 14:49:55 +02:00
|
|
|
|
g_free (delete_time);
|
2015-09-29 16:16:52 +02:00
|
|
|
|
|
2020-05-27 19:00:48 +02:00
|
|
|
|
g_file_set_contents_full (infofile, data, -1,
|
|
|
|
|
G_FILE_SET_CONTENTS_CONSISTENT | G_FILE_SET_CONTENTS_ONLY_EXISTING,
|
|
|
|
|
0600, NULL);
|
2015-09-29 16:16:52 +02:00
|
|
|
|
|
2007-11-26 17:13:05 +01:00
|
|
|
|
/* TODO: Maybe we should verify that you can delete the file from the trash
|
2015-08-26 17:06:31 +02:00
|
|
|
|
* before moving it? OTOH, that is hard, as it needs a recursive scan
|
|
|
|
|
*/
|
2007-11-26 17:13:05 +01:00
|
|
|
|
|
|
|
|
|
trashfile = g_build_filename (filesdir, trashname, NULL);
|
|
|
|
|
|
|
|
|
|
g_free (filesdir);
|
|
|
|
|
|
|
|
|
|
if (g_rename (local->filename, trashfile) == -1)
|
|
|
|
|
{
|
2017-07-31 12:30:55 +02: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
|
|
|
|
|
2013-10-30 19:34:14 +01:00
|
|
|
|
g_unlink (infofile);
|
|
|
|
|
|
2007-11-26 17:13:05 +01:00
|
|
|
|
g_free (trashname);
|
|
|
|
|
g_free (infofile);
|
|
|
|
|
g_free (trashfile);
|
2009-04-02 19:01:56 +02:00
|
|
|
|
|
|
|
|
|
if (errsv == EXDEV)
|
|
|
|
|
/* The trash dir was actually on another fs anyway!?
|
2015-08-26 17:06:31 +02:00
|
|
|
|
* This can happen when the same device is mounted multiple
|
|
|
|
|
* times, or with bind mounts of the same fs.
|
|
|
|
|
*/
|
|
|
|
|
g_set_io_error (error,
|
|
|
|
|
_("Unable to trash file %s across filesystem boundaries"),
|
|
|
|
|
file, ENOTSUP);
|
2009-04-02 19:01:56 +02:00
|
|
|
|
else
|
2015-08-26 17:06:31 +02:00
|
|
|
|
g_set_io_error (error,
|
|
|
|
|
_("Unable to trash file %s: %s"),
|
|
|
|
|
file, errsv);
|
2007-11-26 17:13:05 +01:00
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
2010-02-22 12:37:16 +01:00
|
|
|
|
vfs = g_vfs_get_default ();
|
|
|
|
|
class = G_VFS_GET_CLASS (vfs);
|
|
|
|
|
if (class->local_file_moved)
|
|
|
|
|
class->local_file_moved (vfs, local->filename, trashfile);
|
|
|
|
|
|
2007-11-26 17:13:05 +01:00
|
|
|
|
g_free (trashfile);
|
|
|
|
|
|
|
|
|
|
/* TODO: Do we need to update mtime/atime here after the move? */
|
|
|
|
|
|
|
|
|
|
g_free (infofile);
|
|
|
|
|
g_free (data);
|
|
|
|
|
|
|
|
|
|
g_free (original_name_escaped);
|
|
|
|
|
g_free (trashname);
|
|
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
2008-02-25 09:15:35 +01:00
|
|
|
|
#else /* G_OS_WIN32 */
|
2008-03-12 14:16:54 +01:00
|
|
|
|
gboolean
|
|
|
|
|
_g_local_file_has_trash_dir (const char *dirname, dev_t dir_dev)
|
|
|
|
|
{
|
|
|
|
|
return FALSE; /* XXX ??? */
|
|
|
|
|
}
|
|
|
|
|
|
2008-02-25 09:15:35 +01:00
|
|
|
|
static gboolean
|
|
|
|
|
g_local_file_trash (GFile *file,
|
|
|
|
|
GCancellable *cancellable,
|
|
|
|
|
GError **error)
|
|
|
|
|
{
|
|
|
|
|
GLocalFile *local = G_LOCAL_FILE (file);
|
|
|
|
|
SHFILEOPSTRUCTW op = {0};
|
|
|
|
|
gboolean success;
|
|
|
|
|
wchar_t *wfilename;
|
|
|
|
|
long len;
|
|
|
|
|
|
|
|
|
|
wfilename = g_utf8_to_utf16 (local->filename, -1, NULL, &len, NULL);
|
|
|
|
|
/* SHFILEOPSTRUCT.pFrom is double-zero-terminated */
|
|
|
|
|
wfilename = g_renew (wchar_t, wfilename, len + 2);
|
|
|
|
|
wfilename[len + 1] = 0;
|
|
|
|
|
|
|
|
|
|
op.wFunc = FO_DELETE;
|
|
|
|
|
op.pFrom = wfilename;
|
|
|
|
|
op.fFlags = FOF_ALLOWUNDO;
|
|
|
|
|
|
|
|
|
|
success = SHFileOperationW (&op) == 0;
|
|
|
|
|
|
|
|
|
|
if (success && op.fAnyOperationsAborted)
|
|
|
|
|
{
|
|
|
|
|
if (cancellable && !g_cancellable_is_cancelled (cancellable))
|
|
|
|
|
g_cancellable_cancel (cancellable);
|
2015-08-26 17:06:31 +02:00
|
|
|
|
g_set_io_error (error,
|
|
|
|
|
_("Unable to trash file %s: %s"),
|
|
|
|
|
file, ECANCELED);
|
2008-02-25 09:15:35 +01:00
|
|
|
|
success = FALSE;
|
|
|
|
|
}
|
|
|
|
|
else if (!success)
|
2015-08-26 17:06:31 +02:00
|
|
|
|
g_set_io_error (error,
|
|
|
|
|
_("Unable to trash file %s"),
|
|
|
|
|
file, 0);
|
2008-02-25 09:15:35 +01:00
|
|
|
|
|
|
|
|
|
g_free (wfilename);
|
|
|
|
|
return success;
|
|
|
|
|
}
|
|
|
|
|
#endif /* G_OS_WIN32 */
|
2007-11-26 17:13:05 +01:00
|
|
|
|
|
|
|
|
|
static gboolean
|
2007-11-30 06:11:25 +01:00
|
|
|
|
g_local_file_make_directory (GFile *file,
|
|
|
|
|
GCancellable *cancellable,
|
|
|
|
|
GError **error)
|
2007-11-26 17:13:05 +01:00
|
|
|
|
{
|
|
|
|
|
GLocalFile *local = G_LOCAL_FILE (file);
|
|
|
|
|
|
2008-08-08 11:26:51 +02:00
|
|
|
|
if (g_mkdir (local->filename, 0777) == -1)
|
2007-11-26 17:13:05 +01:00
|
|
|
|
{
|
|
|
|
|
int errsv = errno;
|
|
|
|
|
|
|
|
|
|
if (errsv == EINVAL)
|
|
|
|
|
/* This must be an invalid filename, on e.g. FAT */
|
2008-06-16 18:53:58 +02:00
|
|
|
|
g_set_error_literal (error, G_IO_ERROR,
|
|
|
|
|
G_IO_ERROR_INVALID_FILENAME,
|
|
|
|
|
_("Invalid filename"));
|
2007-11-26 17:13:05 +01:00
|
|
|
|
else
|
2015-08-26 17:06:31 +02:00
|
|
|
|
g_set_io_error (error,
|
|
|
|
|
_("Error creating directory %s: %s"),
|
|
|
|
|
file, errsv);
|
2007-11-26 17:13:05 +01:00
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
2019-09-27 18:27:24 +02:00
|
|
|
|
#ifdef HAVE_SYMLINK
|
2007-11-26 17:13:05 +01:00
|
|
|
|
static gboolean
|
2007-11-30 06:11:25 +01:00
|
|
|
|
g_local_file_make_symbolic_link (GFile *file,
|
|
|
|
|
const char *symlink_value,
|
|
|
|
|
GCancellable *cancellable,
|
|
|
|
|
GError **error)
|
2007-11-26 17:13:05 +01:00
|
|
|
|
{
|
|
|
|
|
GLocalFile *local = G_LOCAL_FILE (file);
|
|
|
|
|
|
|
|
|
|
if (symlink (symlink_value, local->filename) == -1)
|
|
|
|
|
{
|
|
|
|
|
int errsv = errno;
|
|
|
|
|
|
|
|
|
|
if (errsv == EINVAL)
|
|
|
|
|
/* This must be an invalid filename, on e.g. FAT */
|
2008-06-16 18:53:58 +02:00
|
|
|
|
g_set_error_literal (error, G_IO_ERROR,
|
|
|
|
|
G_IO_ERROR_INVALID_FILENAME,
|
|
|
|
|
_("Invalid filename"));
|
2009-10-25 11:14:36 +01:00
|
|
|
|
else if (errsv == EPERM)
|
|
|
|
|
g_set_error (error, G_IO_ERROR,
|
|
|
|
|
G_IO_ERROR_NOT_SUPPORTED,
|
|
|
|
|
_("Filesystem does not support symbolic links"));
|
2007-11-26 17:13:05 +01:00
|
|
|
|
else
|
2015-08-26 17:06:31 +02:00
|
|
|
|
g_set_io_error (error,
|
|
|
|
|
_("Error making symbolic link %s: %s"),
|
|
|
|
|
file, errsv);
|
2007-11-26 17:13:05 +01:00
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
2019-09-27 18:27:24 +02:00
|
|
|
|
#endif
|
2007-11-26 17:13:05 +01:00
|
|
|
|
|
|
|
|
|
static gboolean
|
2007-11-30 06:11:25 +01:00
|
|
|
|
g_local_file_move (GFile *source,
|
|
|
|
|
GFile *destination,
|
|
|
|
|
GFileCopyFlags flags,
|
|
|
|
|
GCancellable *cancellable,
|
|
|
|
|
GFileProgressCallback progress_callback,
|
|
|
|
|
gpointer progress_callback_data,
|
|
|
|
|
GError **error)
|
2007-11-26 17:13:05 +01:00
|
|
|
|
{
|
2008-02-05 11:11:12 +01:00
|
|
|
|
GLocalFile *local_source, *local_destination;
|
2010-03-21 19:04:18 +01:00
|
|
|
|
GStatBuf statbuf;
|
2007-11-26 17:13:05 +01:00
|
|
|
|
gboolean destination_exist, source_is_dir;
|
|
|
|
|
char *backup_name;
|
|
|
|
|
int res;
|
2008-01-17 11:57:48 +01:00
|
|
|
|
off_t source_size;
|
2009-06-18 09:05:27 +02:00
|
|
|
|
GVfsClass *class;
|
|
|
|
|
GVfs *vfs;
|
|
|
|
|
|
2008-01-17 11:57:48 +01:00
|
|
|
|
if (!G_IS_LOCAL_FILE (source) ||
|
|
|
|
|
!G_IS_LOCAL_FILE (destination))
|
|
|
|
|
{
|
|
|
|
|
/* Fall back to default move */
|
2008-06-16 18:53:58 +02:00
|
|
|
|
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, "Move not supported");
|
2008-01-17 11:57:48 +01:00
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
local_source = G_LOCAL_FILE (source);
|
2008-02-05 11:11:12 +01:00
|
|
|
|
local_destination = G_LOCAL_FILE (destination);
|
2008-01-17 11:57:48 +01:00
|
|
|
|
|
2007-11-26 17:13:05 +01:00
|
|
|
|
res = g_lstat (local_source->filename, &statbuf);
|
|
|
|
|
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;
|
|
|
|
|
|
2015-08-26 17:06:31 +02:00
|
|
|
|
g_set_io_error (error,
|
|
|
|
|
_("Error moving file %s: %s"),
|
|
|
|
|
source, errsv);
|
2007-11-26 17:13:05 +01:00
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
2008-01-17 11:57:48 +01:00
|
|
|
|
|
2008-03-27 21:13:00 +01:00
|
|
|
|
source_is_dir = S_ISDIR (statbuf.st_mode);
|
2008-01-17 11:57:48 +01:00
|
|
|
|
source_size = statbuf.st_size;
|
2007-11-26 17:13:05 +01:00
|
|
|
|
|
|
|
|
|
destination_exist = FALSE;
|
|
|
|
|
res = g_lstat (local_destination->filename, &statbuf);
|
|
|
|
|
if (res == 0)
|
|
|
|
|
{
|
|
|
|
|
destination_exist = TRUE; /* Target file exists */
|
|
|
|
|
|
|
|
|
|
if (flags & G_FILE_COPY_OVERWRITE)
|
|
|
|
|
{
|
|
|
|
|
/* Always fail on dirs, even with overwrite */
|
|
|
|
|
if (S_ISDIR (statbuf.st_mode))
|
|
|
|
|
{
|
2008-03-27 21:13:00 +01:00
|
|
|
|
if (source_is_dir)
|
2008-06-16 18:53:58 +02:00
|
|
|
|
g_set_error_literal (error,
|
|
|
|
|
G_IO_ERROR,
|
|
|
|
|
G_IO_ERROR_WOULD_MERGE,
|
2016-09-30 05:47:15 +02:00
|
|
|
|
_("Can’t move directory over directory"));
|
2008-03-27 21:13:00 +01:00
|
|
|
|
else
|
2008-06-16 18:53:58 +02:00
|
|
|
|
g_set_error_literal (error,
|
|
|
|
|
G_IO_ERROR,
|
|
|
|
|
G_IO_ERROR_IS_DIRECTORY,
|
2016-09-30 05:47:15 +02:00
|
|
|
|
_("Can’t copy over directory"));
|
2007-11-26 17:13:05 +01:00
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2015-08-26 17:06:31 +02:00
|
|
|
|
g_set_io_error (error,
|
|
|
|
|
_("Error moving file %s: %s"),
|
|
|
|
|
source, EEXIST);
|
2007-11-26 17:13:05 +01:00
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (flags & G_FILE_COPY_BACKUP && destination_exist)
|
|
|
|
|
{
|
|
|
|
|
backup_name = g_strconcat (local_destination->filename, "~", NULL);
|
2008-02-18 16:35:16 +01:00
|
|
|
|
if (g_rename (local_destination->filename, backup_name) == -1)
|
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_name);
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
g_free (backup_name);
|
|
|
|
|
destination_exist = FALSE; /* It did, but no more */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (source_is_dir && destination_exist && (flags & G_FILE_COPY_OVERWRITE))
|
|
|
|
|
{
|
|
|
|
|
/* Source is a dir, destination exists (and is not a dir, because that would have failed
|
|
|
|
|
earlier), and we're overwriting. Manually remove the target so we can do the rename. */
|
2008-02-18 16:35:16 +01:00
|
|
|
|
res = g_unlink (local_destination->filename);
|
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;
|
|
|
|
|
|
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 removing target 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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2008-02-18 16:35:16 +01:00
|
|
|
|
if (g_rename (local_source->filename, local_destination->filename) == -1)
|
2007-11-26 17:13:05 +01:00
|
|
|
|
{
|
|
|
|
|
int errsv = errno;
|
|
|
|
|
|
2007-12-03 10:11:48 +01:00
|
|
|
|
if (errsv == EXDEV)
|
|
|
|
|
/* This will cause the fallback code to run */
|
2008-06-16 18:53:58 +02:00
|
|
|
|
g_set_error_literal (error, G_IO_ERROR,
|
|
|
|
|
G_IO_ERROR_NOT_SUPPORTED,
|
|
|
|
|
_("Move between mounts not supported"));
|
2007-12-03 10:11:48 +01:00
|
|
|
|
else if (errsv == EINVAL)
|
2007-11-26 17:13:05 +01:00
|
|
|
|
/* This must be an invalid filename, on e.g. FAT, or
|
|
|
|
|
we're trying to move the file into itself...
|
|
|
|
|
We return invalid filename for both... */
|
2008-06-16 18:53:58 +02:00
|
|
|
|
g_set_error_literal (error, G_IO_ERROR,
|
|
|
|
|
G_IO_ERROR_INVALID_FILENAME,
|
|
|
|
|
_("Invalid filename"));
|
2007-11-26 17:13:05 +01:00
|
|
|
|
else
|
2015-08-26 17:06:31 +02:00
|
|
|
|
g_set_io_error (error,
|
|
|
|
|
_("Error moving file %s: %s"),
|
|
|
|
|
source, errsv);
|
2007-11-26 17:13:05 +01:00
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
2008-01-17 11:57:48 +01:00
|
|
|
|
|
2009-06-18 09:05:27 +02:00
|
|
|
|
vfs = g_vfs_get_default ();
|
|
|
|
|
class = G_VFS_GET_CLASS (vfs);
|
|
|
|
|
if (class->local_file_moved)
|
|
|
|
|
class->local_file_moved (vfs, local_source->filename, local_destination->filename);
|
|
|
|
|
|
2008-01-17 11:57:48 +01:00
|
|
|
|
/* Make sure we send full copied size */
|
|
|
|
|
if (progress_callback)
|
|
|
|
|
progress_callback (source_size, source_size, progress_callback_data);
|
|
|
|
|
|
2007-11-26 17:13:05 +01:00
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
2013-02-02 06:19:15 +01:00
|
|
|
|
#ifdef G_OS_WIN32
|
|
|
|
|
|
2015-01-12 20:59:35 +01:00
|
|
|
|
gboolean
|
2020-06-15 17:44:26 +02:00
|
|
|
|
g_local_file_is_nfs_home (const gchar *filename)
|
2013-02-02 06:19:15 +01:00
|
|
|
|
{
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#else
|
|
|
|
|
|
2013-01-19 00:44:11 +01:00
|
|
|
|
static gboolean
|
2020-06-15 17:24:37 +02:00
|
|
|
|
is_remote_fs_type (const gchar *fsname)
|
2013-01-19 00:44:11 +01:00
|
|
|
|
{
|
|
|
|
|
if (fsname != NULL)
|
|
|
|
|
{
|
|
|
|
|
if (strcmp (fsname, "nfs") == 0)
|
|
|
|
|
return TRUE;
|
|
|
|
|
if (strcmp (fsname, "nfs4") == 0)
|
|
|
|
|
return TRUE;
|
2020-06-15 17:43:49 +02:00
|
|
|
|
if (strcmp (fsname, "cifs") == 0)
|
|
|
|
|
return TRUE;
|
|
|
|
|
if (strcmp (fsname, "smb") == 0)
|
|
|
|
|
return TRUE;
|
|
|
|
|
if (strcmp (fsname, "smb2") == 0)
|
|
|
|
|
return TRUE;
|
2013-01-19 00:44:11 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
2015-01-12 20:59:35 +01:00
|
|
|
|
gboolean
|
2020-06-15 17:44:26 +02:00
|
|
|
|
g_local_file_is_nfs_home (const gchar *filename)
|
2013-01-19 00:44:11 +01:00
|
|
|
|
{
|
2020-06-15 17:43:49 +02:00
|
|
|
|
static gboolean remote_home = FALSE;
|
2013-01-19 00:44:11 +01:00
|
|
|
|
static gsize initialized;
|
|
|
|
|
const gchar *home;
|
|
|
|
|
|
|
|
|
|
home = g_get_home_dir ();
|
|
|
|
|
if (path_has_prefix (filename, home))
|
|
|
|
|
{
|
|
|
|
|
if (g_once_init_enter (&initialized))
|
|
|
|
|
{
|
2020-06-15 17:24:37 +02:00
|
|
|
|
GFile *file;
|
|
|
|
|
GFileInfo *info;
|
|
|
|
|
const gchar *fs_type = NULL;
|
|
|
|
|
|
|
|
|
|
file = _g_local_file_new (home);
|
|
|
|
|
info = g_local_file_query_filesystem_info (file, G_FILE_ATTRIBUTE_FILESYSTEM_TYPE, NULL, NULL);
|
|
|
|
|
if (info != NULL)
|
|
|
|
|
fs_type = g_file_info_get_attribute_string (info, G_FILE_ATTRIBUTE_FILESYSTEM_TYPE);
|
2020-06-15 17:43:49 +02:00
|
|
|
|
if (g_strcmp0 (fs_type, "nfs") == 0 || g_strcmp0 (fs_type, "nfs4") == 0)
|
|
|
|
|
remote_home = TRUE;
|
2020-06-15 17:24:37 +02:00
|
|
|
|
g_clear_object (&info);
|
|
|
|
|
g_object_unref (file);
|
|
|
|
|
|
2013-01-19 00:44:11 +01:00
|
|
|
|
g_once_init_leave (&initialized, TRUE);
|
|
|
|
|
}
|
|
|
|
|
return remote_home;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
2013-02-02 06:19:15 +01:00
|
|
|
|
#endif /* !G_OS_WIN32 */
|
2013-01-19 00:44:11 +01:00
|
|
|
|
|
2008-01-07 14:42:08 +01:00
|
|
|
|
static GFileMonitor*
|
2007-11-30 06:11:25 +01:00
|
|
|
|
g_local_file_monitor_dir (GFile *file,
|
|
|
|
|
GFileMonitorFlags flags,
|
2008-01-14 16:00:31 +01:00
|
|
|
|
GCancellable *cancellable,
|
|
|
|
|
GError **error)
|
2007-11-26 17:13:05 +01:00
|
|
|
|
{
|
2015-01-12 20:59:35 +01:00
|
|
|
|
GLocalFile *local_file = G_LOCAL_FILE (file);
|
|
|
|
|
|
|
|
|
|
return g_local_file_monitor_new_for_path (local_file->filename, TRUE, flags, error);
|
2007-11-26 17:13:05 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static GFileMonitor*
|
2007-11-30 06:11:25 +01:00
|
|
|
|
g_local_file_monitor_file (GFile *file,
|
|
|
|
|
GFileMonitorFlags flags,
|
2008-01-14 16:00:31 +01:00
|
|
|
|
GCancellable *cancellable,
|
|
|
|
|
GError **error)
|
2007-11-26 17:13:05 +01:00
|
|
|
|
{
|
2015-01-12 20:59:35 +01:00
|
|
|
|
GLocalFile *local_file = G_LOCAL_FILE (file);
|
2013-07-25 20:05:36 +02:00
|
|
|
|
|
2015-01-12 20:59:35 +01:00
|
|
|
|
return g_local_file_monitor_new_for_path (local_file->filename, FALSE, flags, error);
|
2013-07-25 20:05:36 +02:00
|
|
|
|
}
|
|
|
|
|
|
2013-07-28 19:41:17 +02:00
|
|
|
|
/* Here is the GLocalFile implementation of g_file_measure_disk_usage().
|
|
|
|
|
*
|
|
|
|
|
* If available, we use fopenat() in preference to filenames for
|
|
|
|
|
* efficiency and safety reasons. We know that fopenat() is available
|
|
|
|
|
* based on if AT_FDCWD is defined. POSIX guarantees that this will be
|
|
|
|
|
* defined as a macro.
|
|
|
|
|
*
|
|
|
|
|
* We use a linked list of stack-allocated GSList nodes in order to be
|
|
|
|
|
* able to reconstruct the filename for error messages. We actually
|
|
|
|
|
* pass the filename to operate on through the top node of the list.
|
|
|
|
|
*
|
|
|
|
|
* In case we're using openat(), this top filename will be a basename
|
|
|
|
|
* which should be opened in the directory which has also had its fd
|
|
|
|
|
* passed along. If we're not using openat() then it will be a full
|
|
|
|
|
* absolute filename.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
|
g_local_file_measure_size_error (GFileMeasureFlags flags,
|
|
|
|
|
gint saved_errno,
|
|
|
|
|
GSList *name,
|
|
|
|
|
GError **error)
|
|
|
|
|
{
|
|
|
|
|
/* Only report an error if we were at the toplevel or if the caller
|
|
|
|
|
* requested reporting of all errors.
|
|
|
|
|
*/
|
|
|
|
|
if ((name->next == NULL) || (flags & G_FILE_MEASURE_REPORT_ANY_ERROR))
|
|
|
|
|
{
|
|
|
|
|
GString *filename;
|
|
|
|
|
GSList *node;
|
|
|
|
|
|
|
|
|
|
/* Skip some work if there is no error return */
|
|
|
|
|
if (!error)
|
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
|
|
#ifdef AT_FDCWD
|
|
|
|
|
/* If using openat() we need to rebuild the filename for the message */
|
|
|
|
|
filename = g_string_new (name->data);
|
|
|
|
|
for (node = name->next; node; node = node->next)
|
|
|
|
|
{
|
2013-09-12 17:44:11 +02:00
|
|
|
|
gchar *utf8;
|
|
|
|
|
|
2013-07-28 19:41:17 +02:00
|
|
|
|
g_string_prepend_c (filename, G_DIR_SEPARATOR);
|
2013-09-12 17:44:11 +02:00
|
|
|
|
utf8 = g_filename_display_name (node->data);
|
|
|
|
|
g_string_prepend (filename, utf8);
|
|
|
|
|
g_free (utf8);
|
2013-07-28 19:41:17 +02:00
|
|
|
|
}
|
|
|
|
|
#else
|
2013-09-12 17:44:11 +02:00
|
|
|
|
{
|
|
|
|
|
gchar *utf8;
|
|
|
|
|
|
|
|
|
|
/* Otherwise, we already have it, so just use it. */
|
|
|
|
|
node = name;
|
|
|
|
|
filename = g_string_new (NULL);
|
|
|
|
|
utf8 = g_filename_display_name (node->data);
|
|
|
|
|
g_string_append (filename, utf8);
|
|
|
|
|
g_free (utf8);
|
|
|
|
|
}
|
2013-07-28 19:41:17 +02:00
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
g_set_error (error, G_IO_ERROR, g_io_error_from_errno (saved_errno),
|
|
|
|
|
_("Could not determine the disk usage of %s: %s"),
|
|
|
|
|
filename->str, g_strerror (saved_errno));
|
|
|
|
|
|
|
|
|
|
g_string_free (filename, TRUE);
|
|
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
else
|
|
|
|
|
/* We're not reporting this error... */
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
typedef struct
|
|
|
|
|
{
|
|
|
|
|
GFileMeasureFlags flags;
|
|
|
|
|
dev_t contained_on;
|
|
|
|
|
GCancellable *cancellable;
|
|
|
|
|
|
|
|
|
|
GFileMeasureProgressCallback progress_callback;
|
|
|
|
|
gpointer progress_data;
|
|
|
|
|
|
|
|
|
|
guint64 disk_usage;
|
|
|
|
|
guint64 num_dirs;
|
|
|
|
|
guint64 num_files;
|
|
|
|
|
|
|
|
|
|
guint64 last_progress_report;
|
|
|
|
|
} MeasureState;
|
|
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
|
g_local_file_measure_size_of_contents (gint fd,
|
|
|
|
|
GSList *dir_name,
|
|
|
|
|
MeasureState *state,
|
|
|
|
|
GError **error);
|
|
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
|
g_local_file_measure_size_of_file (gint parent_fd,
|
|
|
|
|
GSList *name,
|
|
|
|
|
MeasureState *state,
|
|
|
|
|
GError **error)
|
|
|
|
|
{
|
2013-11-06 14:02:26 +01:00
|
|
|
|
GLocalFileStat buf;
|
2013-07-28 19:41:17 +02:00
|
|
|
|
|
|
|
|
|
if (g_cancellable_set_error_if_cancelled (state->cancellable, error))
|
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
|
|
#if defined (AT_FDCWD)
|
2020-08-14 16:52:03 +02:00
|
|
|
|
if (g_local_file_fstatat (parent_fd, name->data, AT_SYMLINK_NOFOLLOW,
|
glocalfile: Never require G_LOCAL_FILE_STAT_FIELD_ATIME
Some filesystems don't have meaningful access times under at least some
circumstances (see #2189, #2205). In this situation the traditional stat()
and related kernel interfaces have to put something meaningless in the
st_atime field, and have no way to signal that it is meaningless.
However, statx() does have a way to signal that the atime is meaningless:
if the filesystem doesn't provide a useful access time, it will unset
the STATX_ATIME bit (as well as filling in the same meaningless value
for the stx_atime field that stat() would have used, for compatibility).
We don't actually *need* the atime, so never include it in the required
mask. This was already done for one code path in commit 6fc143bb
"gio: Allow no atime from statx" to fix #2189, but other callers were
left unchanged in that commit, and receive the same change here.
It is not actually guaranteed that *any* of the flags in the
returned stx_mask will be set (the only guarantee is that items in
STATX_BASIC_STATS have at least a harmless compatibility value, even if
their corresponding flag is cleared), so it might be better to follow
this up by removing the concept of the required mask entirely. However,
as of Linux 5.8 it looks as though STATX_ATIME is the only flag in
STATX_BASIC_STATS that might be cleared in practice, so this simpler
change fixes the immediate regression.
Resolves: https://gitlab.gnome.org/GNOME/glib/-/issues/2205
Signed-off-by: Simon McVittie <smcv@collabora.com>
2020-09-15 11:08:14 +02:00
|
|
|
|
G_LOCAL_FILE_STAT_FIELD_BASIC_STATS,
|
|
|
|
|
G_LOCAL_FILE_STAT_FIELD_ALL & (~G_LOCAL_FILE_STAT_FIELD_ATIME),
|
2020-08-14 16:52:03 +02:00
|
|
|
|
&buf) != 0)
|
2017-07-31 12:30:55 +02:00
|
|
|
|
{
|
|
|
|
|
int errsv = errno;
|
|
|
|
|
return g_local_file_measure_size_error (state->flags, errsv, name, error);
|
|
|
|
|
}
|
2014-08-04 14:39:08 +02:00
|
|
|
|
#elif defined (HAVE_LSTAT) || !defined (G_OS_WIN32)
|
2013-09-12 17:44:11 +02:00
|
|
|
|
if (g_lstat (name->data, &buf) != 0)
|
2017-07-31 12:30:55 +02:00
|
|
|
|
{
|
|
|
|
|
int errsv = errno;
|
|
|
|
|
return g_local_file_measure_size_error (state->flags, errsv, name, error);
|
|
|
|
|
}
|
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
|
|
|
|
#else /* !AT_FDCWD && !HAVE_LSTAT && G_OS_WIN32 */
|
|
|
|
|
if (GLIB_PRIVATE_CALL (g_win32_lstat_utf8) (name->data, &buf) != 0)
|
|
|
|
|
{
|
|
|
|
|
int errsv = errno;
|
|
|
|
|
return g_local_file_measure_size_error (state->flags, errsv, name, error);
|
|
|
|
|
}
|
2014-08-04 14:39:08 +02:00
|
|
|
|
#endif
|
2013-07-28 19:41:17 +02:00
|
|
|
|
|
|
|
|
|
if (name->next)
|
|
|
|
|
{
|
|
|
|
|
/* If not at the toplevel, check for a device boundary. */
|
|
|
|
|
|
|
|
|
|
if (state->flags & G_FILE_MEASURE_NO_XDEV)
|
2020-08-14 15:46:14 +02:00
|
|
|
|
if (state->contained_on != _g_stat_dev (&buf))
|
2013-07-28 19:41:17 +02:00
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/* If, however, this is the toplevel, set the device number so
|
|
|
|
|
* that recursive invocations can compare against it.
|
|
|
|
|
*/
|
2020-08-14 15:46:14 +02:00
|
|
|
|
state->contained_on = _g_stat_dev (&buf);
|
2013-07-28 19:41:17 +02:00
|
|
|
|
}
|
|
|
|
|
|
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 defined (G_OS_WIN32)
|
|
|
|
|
if (~state->flags & G_FILE_MEASURE_APPARENT_SIZE)
|
|
|
|
|
state->disk_usage += buf.allocated_size;
|
|
|
|
|
else
|
|
|
|
|
#elif defined (HAVE_STRUCT_STAT_ST_BLOCKS)
|
2013-07-28 19:41:17 +02:00
|
|
|
|
if (~state->flags & G_FILE_MEASURE_APPARENT_SIZE)
|
2020-08-14 15:46:14 +02:00
|
|
|
|
state->disk_usage += _g_stat_blocks (&buf) * G_GUINT64_CONSTANT (512);
|
2013-07-28 19:41:17 +02:00
|
|
|
|
else
|
|
|
|
|
#endif
|
2020-08-14 15:46:14 +02:00
|
|
|
|
state->disk_usage += _g_stat_size (&buf);
|
2013-07-28 19:41:17 +02:00
|
|
|
|
|
2020-08-14 15:46:14 +02:00
|
|
|
|
if (S_ISDIR (_g_stat_mode (&buf)))
|
2013-07-28 19:41:17 +02:00
|
|
|
|
state->num_dirs++;
|
|
|
|
|
else
|
|
|
|
|
state->num_files++;
|
|
|
|
|
|
|
|
|
|
if (state->progress_callback)
|
|
|
|
|
{
|
|
|
|
|
/* We could attempt to do some cleverness here in order to avoid
|
|
|
|
|
* calling clock_gettime() so much, but we're doing stats and opens
|
|
|
|
|
* all over the place already...
|
|
|
|
|
*/
|
|
|
|
|
if (state->last_progress_report)
|
|
|
|
|
{
|
|
|
|
|
guint64 now;
|
|
|
|
|
|
|
|
|
|
now = g_get_monotonic_time ();
|
|
|
|
|
|
|
|
|
|
if (state->last_progress_report + 200 * G_TIME_SPAN_MILLISECOND < now)
|
|
|
|
|
{
|
|
|
|
|
(* state->progress_callback) (TRUE,
|
|
|
|
|
state->disk_usage, state->num_dirs, state->num_files,
|
|
|
|
|
state->progress_data);
|
|
|
|
|
state->last_progress_report = now;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/* We must do an initial report to inform that more reports
|
|
|
|
|
* will be coming.
|
|
|
|
|
*/
|
|
|
|
|
(* state->progress_callback) (TRUE, 0, 0, 0, state->progress_data);
|
|
|
|
|
state->last_progress_report = g_get_monotonic_time ();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-14 15:46:14 +02:00
|
|
|
|
if (S_ISDIR (_g_stat_mode (&buf)))
|
2013-07-28 19:41:17 +02:00
|
|
|
|
{
|
|
|
|
|
int dir_fd = -1;
|
2018-04-25 16:07:59 +02:00
|
|
|
|
#ifdef AT_FDCWD
|
2017-07-31 12:30:55 +02:00
|
|
|
|
int errsv;
|
2018-04-25 16:07:59 +02:00
|
|
|
|
#endif
|
2013-07-28 19:41:17 +02:00
|
|
|
|
|
|
|
|
|
if (g_cancellable_set_error_if_cancelled (state->cancellable, error))
|
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
|
|
#ifdef AT_FDCWD
|
2013-09-26 20:02:06 +02:00
|
|
|
|
#ifdef HAVE_OPEN_O_DIRECTORY
|
|
|
|
|
dir_fd = openat (parent_fd, name->data, O_RDONLY|O_DIRECTORY);
|
|
|
|
|
#else
|
|
|
|
|
dir_fd = openat (parent_fd, name->data, O_RDONLY);
|
|
|
|
|
#endif
|
2017-07-31 12:30:55 +02:00
|
|
|
|
errsv = errno;
|
2013-07-28 19:41:17 +02:00
|
|
|
|
if (dir_fd < 0)
|
2017-07-31 12:30:55 +02:00
|
|
|
|
return g_local_file_measure_size_error (state->flags, errsv, name, error);
|
2013-07-28 19:41:17 +02:00
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
if (!g_local_file_measure_size_of_contents (dir_fd, name, state, error))
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
|
g_local_file_measure_size_of_contents (gint fd,
|
|
|
|
|
GSList *dir_name,
|
|
|
|
|
MeasureState *state,
|
|
|
|
|
GError **error)
|
|
|
|
|
{
|
|
|
|
|
gboolean success = TRUE;
|
2013-09-12 17:44:11 +02:00
|
|
|
|
const gchar *name;
|
|
|
|
|
GDir *dir;
|
2020-12-08 12:15:25 +01:00
|
|
|
|
gint saved_errno;
|
2013-07-28 19:41:17 +02:00
|
|
|
|
|
|
|
|
|
#ifdef AT_FDCWD
|
2013-09-12 17:44:11 +02:00
|
|
|
|
{
|
2020-12-08 12:15:25 +01:00
|
|
|
|
/* If this fails, we want to preserve the errno from fdopendir() */
|
2013-09-12 17:44:11 +02:00
|
|
|
|
DIR *dirp;
|
|
|
|
|
dirp = fdopendir (fd);
|
2020-12-08 12:15:25 +01:00
|
|
|
|
saved_errno = errno;
|
2013-09-12 17:44:11 +02:00
|
|
|
|
dir = dirp ? GLIB_PRIVATE_CALL(g_dir_new_from_dirp) (dirp) : NULL;
|
2020-12-08 12:15:25 +01:00
|
|
|
|
g_assert ((dirp == NULL) == (dir == NULL));
|
2013-09-12 17:44:11 +02:00
|
|
|
|
}
|
2013-07-28 19:41:17 +02:00
|
|
|
|
#else
|
2013-09-12 17:44:11 +02:00
|
|
|
|
dir = GLIB_PRIVATE_CALL(g_dir_open_with_errno) (dir_name->data, 0);
|
2020-12-08 12:15:25 +01:00
|
|
|
|
saved_errno = errno;
|
2013-07-28 19:41:17 +02:00
|
|
|
|
#endif
|
|
|
|
|
|
2013-09-12 17:44:11 +02:00
|
|
|
|
if (dir == NULL)
|
2013-07-28 19:41:17 +02:00
|
|
|
|
{
|
|
|
|
|
#ifdef AT_FDCWD
|
|
|
|
|
close (fd);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
return g_local_file_measure_size_error (state->flags, saved_errno, dir_name, error);
|
|
|
|
|
}
|
|
|
|
|
|
2013-09-12 17:44:11 +02:00
|
|
|
|
while (success && (name = g_dir_read_name (dir)))
|
2013-07-28 19:41:17 +02:00
|
|
|
|
{
|
|
|
|
|
GSList node;
|
|
|
|
|
|
|
|
|
|
node.next = dir_name;
|
|
|
|
|
#ifdef AT_FDCWD
|
2013-09-12 17:44:11 +02:00
|
|
|
|
node.data = (gchar *) name;
|
2013-07-28 19:41:17 +02:00
|
|
|
|
#else
|
|
|
|
|
node.data = g_build_filename (dir_name->data, name, NULL);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
success = g_local_file_measure_size_of_file (fd, &node, state, error);
|
|
|
|
|
|
|
|
|
|
#ifndef AT_FDCWD
|
|
|
|
|
g_free (node.data);
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
2013-09-12 17:44:11 +02:00
|
|
|
|
g_dir_close (dir);
|
2013-07-28 19:41:17 +02:00
|
|
|
|
|
|
|
|
|
return success;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
|
g_local_file_measure_disk_usage (GFile *file,
|
|
|
|
|
GFileMeasureFlags flags,
|
|
|
|
|
GCancellable *cancellable,
|
|
|
|
|
GFileMeasureProgressCallback progress_callback,
|
|
|
|
|
gpointer progress_data,
|
|
|
|
|
guint64 *disk_usage,
|
|
|
|
|
guint64 *num_dirs,
|
|
|
|
|
guint64 *num_files,
|
|
|
|
|
GError **error)
|
|
|
|
|
{
|
|
|
|
|
GLocalFile *local_file = G_LOCAL_FILE (file);
|
|
|
|
|
MeasureState state = { 0, };
|
|
|
|
|
gint root_fd = -1;
|
|
|
|
|
GSList node;
|
|
|
|
|
|
|
|
|
|
state.flags = flags;
|
|
|
|
|
state.cancellable = cancellable;
|
|
|
|
|
state.progress_callback = progress_callback;
|
|
|
|
|
state.progress_data = progress_data;
|
|
|
|
|
|
|
|
|
|
#ifdef AT_FDCWD
|
|
|
|
|
root_fd = AT_FDCWD;
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
node.data = local_file->filename;
|
|
|
|
|
node.next = NULL;
|
|
|
|
|
|
|
|
|
|
if (!g_local_file_measure_size_of_file (root_fd, &node, &state, error))
|
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
|
|
if (disk_usage)
|
|
|
|
|
*disk_usage = state.disk_usage;
|
|
|
|
|
|
|
|
|
|
if (num_dirs)
|
|
|
|
|
*num_dirs = state.num_dirs;
|
|
|
|
|
|
|
|
|
|
if (num_files)
|
|
|
|
|
*num_files = state.num_files;
|
|
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
2007-11-26 17:13:05 +01:00
|
|
|
|
static void
|
|
|
|
|
g_local_file_file_iface_init (GFileIface *iface)
|
|
|
|
|
{
|
|
|
|
|
iface->dup = g_local_file_dup;
|
|
|
|
|
iface->hash = g_local_file_hash;
|
|
|
|
|
iface->equal = g_local_file_equal;
|
|
|
|
|
iface->is_native = g_local_file_is_native;
|
|
|
|
|
iface->has_uri_scheme = g_local_file_has_uri_scheme;
|
|
|
|
|
iface->get_uri_scheme = g_local_file_get_uri_scheme;
|
|
|
|
|
iface->get_basename = g_local_file_get_basename;
|
|
|
|
|
iface->get_path = g_local_file_get_path;
|
|
|
|
|
iface->get_uri = g_local_file_get_uri;
|
|
|
|
|
iface->get_parse_name = g_local_file_get_parse_name;
|
|
|
|
|
iface->get_parent = g_local_file_get_parent;
|
2008-02-21 10:09:59 +01:00
|
|
|
|
iface->prefix_matches = g_local_file_prefix_matches;
|
2007-11-26 17:13:05 +01:00
|
|
|
|
iface->get_relative_path = g_local_file_get_relative_path;
|
|
|
|
|
iface->resolve_relative_path = g_local_file_resolve_relative_path;
|
|
|
|
|
iface->get_child_for_display_name = g_local_file_get_child_for_display_name;
|
|
|
|
|
iface->set_display_name = g_local_file_set_display_name;
|
|
|
|
|
iface->enumerate_children = g_local_file_enumerate_children;
|
|
|
|
|
iface->query_info = g_local_file_query_info;
|
|
|
|
|
iface->query_filesystem_info = g_local_file_query_filesystem_info;
|
Rework how volumes, drives and volume monitoring is done. Previosly the
2007-12-11 David Zeuthen <davidz@redhat.com>
Rework how volumes, drives and volume monitoring is
done. Previosly the model was
GDrive <1-1> GVolume
where a GDrive instance represented a mount point and a GVolume
instance represented a mounted file system. This patch changes it
the model to
GDrive <1-N> GVolume <1-1> GMount
where GMount now serves the purpose of the old GVolume and the new
GVolume serves the purpose of the old GDrive. In addition the new
GDrive interface is used to represent a collection of GVolume
instances (typically partitions) and also contains utility to query
the state of the physical drive the GDrive object represents (such
as checking for media, polling the drive, ejecting the media etc.).
Also implement mounting and unmounting in the Unix volume monitor
backend. A subquent patch will introduce GDrive support for ejection
of media.
* Makefile.am:
* gdrive.c: (g_drive_is_media_check_automatic),
(g_drive_is_media_removable), (g_drive_has_media),
(g_drive_can_poll_for_media), (g_drive_eject),
(g_drive_eject_finish), (g_drive_poll_for_media),
(g_drive_poll_for_media_finish):
* gdrive.h:
* gfile.c: (g_file_find_enclosing_mount):
* gfile.h:
* gio.symbols:
* glocaldirectorymonitor.c:
(g_local_directory_monitor_constructor), (mounts_changed):
* glocalfile.c: (get_mount_info),
(g_local_file_find_enclosing_mount),
(g_local_file_file_iface_init):
* gnativevolumemonitor.h:
* gunionvolumemonitor.c: (get_mounts), (get_volumes),
(get_connected_drives), (g_union_volume_monitor_class_init),
(child_volume_added), (child_volume_removed),
(child_volume_changed), (child_mount_added), (child_mount_removed),
(child_mount_pre_unmount), (child_mount_changed),
(child_drive_changed), (g_union_volume_monitor_add_monitor),
(g_union_volume_monitor_remove_monitor),
(_g_mount_get_for_mount_path):
* gunixmounts.c: (g_unix_is_mount_path_system_internal),
(guess_system_internal), (_g_get_unix_mounts),
(_g_get_unix_mount_points), (g_get_unix_mount_at),
(g_unix_mount_free), (g_unix_mount_compare),
(g_unix_mount_get_mount_path), (g_unix_mount_get_device_path),
(g_unix_mount_get_fs_type), (g_unix_mount_is_readonly),
(g_unix_mount_is_system_internal), (g_unix_mount_guess_type),
(type_to_icon), (g_unix_mount_guess_name),
(g_unix_mount_guess_icon), (g_unix_mount_point_guess_name),
(g_unix_mount_point_guess_icon), (_canonicalize_filename),
(_resolve_symlink), (_resolve_dev_root):
* gunixmounts.h:
* gunixvolume.c: (g_unix_volume_finalize), (_g_unix_volume_new),
(_g_unix_volume_disconnected), (_g_unix_volume_set_mount),
(_g_unix_volume_unset_mount), (g_unix_volume_get_icon),
(g_unix_volume_get_name), (g_unix_volume_can_mount),
(g_unix_volume_get_drive), (g_unix_volume_get_mount),
(_g_unix_volume_has_mount_path), (mount_cb), (mount_read_error),
(g_unix_volume_mount), (g_unix_volume_mount_finish),
(g_unix_volume_volume_iface_init):
* gunixvolume.h:
* gunixvolumemonitor.c: (g_unix_volume_monitor_finalize),
(get_mounts), (get_volumes), (get_connected_drives),
(get_mount_for_mount_path), (g_unix_volume_monitor_class_init),
(mountpoints_changed), (mounts_changed),
(g_unix_volume_monitor_init),
(_g_unix_volume_monitor_lookup_volume_for_mount_path),
(find_mount_by_mountpath), (update_volumes), (update_mounts):
* gunixvolumemonitor.h:
* gvolume.c: (g_volume_get_mount), (g_volume_can_mount),
(g_volume_mount), (g_volume_mount_finish):
* gvolume.h:
* gvolumemonitor.c: (g_volume_monitor_class_init),
(g_volume_monitor_get_connected_drives),
(g_volume_monitor_get_volumes), (g_volume_monitor_get_mounts):
* gvolumemonitor.h:
svn path=/trunk/; revision=6095
2007-12-11 22:23:55 +01:00
|
|
|
|
iface->find_enclosing_mount = g_local_file_find_enclosing_mount;
|
2007-11-26 17:13:05 +01:00
|
|
|
|
iface->query_settable_attributes = g_local_file_query_settable_attributes;
|
|
|
|
|
iface->query_writable_namespaces = g_local_file_query_writable_namespaces;
|
|
|
|
|
iface->set_attribute = g_local_file_set_attribute;
|
|
|
|
|
iface->set_attributes_from_info = g_local_file_set_attributes_from_info;
|
2007-12-05 11:38:03 +01:00
|
|
|
|
iface->read_fn = g_local_file_read;
|
2007-11-26 17:13:05 +01:00
|
|
|
|
iface->append_to = g_local_file_append_to;
|
|
|
|
|
iface->create = g_local_file_create;
|
|
|
|
|
iface->replace = g_local_file_replace;
|
2009-05-13 13:03:47 +02:00
|
|
|
|
iface->open_readwrite = g_local_file_open_readwrite;
|
|
|
|
|
iface->create_readwrite = g_local_file_create_readwrite;
|
|
|
|
|
iface->replace_readwrite = g_local_file_replace_readwrite;
|
2007-11-26 17:13:05 +01:00
|
|
|
|
iface->delete_file = g_local_file_delete;
|
|
|
|
|
iface->trash = g_local_file_trash;
|
|
|
|
|
iface->make_directory = g_local_file_make_directory;
|
2019-09-27 18:27:24 +02:00
|
|
|
|
#ifdef HAVE_SYMLINK
|
2007-11-26 17:13:05 +01:00
|
|
|
|
iface->make_symbolic_link = g_local_file_make_symbolic_link;
|
2019-09-27 18:27:24 +02:00
|
|
|
|
#endif
|
2007-11-26 17:13:05 +01:00
|
|
|
|
iface->move = g_local_file_move;
|
|
|
|
|
iface->monitor_dir = g_local_file_monitor_dir;
|
|
|
|
|
iface->monitor_file = g_local_file_monitor_file;
|
2013-07-28 19:41:17 +02:00
|
|
|
|
iface->measure_disk_usage = g_local_file_measure_disk_usage;
|
2009-06-17 02:22:58 +02:00
|
|
|
|
|
|
|
|
|
iface->supports_thread_contexts = TRUE;
|
2007-11-26 17:13:05 +01:00
|
|
|
|
}
|